"Fossies" - the Fresh Open Source Software Archive

Member "gambas-3.16.3/gb.db.mysql/src/main.c" (7 Sep 2021, 65350 Bytes) of package /linux/misc/gambas-3.16.3.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "main.c": 3.16.2_vs_3.16.3.

    1 /***************************************************************************
    2 
    3   main.c
    4 
    5   (c) 2000-2017 BenoƮt Minisini <g4mba5@gmail.com>
    6 
    7   This program is free software; you can redistribute it and/or modify
    8   it under the terms of the GNU General Public License as published by
    9   the Free Software Foundation; either version 2, or (at your option)
   10   any later version.
   11 
   12   This program is distributed in the hope that it will be useful,
   13   but WITHOUT ANY WARRANTY; without even the implied warranty of
   14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   15   GNU General Public License for more details.
   16 
   17   You should have received a copy of the GNU General Public License
   18   along with this program; if not, write to the Free Software
   19   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
   20   MA 02110-1301, USA.
   21 
   22 ***************************************************************************/
   23 
   24 #define __MAIN_C
   25 
   26 #include <stdio.h>
   27 #include <stdlib.h>
   28 #include <stdarg.h>
   29 #include <string.h>
   30 #include <time.h>
   31 
   32 #include <mysql.h>
   33 
   34 #include "gb.db.proto.h"
   35 #include "main.h"
   36 
   37 typedef
   38     struct {
   39         const char *pattern;
   40         int type;
   41         }
   42     CONV_STRING_TYPE;
   43 
   44 
   45 GB_INTERFACE GB EXPORT;
   46 DB_INTERFACE DB EXPORT;
   47 
   48 static char _buffer[125];
   49 static DB_DRIVER _driver;
   50 /*static int _print_query = FALSE;*/
   51 
   52 // Cached queries
   53 #define CACHE(_db) ((GB_HASHTABLE)(_db)->data)
   54 
   55 typedef
   56     struct {
   57         MYSQL_RES *res;
   58         time_t timestamp;
   59     }
   60     CACHE_ENTRY;
   61 
   62 // mySQL datatypes
   63 
   64 static CONV_STRING_TYPE _types[] =
   65     {
   66         { "tinyint", FIELD_TYPE_TINY },
   67         { "smallint", FIELD_TYPE_SHORT },
   68         { "mediumint", FIELD_TYPE_INT24 },
   69         { "int", FIELD_TYPE_LONG },
   70         { "bigint", FIELD_TYPE_LONGLONG },
   71         { "decimal", FIELD_TYPE_DECIMAL },
   72         { "numeric", FIELD_TYPE_DECIMAL },
   73         { "float", FIELD_TYPE_FLOAT },
   74         { "double", FIELD_TYPE_DOUBLE },
   75         { "real", FIELD_TYPE_DOUBLE },
   76         { "timestamp", FIELD_TYPE_TIMESTAMP },
   77         { "date", FIELD_TYPE_DATE },
   78         { "time", FIELD_TYPE_TIME },
   79         { "datetime", FIELD_TYPE_DATETIME },
   80         { "year", FIELD_TYPE_YEAR },
   81         { "char", FIELD_TYPE_STRING },
   82         { "varchar", FIELD_TYPE_VAR_STRING },
   83         { "blob", FIELD_TYPE_BLOB },
   84         { "tinyblob", FIELD_TYPE_TINY_BLOB },
   85         { "mediumblob", FIELD_TYPE_MEDIUM_BLOB },
   86         { "longblob", FIELD_TYPE_LONG_BLOB },
   87         { "text", FIELD_TYPE_BLOB },
   88         { "tinytext", FIELD_TYPE_TINY_BLOB },
   89         { "mediumtext", FIELD_TYPE_MEDIUM_BLOB },
   90         { "longtext", FIELD_TYPE_LONG_BLOB },
   91         { "set", FIELD_TYPE_SET },
   92         { "enum", FIELD_TYPE_ENUM },
   93         { "bit", FIELD_TYPE_BIT },
   94         { "null", FIELD_TYPE_NULL },
   95         { NULL, 0 },
   96     };
   97 
   98 /* internal function to quote a value stored as a string */
   99 
  100 static void quote_string(const char *data, long len, DB_FORMAT_CALLBACK add)
  101 {
  102     int i;
  103     unsigned char c;
  104     //char buffer[8];
  105 
  106     (*add)("'", 1);
  107     for (i = 0; i < len; i++)
  108     {
  109         c = (unsigned char)data[i];
  110         if (c == '\\')
  111             (*add)("\\\\", 2);
  112         else if (c == '\'')
  113             (*add)("''", 2);
  114         else if (c == 0)
  115             (*add)("\\0", 2);
  116         else
  117             (*add)((char *)&c, 1);
  118     }
  119     (*add)("'", 1);
  120 }
  121 
  122 /* internal function to quote a value stored as a blob */
  123 
  124 #define quote_blob quote_string
  125 
  126 /* Quote a string and returns the result as a temporary string */
  127 
  128 static char *get_quote_string(const char *str, int len, char quote)
  129 {
  130     char *res, *p, c;
  131     int len_res;
  132     int i;
  133     
  134     len_res = len;
  135     for (i = 0; i < len; i++)
  136     {
  137         c = str[i];
  138         if (c == quote || c == '\\' || c == 0)
  139             len_res++;
  140     }
  141     
  142     res = GB.TempString(NULL, len_res);
  143     
  144     p = res;
  145     for (i = 0; i < len; i++)
  146     {
  147         c = str[i];
  148         if (c == '\\' || c == quote)
  149         {
  150             *p++ = c;
  151             *p++ = c;
  152         }
  153         else if (c == 0)
  154         {
  155             *p++ = '\\';
  156             *p++ = '0';
  157         }
  158         else
  159             *p++ = c;
  160     }
  161     *p = 0;
  162     
  163     return res;
  164 }
  165 
  166 // Internal function to convert a database type into a Gambas type
  167 //
  168 // Look at https://dev.mysql.com/doc/refman/5.0/en/c-api-data-structures.html
  169 // for how to make the difference between a text field and a blob field.
  170 
  171 #define IS_BINARY_FIELD(_f) ((_f)->charsetnr == 63)
  172 #define SET_BINARY_FIELD(_f, _v) ((_f)->charsetnr = (_v) ? 63 : 0)
  173 
  174 static GB_TYPE conv_type(const MYSQL_FIELD *f)
  175 {
  176     switch(f->type)
  177     {
  178         case FIELD_TYPE_TINY:
  179             return (f->max_length == 1 && f->length == 1) ? GB_T_BOOLEAN : GB_T_INTEGER;
  180 
  181         case FIELD_TYPE_INT24:
  182         case FIELD_TYPE_SHORT:
  183         case FIELD_TYPE_LONG:
  184         case FIELD_TYPE_YEAR:
  185             return GB_T_INTEGER;
  186 
  187         case FIELD_TYPE_LONGLONG:
  188             return GB_T_LONG;
  189 
  190         case FIELD_TYPE_FLOAT:
  191         case FIELD_TYPE_DOUBLE:
  192         case FIELD_TYPE_DECIMAL:
  193             return GB_T_FLOAT;
  194 
  195         case FIELD_TYPE_DATE:
  196         case FIELD_TYPE_DATETIME:
  197         case FIELD_TYPE_TIME:
  198         case FIELD_TYPE_TIMESTAMP:
  199             return GB_T_DATE;
  200 
  201         case FIELD_TYPE_LONG_BLOB:
  202         case FIELD_TYPE_TINY_BLOB:
  203         case FIELD_TYPE_MEDIUM_BLOB:
  204         case FIELD_TYPE_BLOB:
  205             if (IS_BINARY_FIELD(f))
  206                 return DB_T_BLOB;
  207             else
  208                 return GB_T_STRING;
  209             
  210         case FIELD_TYPE_BIT:
  211             if (f->max_length == 1)
  212                 return GB_T_BOOLEAN;
  213             else if (f->max_length <= 32)
  214                 return GB_T_INTEGER;
  215             else if (f->max_length <= 64)
  216                 return GB_T_LONG;
  217 
  218         case FIELD_TYPE_STRING:
  219         case FIELD_TYPE_VAR_STRING:
  220         case FIELD_TYPE_SET:
  221         case FIELD_TYPE_ENUM:
  222         default:
  223             //fprintf(stderr, "FIELD_TYPE_*: %d\n", len);
  224             return GB_T_STRING;
  225 
  226     }
  227 }
  228 
  229 
  230 // Internal function to convert a string database type
  231 // into a fake MYSQL_FIELD structure
  232 
  233 static void conv_string_type(const char *type, MYSQL_FIELD *f)
  234 {
  235     CONV_STRING_TYPE *cst;
  236     long l;
  237 
  238     if (strncmp(type, "national ", 9) == 0)
  239         type += 9;
  240 
  241     for (cst = _types; cst->pattern; cst++)
  242     {
  243         if (strncmp(type, cst->pattern, strlen(cst->pattern)) == 0)
  244             break;
  245     }
  246 
  247     if (cst->type)
  248     {
  249         SET_BINARY_FIELD(f, FALSE);
  250         f->max_length = 0;
  251         
  252         if (cst->type == FIELD_TYPE_BLOB || cst->type == FIELD_TYPE_TINY_BLOB || cst->type == FIELD_TYPE_MEDIUM_BLOB || cst->type == FIELD_TYPE_LONG_BLOB)
  253         {
  254             SET_BINARY_FIELD(f, strcmp(&type[strlen(type) - 4], "blob") == 0);
  255         }
  256         else
  257         {
  258             type += strlen(cst->pattern);
  259             if (sscanf(type, "(%ld)", &l) == 1)
  260             {
  261                 f->max_length = l;
  262                 if (cst->type == FIELD_TYPE_TINY)
  263                     f->length = l;
  264             }
  265         }
  266     }
  267 
  268     f->type = cst->type;
  269 }
  270 
  271 #if 0
  272 static const char *get_type_name(int type)
  273 {
  274     CONV_STRING_TYPE *cst;
  275 
  276     for (cst = _types; cst->pattern; cst++)
  277     {
  278         if (cst->type == type)
  279             return cst->pattern;
  280     }
  281 
  282     return "?";
  283 }
  284 #endif
  285 
  286 /* Internal function to convert a database value into a Gambas variant value */
  287 
  288 static void conv_data(int version, const char *data, long data_length, GB_VARIANT_VALUE *val, MYSQL_FIELD *f)
  289 {
  290     GB_VALUE conv;
  291     GB_DATE_SERIAL date;
  292     double sec;
  293     int type = f->type;
  294 
  295     switch (type)
  296     {
  297         case FIELD_TYPE_TINY:
  298 
  299             if (f->max_length == 1 && f->length == 1)
  300             {
  301                 val->type = GB_T_BOOLEAN;
  302                 /*GB.NumberFromString(GB_NB_READ_INTEGER, data, strlen(data), &conv);*/
  303                 val->value._boolean = atoi(data) != 0 ? -1 : 0;
  304             }
  305             else
  306             {
  307                 GB.NumberFromString(GB_NB_READ_INTEGER, data, strlen(data), &conv);
  308 
  309                 val->type = GB_T_INTEGER;
  310                 val->value._integer = conv._integer.value;
  311             }
  312 
  313             break;
  314 
  315         case FIELD_TYPE_INT24:
  316         case FIELD_TYPE_SHORT:
  317         case FIELD_TYPE_LONG:
  318         /*case FIELD_TYPE_TINY:*/
  319         case FIELD_TYPE_YEAR:
  320 
  321             GB.NumberFromString(GB_NB_READ_INTEGER, data, strlen(data), &conv);
  322 
  323             val->type = GB_T_INTEGER;
  324             val->value._integer = conv._integer.value;
  325 
  326             break;
  327 
  328         case FIELD_TYPE_LONGLONG:
  329 
  330             GB.NumberFromString(GB_NB_READ_LONG, data, strlen(data), &conv);
  331 
  332             val->type = GB_T_LONG;
  333             val->value._long = conv._long.value;
  334 
  335             break;
  336 
  337         case FIELD_TYPE_FLOAT:
  338         case FIELD_TYPE_DOUBLE:
  339         case FIELD_TYPE_DECIMAL:
  340 
  341             GB.NumberFromString(GB_NB_READ_FLOAT, data, strlen(data), &conv);
  342 
  343             val->type = GB_T_FLOAT;
  344             val->value._float = conv._float.value;
  345 
  346             break;
  347 
  348         case FIELD_TYPE_DATE:
  349         case FIELD_TYPE_DATETIME:
  350         case FIELD_TYPE_TIME:
  351         case FIELD_TYPE_TIMESTAMP:
  352 
  353             // TIMESTAMP display format changed since MySQL 4.1!
  354             if (type == FIELD_TYPE_TIMESTAMP && version >= 40100)
  355                 type = FIELD_TYPE_DATETIME;
  356 
  357             memset(&date, 0, sizeof(date));
  358 
  359             switch(type)
  360             {
  361                 case FIELD_TYPE_DATE:
  362 
  363                     sscanf(data, "%4d-%2d-%2d", &date.year, &date.month, &date.day);
  364                     break;
  365 
  366                 case FIELD_TYPE_TIME:
  367 
  368                     sscanf(data, "%4d:%2d:%lf", &date.hour, &date.min, &sec);
  369                     date.sec = (short)sec;
  370                     date.msec = (short)((sec - date.sec) * 1000 + 0.5);
  371                     break;
  372 
  373                 case FIELD_TYPE_DATETIME:
  374 
  375                     sscanf(data, "%4d-%2d-%2d %2d:%2d:%lf", &date.year, &date.month, &date.day, &date.hour, &date.min, &sec);
  376                     date.sec = (short)sec;
  377                     date.msec = (short)((sec - date.sec) * 1000 + 0.5);
  378                     break;
  379 
  380                 case FIELD_TYPE_TIMESTAMP:
  381                     switch(strlen(data))
  382                     {
  383                         case 14:
  384                             sscanf(data, "%4d%2d%2d%2d%2d%lf", &date.year, &date.month, &date.day, &date.hour, &date.min, &sec);
  385                             date.sec = (short)sec;
  386                             date.msec = (short)((sec - date.sec) * 1000 + 0.5);
  387                             break;
  388                         case 12:
  389                             sscanf(data, "%2d%2d%2d%2d%2d%lf", &date.year, &date.month, &date.day, &date.hour, &date.min, &sec);
  390                             date.sec = (short)sec;
  391                             date.msec = (short)((sec - date.sec) * 1000 + 0.5);
  392                             break;
  393                         case 10:
  394                             sscanf(data, "%2d%2d%2d%2d%2d", &date.year, &date.month, &date.day, &date.hour, &date.min );
  395                             break;
  396                         case 8:
  397                             sscanf(data, "%4d%2d%2d", &date.year, &date.month, &date.day);
  398                             break;
  399                         case 6:
  400                             sscanf(data, "%2d%2d%2d", &date.year, &date.month, &date.day);
  401                             break;
  402                         case 4:
  403                             sscanf(data, "%2d%2d", &date.year, &date.month);
  404                             break;
  405                         case 2:
  406                             sscanf(data, "%2d", &date.year);
  407                             break;
  408                     }
  409                     if (date.year < 100)
  410                             date.year += 1900;
  411                 break;
  412             }
  413 
  414             GB.MakeDate(&date, (GB_DATE *)&conv);
  415 
  416             val->type = GB_T_DATE;
  417             val->value._date.date = conv._date.value.date;
  418             val->value._date.time = conv._date.value.time;
  419 
  420             break;
  421 
  422         case FIELD_TYPE_BLOB:
  423         case FIELD_TYPE_LONG_BLOB:
  424         case FIELD_TYPE_TINY_BLOB:
  425         case FIELD_TYPE_MEDIUM_BLOB:
  426             if (IS_BINARY_FIELD(f))
  427             {
  428                 // The BLOB are read by the blob_read() driver function
  429                 // You must set NULL there.
  430                 val->type = GB_T_NULL;
  431                 break;
  432             }
  433             // else continue!
  434 
  435         case FIELD_TYPE_STRING:
  436         case FIELD_TYPE_VAR_STRING:
  437         case FIELD_TYPE_SET:
  438         case FIELD_TYPE_ENUM:
  439         default:
  440             val->type = GB_T_CSTRING;
  441             val->value._string = (char *)data;
  442             //val->_string.start = 0;
  443             //if (data && data_length == 0)
  444             //  data_length = strlen(data);
  445             //val->_string.len = data_length;
  446             //fprintf(stderr, "conv_data: len = %d\n", len);
  447             /*GB.NewString(&val->_string.value, data, strlen(data));*/
  448 
  449             break;
  450     }
  451 
  452 }
  453 
  454 /* Internal function to substitute the table name into a query */
  455 
  456 static char *query_param[3];
  457 
  458 static void query_get_param(int index, char **str, int *len, char quote)
  459 {
  460     if (index > 3)
  461         return;
  462 
  463     index--;
  464     *str = query_param[index];
  465     *len = strlen(*str);
  466     
  467     if (quote == '\'' || quote == '`')
  468     {
  469         *str = get_quote_string(*str, *len, quote);
  470         *len = GB.StringLength(*str);
  471     }
  472 }
  473 
  474 static void check_connection(MYSQL *conn)
  475 {
  476     unsigned long thread_id;
  477 
  478     thread_id = mysql_thread_id(conn);
  479 
  480     mysql_ping(conn);
  481 
  482     if (mysql_thread_id(conn) != thread_id)
  483     {
  484         DB.Debug("gb.db.mysql", "connection lost\n");
  485         // Connection has been reestablished, set utf8 again
  486         mysql_query(conn, "set names 'utf8'");
  487     }
  488 }
  489 
  490 /* Internal function to run a query */
  491 
  492 static int do_query(DB_DATABASE *db, const char *error, MYSQL_RES **pres, const char *qtemp, int nsubst, ...)
  493 {
  494     MYSQL *conn = (MYSQL *)db->handle;
  495     va_list args;
  496     int i;
  497     const char *query;
  498     MYSQL_RES *res;
  499     int ret;
  500 
  501     if (nsubst)
  502     {
  503         va_start(args, nsubst);
  504         if (nsubst > 3)
  505             nsubst = 3;
  506         for (i = 0; i < nsubst; i++)
  507             query_param[i] = va_arg(args, char *);
  508 
  509         query = DB.SubstString(qtemp, 0, query_get_param);
  510     }
  511     else
  512         query = qtemp;
  513 
  514     DB.Debug("gb.db.mysql", "%p: %s", conn, query);
  515 
  516     check_connection(conn);
  517 
  518     if (mysql_query(conn, query))
  519     {
  520         ret = TRUE;
  521         if (error)
  522             GB.Error(error, mysql_error(conn));
  523     }
  524     else 
  525     {
  526         res = mysql_store_result(conn);
  527         ret = FALSE;
  528         if (pres)
  529             *pres = res;
  530         else
  531             mysql_free_result(res);
  532     }
  533 
  534     db->error = mysql_errno(conn);
  535     return ret;
  536 }
  537 
  538 static int do_query_cached(DB_DATABASE *db, const char *error, MYSQL_RES **pres, char *key, const char *qtemp, int nsubst, ...)
  539 {
  540     CACHE_ENTRY *entry;
  541     int len_key;
  542     bool added;
  543     time_t t;
  544     va_list args;
  545     int i;
  546     const char *query;
  547     int ret;
  548 
  549     if (nsubst)
  550     {
  551         va_start(args, nsubst);
  552         if (nsubst > 3)
  553             nsubst = 3;
  554         for (i = 0; i < nsubst; i++)
  555             query_param[i] = va_arg(args, char *);
  556 
  557         query = DB.SubstString(qtemp, 0, query_get_param);
  558         key = DB.SubstString(key, 0, query_get_param);
  559     }
  560     else
  561         query = qtemp;
  562 
  563     len_key = strlen(key);
  564     added = GB.HashTable.Get(CACHE(db), key, len_key, POINTER(&entry));
  565     if (added)
  566     {
  567         GB.AllocZero(POINTER(&entry), sizeof(CACHE_ENTRY));
  568         GB.HashTable.Add(CACHE(db), key, len_key, entry);
  569     }
  570 
  571     t = time(NULL);
  572 
  573     //fprintf(stderr, "-- do_query_cached: %s [ %p %ld ]\n", key, entry->res, entry->timestamp);
  574 
  575     if (entry->res &&  (t - entry->timestamp) < 30)
  576     {
  577         mysql_data_seek(entry->res, 0);
  578         *pres = entry->res;
  579         return 0;
  580     }
  581 
  582     entry->timestamp = t;
  583 
  584     if (entry->res)
  585         mysql_free_result(entry->res);
  586 
  587     ret = do_query(db, error, &entry->res, query, 0);
  588     if (ret == 0)
  589         *pres = entry->res;
  590     return ret;
  591 }
  592 
  593 
  594 /* Internal function to return database version number as a XXYYZZ integer number*/
  595 
  596 static int db_version(DB_DATABASE *db)
  597 {
  598     //Check db version
  599     const char *vquery = "select left(version(),6)";
  600     long dbversion =0;
  601     MYSQL_RES *res;
  602     MYSQL_ROW row;
  603 
  604     if (!do_query(db, NULL, &res, vquery, 0))
  605     {
  606         unsigned int verMain, verMajor, verMinor;
  607         row = mysql_fetch_row(res);
  608         sscanf(row[0],"%2u.%2u.%2u", &verMain, &verMajor, &verMinor);
  609         dbversion = ((verMain * 10000) + (verMajor * 100) + verMinor);
  610         mysql_free_result(res);
  611     }
  612     return dbversion;
  613 }
  614 
  615 /* Search in the first column a result for a specific name */
  616 
  617 static bool search_result(MYSQL_RES *res, const char *name, MYSQL_ROW *row)
  618 {
  619     int i;
  620     MYSQL_ROW r;
  621     
  622     for (i = 0; i < mysql_num_rows(res); i++ )
  623     {
  624         r = mysql_fetch_row(res);
  625 
  626         if (!strcmp(r[0], name))
  627         {
  628             if (row) 
  629                 *row = r;
  630             break;
  631         }
  632     }
  633         
  634     return (i >= mysql_num_rows(res));
  635 }
  636 
  637 // Clear the query cache
  638 
  639 static void free_cache(void *data)
  640 {
  641     GB.Free(&data);
  642 }
  643 
  644 static void clear_cache(DB_DATABASE *db)
  645 {
  646     GB.HashTable.Enum(CACHE(db), free_cache);
  647     GB.HashTable.Free((GB_HASHTABLE *)&db->data);
  648 }
  649 
  650 static void remove_cache_entry(DB_DATABASE *db, char *key)
  651 {
  652     CACHE_ENTRY *entry;
  653 
  654     if (GB.HashTable.Get(CACHE(db), key, -1, POINTER(&entry)))
  655         return;
  656 
  657     mysql_free_result(entry->res);
  658     GB.Free(POINTER(&entry));
  659     GB.HashTable.Remove(CACHE(db), key, -1);
  660 }
  661 
  662 static void clear_table_cache(DB_DATABASE *db, const char *table)
  663 {
  664     char key[strlen(table) + 5];
  665 
  666     strcpy(key, "sts:"); strcat(key, table); remove_cache_entry(db, key);
  667     //strcpy(key, "sc:"); strcat(key, table); remove_cache_entry(db, key);
  668     strcpy(key, "sfc:"); strcat(key, table); remove_cache_entry(db, key);
  669     strcpy(key, "si:"); strcat(key, table); remove_cache_entry(db, key);
  670 }
  671 
  672 /*****************************************************************************
  673 
  674     get_quote()
  675 
  676     Returns the character used for quoting object names.
  677 
  678 *****************************************************************************/
  679 
  680 static const char *get_quote(void)
  681 {
  682     return QUOTE_STRING;
  683 }
  684 
  685 /*****************************************************************************
  686 
  687     open_database()
  688 
  689     Connect to a database.
  690 
  691     <desc> points at a structure describing each connection parameter.
  692 
  693     This function must return a database handle, or NULL if the connection
  694     has failed.
  695 
  696 *****************************************************************************/
  697 
  698 static void set_character_set(DB_DATABASE *db)
  699 {
  700     MYSQL_RES *res;
  701     MYSQL_ROW row;
  702 
  703 //  sys_charset = GB.System.Charset();
  704 //  db_charset = NULL;
  705 
  706 //  for (i = 0; i < strlen(sys_charset); i++)
  707 //  {
  708 //      c = sys_charset[i];
  709 //      if (!c)
  710 //          break;
  711 //      c = tolower(c);
  712 //      if (!((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9')))
  713 //          continue;
  714 //      GB.AddString(&db_charset, (const char *)&c, 1);
  715 //  }
  716 //  
  717 //  db_charset[i] = 0;
  718 
  719     if (do_query(db, NULL, NULL, "set names 'utf8'", 0))
  720         fprintf(stderr, "WARNING: Unable to set database charset to UTF-8\n");
  721         
  722 //  GB.FreeString(&db_charset);
  723     
  724     if (do_query(db, "Unable to get database charset: &1", &res, "show variables like 'character_set_client'", 0))
  725         return;
  726     
  727     if (search_result(res, "character_set_client", &row))
  728         return;
  729     
  730     if (strncasecmp(row[1], "utf8", 4) == 0)
  731         db->charset = GB.NewString("utf8", 4);
  732     else
  733         db->charset = GB.NewZeroString(row[1]);
  734     //fprintf(stderr, "charset is '%s'\n", db->charset);
  735     mysql_free_result(res);
  736 }
  737 
  738 static int open_database(DB_DESC *desc, DB_DATABASE *db)
  739 {
  740     MYSQL *conn;
  741     char *name;
  742     char *host;
  743     char *socket;
  744     char reconnect = TRUE;
  745     unsigned int timeout;
  746     char *env;
  747     #if HAVE_MYSQL_SSL_MODE_DISABLED
  748     unsigned int mode;
  749     #endif
  750 
  751     conn = mysql_init(NULL);
  752 
  753     // NULL is a possible database name
  754     name = desc->name;
  755 
  756     //mysql_options(conn, MYSQL_READ_DEFAULT_GROUP,"Gambas");
  757 
  758     //fprintf(stderr, "mysql_real_connect: host = '%s'\n", desc->host);
  759 
  760     host = desc->host;
  761     if (host && *host == '/')
  762     {
  763         socket = host;
  764         host = NULL;
  765     }
  766     else
  767         socket = NULL;
  768     
  769     mysql_options(conn, MYSQL_OPT_RECONNECT, &reconnect);
  770     
  771     timeout = db->timeout;
  772     mysql_options(conn, MYSQL_OPT_CONNECT_TIMEOUT, &timeout);
  773     
  774     env = getenv("GB_DB_MYSQL_NOSSL");
  775     if (env && strcmp(env, "0"))
  776     {
  777     #if HAVE_MYSQL_SSL_MODE_DISABLED
  778         mode = SSL_MODE_DISABLED;
  779         mysql_options(conn, MYSQL_OPT_SSL_MODE, &mode);
  780     #else
  781         fprintf(stderr, "gb.db.mysql: warning: disabling SSL connection is not supported with your version of MySQL client library.\n");
  782     #endif
  783     }
  784     
  785     if (!mysql_real_connect(conn, host, desc->user, desc->password,
  786             name, desc->port == NULL ? 0 : atoi(desc->port), socket,
  787             CLIENT_MULTI_RESULTS | CLIENT_REMEMBER_OPTIONS /*client flag */)){
  788         mysql_close(conn);
  789         GB.Error("Cannot open database: &1", mysql_error(conn));
  790         return TRUE;
  791     }
  792 
  793     db->handle = conn;
  794     db->version = db_version(db);
  795 
  796     set_character_set(db);
  797     
  798     GB.HashTable.New(POINTER(&db->data), GB_COMP_BINARY);
  799     /* flags: none at the moment */
  800     return FALSE;
  801 }
  802 
  803 
  804 /*****************************************************************************
  805 
  806     close_database()
  807 
  808     Terminates the database connection.
  809 
  810     <handle> contains the database handle.
  811 
  812 *****************************************************************************/
  813 
  814 static void close_database(DB_DATABASE *db)
  815 {
  816     MYSQL *conn = (MYSQL *)db->handle;
  817 
  818     clear_cache(db);
  819 
  820     if (conn)
  821         mysql_close(conn);
  822 }
  823 
  824 
  825 /*****************************************************************************
  826 
  827     get_collations()
  828 
  829     Return the available collations as a Gambas string array.
  830 
  831 *****************************************************************************/
  832 
  833 static GB_ARRAY get_collations(DB_DATABASE *db)
  834 {
  835     const char *query = "show collation like '%'";
  836 
  837     MYSQL_RES *res;
  838     GB_ARRAY array;
  839     MYSQL_ROW row;
  840     int i, n;
  841 
  842     if (do_query(db, "Unable to get collations: &1", &res, query, 0))
  843         return NULL;
  844 
  845     n = mysql_num_rows(res);
  846 
  847     GB.Array.New(&array, GB_T_STRING, n);
  848     for (i = 0; i < n; i++)
  849     {
  850         row = mysql_fetch_row(res);
  851         *((char **)GB.Array.Get(array, i)) = GB.NewZeroString(row[0]);
  852     }
  853 
  854     return array;
  855 }
  856 
  857 /*****************************************************************************
  858 
  859     format_value()
  860 
  861     This function transforms a gambas value into a string value that can
  862     be inserted into a SQL query.
  863 
  864     <arg> points to the value.
  865     <add> is a callback called to insert the string into the query.
  866 
  867     This function must return TRUE if it translates the value, and FALSE if
  868     it does not.
  869 
  870     If the value is not translated, then a default translation is used.
  871 
  872 *****************************************************************************/
  873 
  874 static int format_value(GB_VALUE *arg, DB_FORMAT_CALLBACK add)
  875 {
  876     int l;
  877     GB_DATE_SERIAL *date;
  878 
  879     switch (arg->type)
  880     {
  881         case GB_T_BOOLEAN:
  882 /*Note this is likely to go to a tinyint  */
  883 
  884             if (VALUE((GB_BOOLEAN *)arg))
  885                 add("'1'", 3);
  886             else
  887                 add("'0'", 3);
  888             return TRUE;
  889 
  890         case GB_T_STRING:
  891         case GB_T_CSTRING:
  892 
  893             quote_string(VALUE((GB_STRING *)arg).addr + VALUE((GB_STRING *)arg).start, VALUE((GB_STRING *)arg).len, add);
  894             return TRUE;
  895 
  896         case GB_T_DATE:
  897 
  898             date = GB.SplitDate((GB_DATE *)arg);
  899 
  900             l = sprintf(_buffer, "'%04d-%02d-%02d %02d:%02d:%02d",
  901                     date->year, date->month, date->day,
  902                     date->hour, date->min, date->sec);
  903 
  904             add(_buffer, l);
  905 
  906             if (date->msec)
  907             {
  908                 l = sprintf(_buffer, ".%03d", date->msec);
  909                 add(_buffer, l);
  910             }
  911 
  912             add("'", 1);
  913 
  914             //fprintf(stderr, "format_value: %s / %d %d\n", _buffer, ((GB_DATE *)arg)->value.time, date->msec);
  915 
  916             return TRUE;
  917 
  918         default:
  919             return FALSE;
  920     }
  921 }
  922 
  923 
  924 /*****************************************************************************
  925 
  926     format_blob()
  927 
  928     This function transforms a blob value into a string value that can
  929     be inserted into a SQL query.
  930 
  931     <blob> points to the DB_BLOB structure.
  932     <add> is a callback called to insert the string into the query.
  933 
  934 *****************************************************************************/
  935 
  936 static void format_blob(DB_BLOB *blob, DB_FORMAT_CALLBACK add)
  937 {
  938     quote_blob(blob->data, blob->length, add);
  939 }
  940 
  941 
  942 /*****************************************************************************
  943 
  944     exec_query()
  945 
  946     Send a query to the server and gets the result.
  947 
  948     <db> is the database handle, as returned by open_database()
  949     <query> is the query string.
  950     <result> will receive the result handle of the query.
  951     <err> is an error message used when the query failed.
  952 
  953     <result> can be NULL, when we don't care getting the result.
  954 
  955 *****************************************************************************/
  956 
  957 static int exec_query(DB_DATABASE *db, const char *query, DB_RESULT *result, const char *err)
  958 {
  959     return do_query(db, err, (MYSQL_RES **)result, query, 0);
  960 }
  961 
  962 
  963 /*****************************************************************************
  964 
  965     get_last_insert_id()
  966 
  967     Return the value of the last serial field used in an INSERT statement
  968 
  969     <db> is the database handle, as returned by open_database()
  970 
  971 *****************************************************************************/
  972 
  973 static int64_t get_last_insert_id(DB_DATABASE *db)
  974 {
  975     MYSQL_RES *res;
  976     MYSQL_ROW row;
  977 
  978     if (do_query(db, "Unable to retrieve last insert id", &res, "select last_insert_id();", 0))
  979         return -1;
  980 
  981     row = mysql_fetch_row(res);
  982     return atoll(row[0]);
  983 }
  984 
  985 
  986 /*****************************************************************************
  987 
  988     query_init()
  989 
  990     Initialize an info structure from a query result.
  991 
  992     <result> is the handle of the query result.
  993     <info> points to the info structure.
  994     <count> will receive the number of records returned by the query.
  995 
  996     This function must initialize the info->nfield field with the number of
  997     field in the query result.
  998 
  999 *****************************************************************************/
 1000 
 1001 static void query_init(DB_RESULT result, DB_INFO *info, int *count)
 1002 {
 1003     MYSQL_RES *res = (MYSQL_RES *)result;
 1004 
 1005     if (res)
 1006     {
 1007         *count = mysql_num_rows(res);
 1008         info->nfield = mysql_num_fields(res);
 1009     }
 1010     else
 1011     {
 1012         *count = 0;
 1013         info->nfield = 0;
 1014     }
 1015 }
 1016 
 1017 
 1018 /*****************************************************************************
 1019 
 1020     query_release()
 1021 
 1022     Free the info structure filled by query_init() and the result handle.
 1023 
 1024     <result> is the handle of the query result.
 1025     <info> points to the info structure.
 1026     <invalid> tells if the associated connection has been closed.
 1027 
 1028 *****************************************************************************/
 1029 
 1030 static void query_release(DB_RESULT result, DB_INFO *info, bool invalid)
 1031 {
 1032     mysql_free_result((MYSQL_RES *)result);
 1033 }
 1034 
 1035 
 1036 /*****************************************************************************
 1037 
 1038     query_fill()
 1039 
 1040     Fill a result buffer with the value of each field of a record.
 1041 
 1042     <db> is the database handle, as returned by open_database()
 1043     <result> is the handle of the result.
 1044     <pos> is the index of the record in the result.
 1045     <buffer> points to an array having one element for each field in the
 1046     result.
 1047     <next> is a boolean telling if we want the next row.
 1048 
 1049     This function must return DB_OK, DB_ERROR or DB_NO_DATA
 1050     
 1051     This function must use GB.StoreVariant() to store the value in the
 1052     buffer.
 1053 
 1054 *****************************************************************************/
 1055 
 1056 static int query_fill(DB_DATABASE *db, DB_RESULT result, int pos, GB_VARIANT_VALUE *buffer, int next)
 1057 {
 1058     MYSQL_RES *res = (MYSQL_RES *)result;
 1059     MYSQL_FIELD *field;
 1060     MYSQL_ROW row;
 1061     int i;
 1062     char *data;
 1063     GB_VARIANT value;
 1064 
 1065     if (!next)
 1066         mysql_data_seek(res, pos);/* move to record */
 1067 
 1068     row = mysql_fetch_row(res);
 1069     mysql_field_seek(res, 0);
 1070     for (i = 0; i < mysql_num_fields(res); i++)
 1071     {
 1072         field = mysql_fetch_field(res);
 1073         data = row[i];
 1074 
 1075         value.type = GB_T_VARIANT;
 1076         value.value.type = GB_T_NULL;
 1077 
 1078         if (data)
 1079             conv_data(db->version, data, mysql_fetch_lengths(res)[i], &value.value, field);
 1080 
 1081         GB.StoreVariant(&value, &buffer[i]);
 1082     
 1083         //fprintf(stderr, "query_fill: %d: (%d, %d) : %s : %d\n", i, field->type, field->length, data, buffer[i].type);
 1084     }
 1085 
 1086     return DB_OK;
 1087 }
 1088 
 1089 
 1090 /*****************************************************************************
 1091 
 1092     blob_read()
 1093 
 1094     Returns the value of a BLOB field.
 1095 
 1096     <result> is the handle of the result.
 1097     <pos> is the index of the record in the result.
 1098     <blob> points at a DB_BLOB structure that will receive a pointer to the
 1099     data and its length.
 1100 
 1101 *****************************************************************************/
 1102 
 1103 static void blob_read(DB_RESULT result, int pos, int field, DB_BLOB *blob)
 1104 {
 1105     MYSQL_RES *res = (MYSQL_RES *)result;
 1106     MYSQL_ROW row;
 1107 
 1108     mysql_data_seek(res, pos);/* move to record */
 1109     row = mysql_fetch_row(res);
 1110 
 1111     blob->data = row[field];
 1112     blob->length = mysql_fetch_lengths(res)[field];
 1113     blob->constant = TRUE;
 1114 
 1115     //fprintf(stderr, "blob_read: %ld: %s\n", blob->length, blob->data);
 1116 }
 1117 
 1118 
 1119 /*****************************************************************************
 1120 
 1121     field_name()
 1122 
 1123     Return the name of a field in a result from its index.
 1124 
 1125     <result> is the result handle.
 1126     <field> is the field index.
 1127 
 1128 *****************************************************************************/
 1129 
 1130 static char *field_name(DB_RESULT result, int field)
 1131 {
 1132     MYSQL_FIELD *fld;
 1133     int i, num_fields = mysql_num_fields((MYSQL_RES *)result);
 1134     char *table1 = mysql_fetch_field_direct((MYSQL_RES *)result, 0)->table;
 1135     bool MultiTables = FALSE;
 1136 
 1137     // Need to identify whether multiple tables included
 1138                 fld = mysql_fetch_fields((MYSQL_RES *)result);
 1139                 for ( i = 1; i < num_fields; i++ ){
 1140         if (strcmp(table1, fld[i].table) != 0){
 1141             MultiTables = TRUE;
 1142             break;
 1143         }
 1144     }
 1145                 fld = mysql_fetch_field_direct((MYSQL_RES *)result, field);
 1146     // GB.Alloc((void **)&full, strlen(fld->table) + strlen(fld->name));
 1147     if (MultiTables && *fld->table){
 1148         sprintf(_buffer, "%s.%s", fld->table, fld->name);
 1149         return _buffer;
 1150     }
 1151     else {
 1152         return fld->name;
 1153     }
 1154     //return mysql_fetch_field_direct((MYSQL_RES *)result, field)->name;
 1155 }
 1156 
 1157 
 1158 /*****************************************************************************
 1159 
 1160     field_index()
 1161 
 1162     Return the index of a field in a result from its name.
 1163 
 1164     <Result> is the result handle.
 1165     <name> is the field name.
 1166 
 1167 *****************************************************************************/
 1168 
 1169 static int field_index(DB_RESULT Result, const char *name, DB_DATABASE *db)
 1170 {
 1171     unsigned int num_fields;
 1172     unsigned int i;
 1173     MYSQL_FIELD *field;
 1174     //char *table = NULL, *fld = NULL;
 1175     char *table;
 1176     const char *fld;
 1177     MYSQL_RES *result = (MYSQL_RES *)Result;
 1178 
 1179     fld = strchr(name, (int)FLD_SEP);
 1180     if (fld)
 1181     { /* Field does includes table info */
 1182         table = GB.NewString(name, fld - name);
 1183         fld = fld + 1;
 1184     }
 1185     else 
 1186     {
 1187         table = NULL;
 1188         fld = name;
 1189     }
 1190 
 1191     num_fields = mysql_num_fields(result);
 1192 
 1193     if (strcmp(name,fld)!=0)
 1194     { /* table name included */
 1195         mysql_field_seek(result,0); /* start at beginning */
 1196         for (i = 0; i < num_fields; i++)
 1197         {
 1198             field = mysql_fetch_field(result);
 1199             if (strcmp( fld, field->name) == 0 && strcmp( table, field->table) == 0)
 1200             {
 1201                 GB.FreeString(&table); 
 1202                 return i;
 1203             }
 1204         }
 1205         fld = name;
 1206     }
 1207 
 1208     if (table)
 1209         GB.FreeString(&table);
 1210 
 1211     /* Do not consider table name, also reached where table cannot be found. *
 1212     * Mysql can include . in the fieldname!!                                */
 1213     mysql_field_seek(result, 0); /* start at beginning */
 1214     for (i = 0; i < num_fields; i++)
 1215     {
 1216         field = mysql_fetch_field(result);
 1217         if (strcmp( fld, field->name) == 0)
 1218             return i;
 1219     }
 1220 
 1221     return -1;
 1222 }
 1223 
 1224 
 1225 /*****************************************************************************
 1226 
 1227     field_type()
 1228 
 1229     Return the Gambas type of a field in a result from its index.
 1230 
 1231     <result> is the result handle.
 1232     <field> is the field index.
 1233 
 1234 *****************************************************************************/
 1235 
 1236 static GB_TYPE field_type(DB_RESULT result, int field)
 1237 {
 1238     MYSQL_FIELD *f = mysql_fetch_field_direct((MYSQL_RES *)result, field);
 1239     GB_TYPE type = conv_type(f);
 1240 
 1241     return type;
 1242 }
 1243 
 1244 
 1245 /*****************************************************************************
 1246 
 1247     field_length()
 1248 
 1249     Return the length of a field in a result from its index.
 1250 
 1251     <result> is the result handle.
 1252     <field> is the field index.
 1253 
 1254 *****************************************************************************/
 1255 
 1256 static int field_length(DB_RESULT result, int field)
 1257 {
 1258     MYSQL_FIELD *f = mysql_fetch_field_direct((MYSQL_RES *)result, field);
 1259     GB_TYPE type = conv_type(f);
 1260 
 1261     if (type != GB_T_STRING)
 1262         return 0;
 1263     else
 1264         return f->max_length;
 1265 }
 1266 
 1267 
 1268 /*****************************************************************************
 1269 
 1270     begin_transaction()
 1271 
 1272     Begin a transaction.
 1273 
 1274     <handle> is the database handle.
 1275 
 1276     This function returns TRUE if the command has failed, and FALSE if
 1277     everything was OK.
 1278 
 1279     In mysql commit/rollback can only be used with transaction safe tables (BDB,
 1280     or InnoDB tables)
 1281 
 1282     ISAM, MyISAM and HEAP tables will commit straight away. The transaction
 1283     methods are therefore ignored.
 1284 
 1285 *****************************************************************************/
 1286 
 1287 static int begin_transaction(DB_DATABASE *db)
 1288 {
 1289     /* Autocommit is on by default. Lets set it off. */
 1290     /* BM: why not doing that when we open the connection ? */
 1291     //do_query(db, "Unable to set autocommit to 0: &1", NULL, "set autocommit=0", 0);
 1292     return do_query(db, "Unable to begin transaction: &1", NULL, "START TRANSACTION", 0);
 1293 }
 1294 
 1295 
 1296 /*****************************************************************************
 1297 
 1298     commit_transaction()
 1299 
 1300     Commit a transaction.
 1301 
 1302     <handle> is the database handle.
 1303 
 1304     This function returns TRUE if the command has failed, and FALSE if
 1305     everything was OK.
 1306 
 1307 *****************************************************************************/
 1308 
 1309 static int commit_transaction(DB_DATABASE *db)
 1310 {
 1311     bool ret = do_query(db, "Unable to commit transaction: &1", NULL, "COMMIT", 0);
 1312     /* Autocommit needs to be set back on. */
 1313     /* BM: and what happens if transactions are imbricated ? */
 1314     //do_query(db, "Unable to set autocommit to On: &1", NULL, "set autocommit=1", 0);
 1315     return ret;
 1316 }
 1317 
 1318 
 1319 /*****************************************************************************
 1320 
 1321     rollback_transaction()
 1322 
 1323     Rollback a transaction.
 1324 
 1325     <handle> is the database handle.
 1326 
 1327     This function returns TRUE if the command has failed, and FALSE if
 1328     everything was OK.
 1329 
 1330     In mysql commit/rollback can only be used with transaction safe tables (BDB,
 1331     or InnoDB tables)
 1332 
 1333     ISAM, MyISAM and HEAP tables will commit straight away. Therefore a rollback
 1334     cannot occur!
 1335 
 1336 *****************************************************************************/
 1337 
 1338 static int rollback_transaction(DB_DATABASE *db)
 1339 {
 1340     bool ret = do_query(db, "Unable to rollback transaction: &1", NULL, "ROLLBACK", 0);
 1341     /* Autocommit needs to be set back on. */
 1342     /* BM: and what happens if transactions are imbricated ? */
 1343     //do_query(db, "Unable to set autocommit to On: &1", NULL, "set autocommit=1", 0);
 1344     return ret;
 1345 }
 1346 
 1347 /*****************************************************************************
 1348 
 1349     table_init()
 1350 
 1351     Initialize an info structure from table fields.
 1352 
 1353     <handle> is the database handle.
 1354     <table> is the table name.
 1355     <info> points at the info structure.
 1356 
 1357     This function must initialize the following info fields:
 1358     - info->nfield must contain the number of fields in the table.
 1359     - info->field is an array of DB_FIELD, one element for each field.
 1360 
 1361     This function returns TRUE if the command has failed, and FALSE if
 1362     everything was OK.
 1363 
 1364 *****************************************************************************/
 1365 
 1366 static int field_info(DB_DATABASE *db, const char *table, const char *field, DB_FIELD *info);
 1367 
 1368 static int table_init(DB_DATABASE *db, const char *table, DB_INFO *info)
 1369 {
 1370     MYSQL_FIELD *field;
 1371 
 1372     MYSQL *conn = (MYSQL *)db->handle;
 1373     MYSQL_RES *res;
 1374     int i, n;
 1375     DB_FIELD *f;
 1376 
 1377     /* Nom de la table */
 1378 
 1379     info->table = GB.NewZeroString(table);
 1380 
 1381     check_connection(conn);
 1382 
 1383     res = mysql_list_fields( conn, table, 0);
 1384     if (!res)
 1385         return TRUE;
 1386 
 1387     info->nfield = n = mysql_num_fields(res);
 1388     if (n == 0)
 1389         return TRUE;
 1390 
 1391     GB.Alloc((void **)POINTER(&info->field), sizeof(DB_FIELD) * n);
 1392 
 1393     i = 0;
 1394 
 1395     while ((field = mysql_fetch_field(res)))
 1396     {
 1397         f = &info->field[i];
 1398 
 1399         if (field_info(db, table, field->name, f))
 1400         {
 1401             mysql_free_result(res);
 1402             return TRUE;
 1403         }
 1404 
 1405         f->name = GB.NewZeroString(field->name);
 1406 
 1407         /*f->type = conv_type(field->type, field->length);
 1408         f->length = 0;
 1409         if (f->type == GB_T_STRING)
 1410             f->length = field->length;*/
 1411 
 1412         i++;
 1413     }
 1414 
 1415     mysql_free_result(res);
 1416 
 1417     return FALSE;
 1418 }
 1419 
 1420 
 1421 /*****************************************************************************
 1422 
 1423     table_index()
 1424 
 1425     Initialize an info structure from table primary index.
 1426 
 1427     <handle> is the database handle.
 1428     <table> is the table name.
 1429     <info> points at the info structure.
 1430 
 1431     This function must initialize the following info fields:
 1432     - info->nindex must contain the number of fields in the primary index.
 1433     - info->index is a int[] giving the index of each index field in
 1434         info->fields.
 1435 
 1436     This function must be called after table_init().
 1437 
 1438     This function returns TRUE if the command has failed, and FALSE if
 1439     everything was OK.
 1440 
 1441 *****************************************************************************/
 1442 
 1443 static int table_index(DB_DATABASE *db, const char *table, DB_INFO *info)
 1444 {
 1445     char *qindex = "show index from `&1`";
 1446 
 1447     MYSQL_RES *res;
 1448     MYSQL_ROW row;
 1449     int i, j, n;
 1450 
 1451     /* Index primaire */
 1452 
 1453     if (do_query_cached(db, "Unable to get primary index: &1", &res, "si:&1", qindex, 1, table))
 1454         return TRUE;
 1455 
 1456     for ( i = 0, n = 0;  i < mysql_num_rows(res); i++ )
 1457     {
 1458         row = mysql_fetch_row(res);
 1459         if (strcmp("PRIMARY", row[2]) == 0) /* Use only Primary key */
 1460             n++;
 1461     }
 1462 
 1463     mysql_data_seek(res, 0);/* move back to first record */
 1464     info->nindex = n;
 1465     /* Note: Col 3 is Key_name, Col 4 is Sq_in_index, Col 5 is Field Name */
 1466 
 1467     if (n <= 0)
 1468     {
 1469         GB.Error("Table '&1' has no primary index", table);
 1470         return TRUE;
 1471     }
 1472 
 1473     GB.Alloc((void **)POINTER(&info->index), sizeof(int) * n);
 1474 
 1475     for (i = 0; i < n; i++)
 1476     {
 1477         row = mysql_fetch_row(res);
 1478         if (strcmp("PRIMARY", row[2]) == 0) /* Use only Primary key */
 1479         {
 1480                 for (j = 0; j < info->nfield; j++)
 1481                 {
 1482                         if (strcmp(info->field[j].name, row[4]) == 0)
 1483                         {
 1484                                 info->index[i] = j;
 1485                                 break;
 1486                         }
 1487             }
 1488         }
 1489     }
 1490 
 1491     return FALSE;
 1492 }
 1493 
 1494 
 1495 /*****************************************************************************
 1496 
 1497     table_release()
 1498 
 1499     Free the info structure filled by table_init() and/or table_index()
 1500 
 1501     <handle> is the database handle.
 1502     <info> points at the info structure.
 1503 
 1504 *****************************************************************************/
 1505 
 1506 static void table_release(DB_DATABASE *db, DB_INFO *info)
 1507 {
 1508     /* All is done outside the driver */
 1509 }
 1510 
 1511 
 1512 /*****************************************************************************
 1513 
 1514     table_exist()
 1515 
 1516     Returns if a table exists
 1517 
 1518     <handle> is the database handle.
 1519     <table> is the table name.
 1520 
 1521     This function returns TRUE if the table exists, and FALSE if not.
 1522 
 1523 *****************************************************************************/
 1524 
 1525 static int table_exist(DB_DATABASE *db, const char *table)
 1526 {
 1527     MYSQL_RES *res;
 1528 
 1529     if (do_query_cached(db, "Unable to check table: &1", &res, "st", "show tables", 0))
 1530         return FALSE;
 1531 
 1532     return !search_result(res, table, NULL);
 1533 }
 1534 
 1535 
 1536 /*****************************************************************************
 1537 
 1538     table_list()
 1539 
 1540     Returns an array containing the name of each table in the database
 1541 
 1542     <handle> is the database handle.
 1543     <tables> points to a variable that will receive the char* array.
 1544 
 1545     This function returns the number of tables, or -1 if the command has
 1546     failed.
 1547 
 1548     Be careful: <tables> can be NULL, so that just the count is returned.
 1549 
 1550 *****************************************************************************/
 1551 
 1552 static int table_list(DB_DATABASE *db, char ***tables)
 1553 {
 1554     MYSQL_RES *res;
 1555     MYSQL_ROW row;
 1556     int i;
 1557     int rows;
 1558 
 1559     if (do_query_cached(db, "Unable to get tables", &res, "st", "show tables", 0))
 1560         return -1;
 1561 
 1562     rows = mysql_num_rows(res);
 1563     GB.NewArray(tables, sizeof(char *), rows);
 1564 
 1565     for (i = 0; i < rows; i++)
 1566     {
 1567         row = mysql_fetch_row(res);
 1568         (*tables)[i] = GB.NewZeroString(row[0]);
 1569     }
 1570 
 1571     return rows;
 1572 }
 1573 
 1574 /*****************************************************************************
 1575 
 1576     table_primary_key()
 1577 
 1578     Returns a string representing the primary key of a table.
 1579 
 1580     <handle> is the database handle.
 1581     <table> is the table name.
 1582     <key> points to a string that will receive the primary key.
 1583 
 1584     This function returns TRUE if the command has failed, and FALSE if
 1585     everything was OK.
 1586 
 1587 *****************************************************************************/
 1588 
 1589 static int table_primary_key(DB_DATABASE *db, const char *table, char ***primary)
 1590 {
 1591     const char *query = "show index from `&1`";
 1592 
 1593     MYSQL_RES *res;
 1594     MYSQL_ROW row;
 1595     int i;
 1596 
 1597     if (do_query_cached(db, "Unable to get primary key: &1", &res, "si:&1", query, 1, table))
 1598         return TRUE;
 1599 
 1600     GB.NewArray(primary, sizeof(char *), 0);
 1601 
 1602     for (i = 0; i < mysql_num_rows(res); i++)
 1603     {
 1604         row = mysql_fetch_row(res);
 1605         if (strcmp("PRIMARY", row[2]) == 0)
 1606             *(char **)GB.Add(primary) = GB.NewZeroString(row[4]);
 1607     }
 1608 
 1609     return FALSE;
 1610 }
 1611 
 1612 /*****************************************************************************
 1613 
 1614     table_is_system()
 1615 
 1616     Returns if a table is a system table.
 1617 
 1618     <handle> is the database handle.
 1619     <table> is the table name.
 1620 
 1621     This function returns TRUE if the table is a system table, and FALSE if
 1622     not.
 1623 
 1624     Note: In mysql the system tables are stored in a separate database.
 1625     The tables are mysql.columns_priv, mysql.db, mysql.func, mysql.host,
 1626     mysql.tables_priv, mysql.user. This has therefore not been implemented.
 1627 
 1628 *****************************************************************************/
 1629 
 1630 static int database_is_system(DB_DATABASE *db, const char *name);
 1631 
 1632 static int table_is_system(DB_DATABASE *db, const char *table)
 1633 {
 1634     return db->flags.system;
 1635 }
 1636 
 1637 
 1638 /*****************************************************************************
 1639 
 1640     table_type()
 1641 
 1642     Returns the table type.
 1643 
 1644     <handle> is the database handle.
 1645     <table> is the table name.
 1646 
 1647     This function returns a string containing table type or NULL if error.
 1648 
 1649 *****************************************************************************/
 1650 
 1651 static char *table_type(DB_DATABASE *db, const char *table, const char *settype)
 1652 {
 1653     static char buffer[16];
 1654 
 1655     const char *query = "show table status like '&1'";
 1656 
 1657     const char *update =
 1658         "alter table `&1` type = &2";
 1659 
 1660     MYSQL_RES *res;
 1661     MYSQL_ROW row;
 1662 
 1663     if (settype)
 1664     {
 1665         clear_table_cache(db, table);
 1666         if (do_query(db, "Cannot set table type: &1", &res, update, 2, table, settype))
 1667                 return NULL;
 1668     }
 1669 
 1670     if (do_query_cached(db, "Invalid table: &1", &res, "sts:&1", query, 1, table))
 1671         return NULL;
 1672 
 1673     if (search_result(res, table, &row))
 1674     {
 1675         GB.Error("Unable to check table for: &1", table);
 1676         return NULL;
 1677     }
 1678     
 1679     if (!row[1]) return "VIEW"; // the table is a view
 1680 
 1681     strcpy(buffer, row[1]);
 1682     return buffer;
 1683 }
 1684 
 1685 
 1686 /*****************************************************************************
 1687 
 1688     table_delete()
 1689 
 1690     Deletes a table.
 1691 
 1692     <handle> is the database handle.
 1693     <table> is the table name.
 1694 
 1695     This function returns TRUE if the command has failed, and FALSE if
 1696     everything was OK.
 1697 
 1698 *****************************************************************************/
 1699 
 1700 static int table_delete(DB_DATABASE *db, const char *table)
 1701 {
 1702     clear_table_cache(db, table);
 1703     remove_cache_entry(db, "st");
 1704     return do_query(db, "Unable to delete table: &1", NULL, "drop table `&1`", 1, table);
 1705 }
 1706 
 1707 /*****************************************************************************
 1708 
 1709     table_create()
 1710 
 1711     Creates a table.
 1712 
 1713     <handle> is the database handle.
 1714     <table> is the table name.
 1715     <fields> points to a linked list of field descriptions.
 1716     <key> is the primary key.
 1717 
 1718     This function returns TRUE if the command has failed, and FALSE if
 1719     everything was OK.
 1720 
 1721     MySql has several different table types: InnoDB and BDB are transaction safe
 1722     whilst HEAP, ISAM, MERGE and MYISAM are not.
 1723 
 1724 TYPE =
 1725 
 1726 *****************************************************************************/
 1727 
 1728 static int table_create(DB_DATABASE *db, const char *table, DB_FIELD *fields, char **primary, const char *tabletype)
 1729 {
 1730     DB_FIELD *fp;
 1731     char *type = NULL;
 1732     int comma;
 1733     int i;
 1734 
 1735     if (db->version < 40100 && !strcasecmp(tabletype, "MEMORY"))
 1736         tabletype = "HEAP";
 1737     
 1738     DB.Query.Init();
 1739     
 1740     DB.Query.Add("CREATE TABLE `");
 1741     DB.Query.Add(table);
 1742     DB.Query.Add("` ( ");
 1743 
 1744     comma = FALSE;
 1745     for (fp = fields; fp; fp = fp->next)
 1746     {
 1747         if (comma)
 1748             DB.Query.Add(", ");
 1749         else
 1750             comma = TRUE;
 1751 
 1752         DB.Query.Add(QUOTE_STRING);
 1753         DB.Query.Add(fp->name);
 1754         DB.Query.Add(QUOTE_STRING);
 1755 
 1756         if (fp->type == DB_T_SERIAL)
 1757             DB.Query.Add(" BIGINT UNSIGNED NOT NULL AUTO_INCREMENT ");
 1758         else if (fp->type == DB_T_BLOB)
 1759             DB.Query.Add(" LONGBLOB ");
 1760         else
 1761         {
 1762             switch (fp->type)
 1763             {
 1764                 case GB_T_BOOLEAN: type = "BOOL"; break;
 1765                 case GB_T_INTEGER: type = "INT"; break;
 1766                 case GB_T_LONG: type = "BIGINT"; break;
 1767                 case GB_T_FLOAT: type = "DOUBLE"; break;
 1768                 case GB_T_DATE: type = "DATETIME"; break;
 1769                 case GB_T_STRING:
 1770 
 1771                     if (fp->length <= 0 || fp->length > 255) //mysql supports upto 255 as varchar
 1772                         type = "MEDIUMTEXT";
 1773                     else
 1774                     {
 1775                         sprintf(_buffer, "VARCHAR(%d)", fp->length);
 1776                         type = _buffer;
 1777                     }
 1778 
 1779                     break;
 1780 
 1781                 default: type = "MEDIUMTEXT"; break;
 1782             }
 1783 
 1784             DB.Query.Add(" ");
 1785             DB.Query.Add(type);
 1786 
 1787             if (fp->collation && *fp->collation)
 1788             {
 1789                 char *p = strchr(fp->collation, '_');
 1790                 if (!p || p == fp->collation)
 1791                 {
 1792                     GB.Error("Incorrect collation");
 1793                     return TRUE;
 1794                 }
 1795 
 1796                 DB.Query.Add(" CHARACTER SET ");
 1797                 DB.Query.AddLength(fp->collation, p - fp->collation);
 1798                 DB.Query.Add(" COLLATE ");
 1799                 DB.Query.Add(fp->collation);
 1800             }
 1801 
 1802             if (fp->def.type != GB_T_NULL)
 1803             {
 1804                 DB.Query.Add(" NOT NULL DEFAULT ");
 1805                 DB.FormatVariant(&_driver, &fp->def, DB.Query.AddLength);
 1806             }
 1807             else if (DB.StringArray.Find(primary, fp->name) >= 0)
 1808             {
 1809                 DB.Query.Add(" NOT NULL");
 1810             }
 1811         }
 1812     }
 1813 
 1814     if (primary)
 1815     {
 1816         DB.Query.Add(", PRIMARY KEY (");
 1817 
 1818         for (i = 0; i < GB.Count(primary); i++)
 1819         {
 1820             if (i > 0)
 1821                 DB.Query.Add(",");
 1822 
 1823             DB.Query.Add("`");
 1824             DB.Query.Add(primary[i]);
 1825             DB.Query.Add("`");
 1826 
 1827             for (fp = fields; fp; fp = fp->next) //Check type of primary field
 1828             {
 1829                             if(strcmp(fp->name, primary[i]) == 0){
 1830                                 if (fp->length <= 0 || fp->length > 255){
 1831                                                 if (fp->type == GB_T_STRING)
 1832                                                     DB.Query.Add("(255)");
 1833                                 }
 1834                             }
 1835             }
 1836         }
 1837 
 1838         DB.Query.Add(")");
 1839     }
 1840 
 1841     DB.Query.Add(" )");
 1842 
 1843     if (tabletype)
 1844     {
 1845         if (db->version < 40018)
 1846             DB.Query.Add(" TYPE = ");
 1847         else
 1848             DB.Query.Add(" ENGINE = ");
 1849         
 1850         DB.Query.Add(tabletype);
 1851     }
 1852 
 1853     remove_cache_entry(db, "st");
 1854     /* printf("table_create syntax: %s\n", DB.Query.Get());*/
 1855     return do_query(db, "Cannot create table: &1", NULL, DB.Query.Get(), 0);
 1856 }
 1857 
 1858 /*****************************************************************************
 1859 
 1860     field_exist()
 1861 
 1862     Returns if a field exists in a given table
 1863 
 1864     <handle> is the database handle.
 1865     <table> is the table name.
 1866     <field> is the field name.
 1867 
 1868     This function returns TRUE if the field exists, and FALSE if not.
 1869 
 1870 *****************************************************************************/
 1871 
 1872 static int field_exist(DB_DATABASE *db, const char *table, const char *field)
 1873 {
 1874     const char *query = "show full columns from `&1`";
 1875 
 1876     MYSQL_RES *res;
 1877 
 1878     if (do_query_cached(db, "Unable to check field: &1", &res, "sfc:&1", query, 1, table))
 1879         return FALSE;
 1880 
 1881     return !search_result(res, field, NULL);
 1882 }
 1883 
 1884 
 1885 
 1886 /*****************************************************************************
 1887 
 1888     field_list()
 1889 
 1890     Returns an array containing the name of each field in a given table
 1891 
 1892     <handle> is the database handle.
 1893     <table> is the table name.
 1894     <fields> points to a variable that will receive the char* array.
 1895 
 1896     This function returns the number of fields, or -1 if the command has
 1897     failed.
 1898 
 1899     Be careful: <fields> can be NULL, so that just the count is returned.
 1900 
 1901 *****************************************************************************/
 1902 
 1903 static int field_list(DB_DATABASE *db, const char *table, char ***fields)
 1904 {
 1905     const char *query = "show full columns from `&1`";
 1906 
 1907     long i, n;
 1908     MYSQL_RES *res;
 1909     MYSQL_ROW row;
 1910 
 1911     if (do_query_cached(db, "Unable to get field: &1", &res, "sfc:&1", query, 1, table))
 1912         return -1;
 1913 
 1914     n = mysql_num_rows(res);
 1915 
 1916     if (fields) /* (BM) see the function commentary */
 1917     {
 1918         GB.NewArray(fields, sizeof(char *), n);
 1919 
 1920         for (i = 0; i < n; i++){
 1921             row = mysql_fetch_row(res);
 1922             (*fields)[i] = GB.NewZeroString(row[0]);
 1923         }
 1924     }
 1925 
 1926     return n;
 1927 }
 1928 
 1929 
 1930 /*****************************************************************************
 1931 
 1932     field_info()
 1933 
 1934     Get field description
 1935 
 1936     <handle> is the database handle.
 1937     <table> is the table name.
 1938     <field> is the field name.
 1939     <info> points to a structure filled by the function.
 1940 
 1941     This function returns TRUE if the command has failed, and FALSE if
 1942     everything was OK.
 1943 
 1944 *****************************************************************************/
 1945 
 1946 static int field_info(DB_DATABASE *db, const char *table, const char *field, DB_FIELD *info)
 1947 {
 1948     const char *query = "show full columns from `&1`";
 1949 
 1950     MYSQL_RES *res;
 1951     MYSQL_ROW row;
 1952     MYSQL_FIELD f;
 1953     GB_VARIANT def;
 1954     char *val;
 1955 
 1956     if (do_query_cached(db, "Unable to get field info: &1", &res, "sfc:&1", query, 1, table))
 1957         return TRUE;
 1958 
 1959     if (search_result(res, field, &row))
 1960     {
 1961         GB.Error("Unable to find field &2 in table &1", table, field);
 1962         return TRUE;
 1963     }
 1964 
 1965     info->name = NULL;
 1966     conv_string_type(row[1], &f);
 1967     info->type = conv_type(&f);
 1968 
 1969     if (info->type == GB_T_STRING)
 1970         info->length = f.max_length;
 1971     else
 1972         info->length = 0;
 1973 
 1974     info->def.type = GB_T_NULL;
 1975 
 1976     if ((info->type == GB_T_INTEGER || info->type == GB_T_LONG) && strstr(row[6], "auto_increment"))
 1977         info->type = DB_T_SERIAL;
 1978     else
 1979     {
 1980         if (!*row[3] || row[3][0] != 'Y')
 1981         {
 1982             def.type = GB_T_VARIANT;
 1983             def.value.type = GB_T_NULL;
 1984 
 1985             val = row[5];
 1986 
 1987             /* (BM) seems there is a bug in mysql */
 1988             if (info->type == GB_T_DATE && val && strlen(val) >= 5 && strncmp(val, "00000", 5) == 0)
 1989                 val = NULL;
 1990 
 1991             if (val && *val)
 1992             {
 1993                 conv_data(db->version, val, 0, &def.value, &f);
 1994                 GB.StoreVariant(&def, &info->def);
 1995             }
 1996         }
 1997     }
 1998 
 1999     if (row[2] && *row[2])
 2000         info->collation = GB.NewZeroString(row[2]);
 2001     else
 2002         info->collation = NULL;
 2003 
 2004     return FALSE;
 2005 }
 2006 
 2007 /*****************************************************************************
 2008 
 2009     index_exist()
 2010 
 2011     Returns if an index exists in a given table
 2012 
 2013     <handle> is the database handle.
 2014     <table> is the table name.
 2015     <field> is the index name.
 2016 
 2017     This function returns TRUE if the index exists, and FALSE if not.
 2018 
 2019 *****************************************************************************/
 2020 
 2021 static int index_exist(DB_DATABASE *db, const char *table, const char *index)
 2022 {
 2023     const char *query = "show index from `&1`";
 2024 
 2025     MYSQL_RES *res;
 2026     MYSQL_ROW row;
 2027     int i, n;
 2028 
 2029     if (do_query_cached(db, "Unable to check index: &1", &res, "si:&1", query, 1, table))
 2030         return FALSE;
 2031 
 2032     for ( i = 0, n = 0; i < mysql_num_rows(res); i++ )
 2033     {
 2034         row = mysql_fetch_row(res);
 2035         if (strcmp(index, row[2]) == 0)
 2036                 n++;
 2037     }
 2038 
 2039     return (n > 0);
 2040 }
 2041 
 2042 /*****************************************************************************
 2043 
 2044     index_list()
 2045 
 2046     Returns an array containing the name of each index in a given table
 2047 
 2048     <handle> is the database handle.
 2049     <table> is the table name.
 2050     <indexes> points to a variable that will receive the char* array.
 2051 
 2052     This function returns the number of indexes, or -1 if the command has
 2053     failed.
 2054 
 2055     Be careful: <indexes> can be NULL, so that just the count is returned.
 2056 
 2057 *****************************************************************************/
 2058 
 2059 static int index_list(DB_DATABASE *db, const char *table, char ***indexes)
 2060 {
 2061     const char *query = "show index from `&1`";
 2062 
 2063     MYSQL_RES *res;
 2064     MYSQL_ROW row;
 2065     long i, n, no_indexes;
 2066 
 2067     if (do_query_cached(db, "Unable to get indexes: &1", &res, "si:&1", query, 1, table))
 2068         return -1;
 2069 
 2070     for (i = 0, no_indexes = 0; i < mysql_num_rows(res); i++ )
 2071     {
 2072         /* Count the number of 1st sequences in Seq_in_index to
 2073             give nmber of indexes. row[3] */
 2074         row = mysql_fetch_row(res);
 2075         if (atoi(row[3]) == 1)
 2076             no_indexes++;
 2077     }
 2078 
 2079 
 2080     GB.NewArray(indexes, sizeof(char *), no_indexes);
 2081     mysql_data_seek(res, 0); /* move back to first record */
 2082 
 2083     for (i = 0, n = 0; i < mysql_num_rows(res); i++ )
 2084     {
 2085         row = mysql_fetch_row(res);
 2086         if (atoi(row[3]) == 1 /* Start of a new index */)
 2087             (*indexes)[n++] = GB.NewZeroString(row[2]); /* (BM) The name is row[2], not row[4] */
 2088     }
 2089 
 2090     return no_indexes;
 2091 }
 2092 
 2093 /*****************************************************************************
 2094 
 2095     index_info()
 2096 
 2097     Get index description
 2098 
 2099     <handle> is the database handle.
 2100     <table> is the table name.
 2101     <field> is the index name.
 2102     <info> points to a structure filled by the function.
 2103 
 2104     This function returns TRUE if the command has failed, and FALSE if
 2105     everything was OK.
 2106 
 2107 *****************************************************************************/
 2108 
 2109 static int index_info(DB_DATABASE *db, const char *table, const char *index, DB_INDEX *info)
 2110 {
 2111     const char *query = "show index from `&1`";
 2112 
 2113     MYSQL_RES *res;
 2114     MYSQL_ROW row = 0;
 2115     int i, n;
 2116 
 2117     if (do_query_cached(db, "Unable to get index info: &1", &res, "si:&1", query, 1, table))
 2118         return TRUE;
 2119 
 2120     n = mysql_num_rows(res);
 2121     for (i = 0; i < n; i++ )
 2122     {
 2123         row = mysql_fetch_row(res);
 2124         if ( strcmp( index, row[2]) == 0)
 2125         { /* (BM) With braces, it should work better :-) */
 2126                 n = 1;
 2127                 break;
 2128         }
 2129     }
 2130 
 2131     if (n != 1)
 2132     {
 2133         GB.Error("Unable to find index &2 in table &1", table, index);
 2134         return TRUE;
 2135     }
 2136 
 2137     info->name = NULL;
 2138     info->unique = strcmp(row[1], "0") == 0;
 2139     info->primary = strcmp("PRIMARY", row[2]) == 0 ? TRUE : FALSE;
 2140 
 2141     DB.Query.Init();
 2142 
 2143     i = 0;
 2144     /* (BM) row can be null if we are seeking the last index */
 2145     while ( row && strcmp(index, row[2]) == 0 )
 2146     {
 2147         if (i > 0)
 2148             DB.Query.Add(",");
 2149 
 2150         DB.Query.Add(row[4]);
 2151         row = mysql_fetch_row(res);
 2152         i++; /* (BM) i must be incremented */
 2153     }
 2154 
 2155     info->fields = DB.Query.GetNew();
 2156 
 2157     return FALSE;
 2158 }
 2159 
 2160 /*****************************************************************************
 2161 
 2162     index_delete()
 2163 
 2164     Deletes an index.
 2165 
 2166     <handle> is the database handle.
 2167     <table> is the table name.
 2168     <index> is the index name.
 2169 
 2170     This function returns TRUE if the command has failed, and FALSE if
 2171     everything was OK.
 2172 
 2173 *****************************************************************************/
 2174 
 2175 static int index_delete(DB_DATABASE *db, const char *table, const char *index)
 2176 {
 2177     clear_table_cache(db, table);
 2178     return do_query(db, "Unable to delete index: &1", NULL, "drop index `&1` on `&2`", 2, index, table);
 2179 }
 2180 
 2181 /*****************************************************************************
 2182 
 2183     index_create()
 2184 
 2185     Creates an index.
 2186 
 2187     <handle> is the database handle.
 2188     <table> is the table name.
 2189     <index> is the index name.
 2190     <info> points to a structure describing the index.
 2191 
 2192     This function returns TRUE if the command has failed, and FALSE if
 2193     everything was OK.
 2194 
 2195 *****************************************************************************/
 2196 
 2197 static int index_create(DB_DATABASE *db, const char *table, const char *index, DB_INDEX *info)
 2198 {
 2199     DB.Query.Init();
 2200 
 2201     DB.Query.Add("CREATE ");
 2202     if (info->unique)
 2203         DB.Query.Add("UNIQUE ");
 2204     DB.Query.Add("INDEX `");
 2205     DB.Query.Add(index);
 2206     DB.Query.Add("` ON ");
 2207     DB.Query.Add(table);
 2208     DB.Query.Add(" ( ");
 2209     DB.Query.Add(info->fields);
 2210     DB.Query.Add(" )");
 2211 
 2212     clear_table_cache(db, table);
 2213     return do_query(db, "Cannot create index: &1", NULL, DB.Query.Get(), 0);
 2214 }
 2215 
 2216 /*****************************************************************************
 2217 *
 2218 *   database_exist()
 2219 *
 2220 *   Returns if a database exists
 2221 *
 2222 *   <handle> is any database handle.
 2223 *   <name> is the database name.
 2224 *
 2225 *   This function returns TRUE if the database exists, and FALSE if not.
 2226 *
 2227 ******************************************************************************/
 2228 
 2229 static int database_exist(DB_DATABASE *db, const char *name)
 2230 {
 2231     MYSQL *conn = (MYSQL *)db->handle;
 2232     MYSQL_RES *res;
 2233     int exist;
 2234 
 2235     check_connection(conn);
 2236     res = mysql_list_dbs(conn, name);
 2237     if (!res)
 2238     {
 2239         db->error = mysql_errno(conn);  
 2240         GB.Error("Unable to check database: &1", mysql_error(conn));
 2241         return FALSE;
 2242     }
 2243 
 2244     exist = mysql_num_rows(res);
 2245     mysql_free_result(res);
 2246     return exist;
 2247 }
 2248 
 2249 /*****************************************************************************
 2250 *
 2251 *   database_list()
 2252 *
 2253 *   Returns an array containing the name of each database
 2254 *
 2255 *   <handle> is any database handle.
 2256 *   <databases> points to a variable that will receive the char* array.
 2257 *
 2258 *   This function returns the number of databases, or -1 if the command has
 2259 *   failed.
 2260 *
 2261 *   Be careful: <databases> can be NULL, so that just the count is returned.
 2262 *
 2263 ******************************************************************************/
 2264 
 2265 static int database_list(DB_DATABASE *db, char ***databases)
 2266 {
 2267     long i, rows;
 2268     MYSQL *conn = (MYSQL *)db->handle;
 2269     MYSQL_RES *res;
 2270     MYSQL_ROW row;
 2271 
 2272     check_connection(conn);
 2273     res = mysql_list_dbs(conn, 0);
 2274     if (!res){
 2275         db->error = mysql_errno(conn);
 2276         GB.Error("Unable to get databases: &1", mysql_error(conn));
 2277         return -1;
 2278     }
 2279 
 2280     rows = mysql_num_rows(res);
 2281     /*printf("Got %d databases\n", rows); */
 2282     GB.NewArray(databases, sizeof(char *), rows);
 2283 
 2284     for (i = 0; i < rows; i++){
 2285         row = mysql_fetch_row(res);
 2286         (*databases)[i] = GB.NewZeroString(row[0]);
 2287         /*printf("%s\n", (*databases)[i]); */
 2288     }
 2289 
 2290     mysql_free_result(res);
 2291 
 2292     return rows;
 2293 }
 2294 
 2295 /*****************************************************************************
 2296 *
 2297 *   database_is_system()
 2298 *
 2299 *   Returns if a database is a system database.
 2300 *
 2301 *   <handle> is any database handle.
 2302 *   <name> is the database name.
 2303 *
 2304 *   This function returns TRUE if the database is a system database, and
 2305 *   FALSE if not.
 2306 *
 2307 ******************************************************************************/
 2308 
 2309 static int database_is_system(DB_DATABASE *db, const char *name)
 2310 {
 2311     return (strcmp("mysql", name) == 0 || strcmp("information_schema", name) == 0);
 2312 }
 2313 
 2314 /*****************************************************************************
 2315 *
 2316 *   database_delete()
 2317 *
 2318 *   Deletes a database.
 2319 *
 2320 *   <handle> is the database handle.
 2321 *   <name> is the database name.
 2322 *
 2323 *   This function returns TRUE if the command has failed, and FALSE if
 2324 *   everything was OK.
 2325 *
 2326 ******************************************************************************/
 2327 
 2328 static int database_delete(DB_DATABASE *db, const char *name)
 2329 {
 2330     if (database_is_system(db, name))
 2331     {
 2332         GB.Error("Unable to delete database: &1", "system database");
 2333         return TRUE;
 2334     }
 2335 
 2336     return do_query(db, "Unable to delete database: &1", NULL, "drop database `&1`", 1, name);
 2337 }
 2338 
 2339 /*****************************************************************************
 2340 *
 2341 *   database_create()
 2342 *
 2343 *   Creates a database.
 2344 *
 2345 *   <handle> is the database handle.
 2346 *   <name> is the database name.
 2347 *
 2348 *   This function returns TRUE if the command has failed, and FALSE if
 2349 *   everything was OK.
 2350 *
 2351 ******************************************************************************/
 2352 
 2353 static int database_create(DB_DATABASE *db, const char *name)
 2354 {
 2355     return do_query(db, "Unable to create database: &1", NULL, "create database `&1`", 1, name);
 2356 }
 2357 
 2358 
 2359 /*****************************************************************************
 2360 *
 2361 *  user_exist()
 2362 *
 2363 *  Returns if a user exists.
 2364 *
 2365 *  <handle> is any database handle.
 2366 *  <name> is the user name.
 2367 *
 2368 *  This function returns TRUE if the user exists, and FALSE if not.
 2369 *
 2370 ******************************************************************************/
 2371 
 2372 static int user_exist(DB_DATABASE *db, const char *name)
 2373 {
 2374     const char *query = "select user from mysql.user "
 2375             "where user = '&1' and host = '&2' ";
 2376     MYSQL_RES *res;
 2377     int exist;
 2378     char *_name, *_host, *_token;
 2379 
 2380     if (!strrchr(name,'@')){
 2381         //To be done: maybe we should  check hostname we are running
 2382         //from and use this instead of localhost
 2383         /* (BM) you forgot the last 0 character */
 2384         _name = malloc(strlen(name) + strlen("@localhost") + 1);
 2385         sprintf(_name,"%s@localhost", name);
 2386     }
 2387     else {
 2388         _name = malloc(strlen(name) + 1);
 2389         strcpy(_name,name);
 2390     }
 2391 
 2392     _host = strrchr(_name,'@') + 1;
 2393     _token = _host - 1;
 2394     _token[0] = 0;
 2395 
 2396 
 2397     if (do_query(db, "Unable to check user: &1@&2", &res, query, 2, _name, _host))
 2398     {
 2399         free(_name);
 2400         return FALSE;
 2401     }
 2402 
 2403     exist = mysql_num_rows(res) == 1;
 2404 
 2405     free(_name);
 2406     mysql_free_result(res);
 2407 
 2408     return exist;
 2409 }
 2410 
 2411 /*****************************************************************************
 2412 *
 2413 *   user_list()
 2414 *
 2415 *   Returns an array containing the name of each user.
 2416 *
 2417 *   <handle> is the database handle.
 2418 *   <users> points to a variable that will receive the char* array.
 2419 *
 2420 *   This function returns the number of users, or -1 if the command has
 2421 *   failed.
 2422 *
 2423 *   Be careful: <users> can be NULL, so that just the count is returned.
 2424 *
 2425 ******************************************************************************/
 2426 
 2427 static int user_list(DB_DATABASE *db, char ***users)
 2428 {
 2429     const char *query = "select user, host from mysql.user";
 2430 
 2431     MYSQL_RES *res;
 2432     MYSQL_ROW row;
 2433     MYSQL_FIELD *field;
 2434     long i, count, length;
 2435     char *_username;
 2436 
 2437     if (do_query(db, "Unable to get users: &1", &res, query, 0))
 2438         return -1;
 2439 
 2440     count = mysql_num_rows(res);
 2441 
 2442     if (users)
 2443     {
 2444         GB.NewArray(users, sizeof(char *), count);
 2445         field = mysql_fetch_field(res); //user field
 2446         length = field->max_length;
 2447         field = mysql_fetch_field(res); //host field
 2448         length += field->max_length;
 2449         _username = malloc(length + 2); /* (BM) +2 because there is the last 0 character ! */
 2450 
 2451         for ( i = 0; i < count; i++ )
 2452         {
 2453             row = mysql_fetch_row(res);
 2454             sprintf(_username,"%s@%s", row[0], row[1]);
 2455             (*users)[i] = GB.NewZeroString(_username);
 2456         }
 2457         free(_username);
 2458     }
 2459 
 2460     mysql_free_result(res);
 2461 
 2462     return count;
 2463 }
 2464 
 2465 /*****************************************************************************
 2466 *
 2467 *   user_info()
 2468 *
 2469 *   Get user description
 2470 *
 2471 *   <handle> is the database handle.
 2472 *   <name> is the user name.
 2473 *   <info> points to a structure filled by the function.
 2474 *
 2475 *   This function returns TRUE if the command has failed, and FALSE if
 2476 *   everything was OK.
 2477 *
 2478 * Mysql notes: Privileges set to Y in the mysql.user table are global settings
 2479 *              and apply to all databases. eg. These are super user privileges.
 2480 *              mysql.tables_priv lists the access granted to a user on a
 2481 *                   particular table
 2482 *              mysql.columns_priv grant column specific privileges.
 2483 *              mysql.db and mysql.host grant database specific privileges.
 2484 *
 2485 *              User may also not be unique as mysql.user also contains
 2486 *              users from different hosts.  e.g host and user columns will
 2487 *              make it unique! Using 'localhost' here to limit.
 2488 *
 2489 *              The privileges are: grant_priv - allows user to grant
 2490 *                                          privileges to others - which
 2491 *                                          includes the ability to create
 2492 *                                          users;
 2493 *                                  create_priv/drop_priv - create database,
 2494 *                                     tables etc.
 2495 ******************************************************************************/
 2496 
 2497 static int user_info(DB_DATABASE *db, const char *name, DB_USER *info )
 2498 {
 2499     const char *query =
 2500         "select create_priv, drop_priv, grant_priv, password from mysql.user "
 2501         "where user = '&1' and host = '&2'";
 2502 
 2503     MYSQL_RES *res;
 2504     MYSQL_ROW row;
 2505     char *_name, *_host, *_token;
 2506 
 2507     if (!strrchr(name,'@')){
 2508         //To be done: check hostname we are running
 2509         //from use instead of localhost
 2510         /* (BM) You forgot the last 0 character */
 2511         _name = malloc(strlen(name) + strlen("@localhost") + 1);
 2512         sprintf(_name,"%s@localhost", name);
 2513     }
 2514     else {
 2515         _name = malloc(strlen(name) + 1);
 2516         strcpy(_name,name);
 2517     }
 2518 
 2519     _host = strrchr(_name,'@') + 1;
 2520     _token = _host - 1;
 2521     _token[0] = 0;
 2522 
 2523     if (do_query(db, "Unable to check user info: &1@&2", &res, query, 2, _name, _host))
 2524     {
 2525         free(_name);
 2526         return TRUE;
 2527     }
 2528 
 2529     if (mysql_num_rows(res) != 1)
 2530     {
 2531         GB.Error("user_info: Non unique user found");
 2532         free(_name);
 2533                     mysql_free_result(res);
 2534         return TRUE;
 2535     }
 2536 
 2537     row = mysql_fetch_row(res);
 2538 
 2539     info->name = NULL;
 2540     if ( strcmp(row[0], "Y") == 0 || strcmp(row[1], "Y") == 0)
 2541         info->admin = 1;
 2542     else
 2543         info->admin = 0;
 2544 
 2545     if (row[3])
 2546         info->password = GB.NewZeroString(row[3]); //password is encrypted in mysql
 2547 
 2548     mysql_free_result(res);
 2549     free(_name);
 2550 
 2551     return FALSE;
 2552 }
 2553 
 2554 /*****************************************************************************
 2555 
 2556     user_delete()
 2557 
 2558     Deletes a user.
 2559 
 2560     <handle> is any database handle.
 2561     <name> is the user name.
 2562 
 2563     This function returns TRUE if the command has failed, and FALSE if
 2564     everything was OK.
 2565 
 2566 *****************************************************************************/
 2567 
 2568 static int user_delete(DB_DATABASE *db, const char *name)
 2569 {
 2570     const char *_delete =
 2571         "delete from mysql.user where user = '&1' and host = '&2'";
 2572 //   "delete from mysql.user, mysql.db, mysql.columns_priv, mysql.tables_priv "
 2573 //   "where user = '&1' and host = '&2'";
 2574     char *_name, *_host, *_token;
 2575     int _ret;
 2576 
 2577     if (!strrchr(name,'@')){
 2578         //To be done: maybe hostname we are running
 2579         //from should be used rather than localhost
 2580         _name = malloc(strlen(name) + strlen("@localhost")) + 1;
 2581         sprintf(_name,"%s@localhost", name);
 2582     }
 2583     else {
 2584         _name = malloc(strlen(name) + 1);
 2585         strcpy(_name,name);
 2586     }
 2587 
 2588     _host = strrchr(_name,'@') + 1;
 2589     _token = _host - 1;
 2590     _token[0] = 0;
 2591 
 2592 //Still need to look at the removal of privileges
 2593 // _ret =  do_query(db, "Unable to delete user: &1", NULL,
 2594 //             "revoke all on *.* from &1@&2", 2, _name, _host);
 2595     _ret =  do_query(db, "Unable to delete user: &1", NULL, _delete, 2, _name, _host);
 2596     free(_name);
 2597     return _ret;
 2598 }
 2599 
 2600 /*****************************************************************************
 2601 *
 2602 *   user_create()
 2603 *
 2604 *     Creates a user.
 2605 *
 2606 *     <handle> is the database handle.
 2607 *     <name> is the user name.
 2608 *     <info> points to a structure describing the user.
 2609 *
 2610 *     This function returns TRUE if the command has failed, and FALSE if
 2611 *           everything was OK.
 2612 *
 2613 ******************************************************************************/
 2614 
 2615 static int user_create(DB_DATABASE *db, const char *name, DB_USER *info)
 2616 {
 2617     char *_name;
 2618 
 2619     DB.Query.Init();
 2620 
 2621     if (!strrchr(name,'@')){
 2622         _name = malloc(strlen(name) + strlen("@localhost") + 1);
 2623         sprintf(_name,"%s@localhost", name);
 2624     }
 2625     else {
 2626         _name = malloc(strlen(name) + 1);
 2627         strcpy(_name,name);
 2628     }
 2629 
 2630     if (info->admin) {
 2631         DB.Query.Add("GRANT ALL PRIVILEGES ON *.* TO ");
 2632         DB.Query.Add(_name);
 2633     }
 2634     else {
 2635         DB.Query.Add("GRANT USAGE ON * TO ");
 2636         DB.Query.Add(_name);
 2637     }
 2638 
 2639     if (info->password)
 2640     {
 2641         DB.Query.Add(" IDENTIFIED BY '");
 2642         DB.Query.Add(info->password);
 2643         DB.Query.Add("'");
 2644     }
 2645 
 2646     if (info->admin)
 2647         DB.Query.Add(" WITH GRANT OPTION");
 2648 
 2649     free(_name);
 2650 
 2651     return do_query(db, "Cannot create user: &1", NULL, DB.Query.Get(), 0);
 2652 }
 2653 
 2654 /*****************************************************************************
 2655 *
 2656 *   user_set_password()
 2657 *
 2658 *   Change the user password.
 2659 *
 2660 *   <handle> is the database handle.
 2661 *   <name> is the user name.
 2662 *   <password> is the new password
 2663 *
 2664 *   This function returns TRUE if the command has failed, and FALSE if
 2665 *   everything was OK.
 2666 *
 2667 ******************************************************************************/
 2668 
 2669 static int user_set_password(DB_DATABASE *db, const char *name, const char *password)
 2670 {
 2671     char *_name;
 2672     DB.Query.Init();
 2673 
 2674     if (!strrchr(name,'@')){
 2675         _name = malloc(strlen(name) + strlen("@localhost") + 1);
 2676         sprintf(_name,"%s@localhost", name);
 2677     }
 2678     else {
 2679         _name = malloc(strlen(name) + 1);
 2680         strcpy(_name,name);
 2681     }
 2682 
 2683     DB.Query.Add("SET PASSWORD FOR ");
 2684     DB.Query.Add(_name);
 2685     DB.Query.Add(" = PASSWORD ('");
 2686     DB.Query.Add(password);
 2687     DB.Query.Add("')");
 2688 
 2689     free(_name);
 2690 
 2691     return do_query(db, "Cannot change user password: &1", NULL, DB.Query.Get(), 0);
 2692 }
 2693 
 2694 
 2695 /*****************************************************************************
 2696 
 2697     The driver interface
 2698 
 2699 *****************************************************************************/
 2700 
 2701 DECLARE_DRIVER(_driver, "mysql");
 2702 
 2703 /*****************************************************************************
 2704 
 2705     The component entry and exit functions.
 2706 
 2707 *****************************************************************************/
 2708 
 2709 int EXPORT GB_INIT(void)
 2710 {
 2711     GB.GetInterface("gb.db", DB_INTERFACE_VERSION, &DB);
 2712     DB.Register(&_driver);
 2713 
 2714     return 0;
 2715 }
 2716 
 2717 void EXPORT GB_EXIT()
 2718 {
 2719 }