mysqltest.c
上传用户:romrleung
上传日期:2022-05-23
资源大小:18897k
文件大小:133k
源码类别:

MySQL数据库

开发平台:

Visual C++

  1. #include <help_end.h>
  2. static my_bool
  3. get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
  4.        char *argument)
  5. {
  6.   switch(optid) {
  7.   case '#':
  8. #ifndef DBUG_OFF
  9.     DBUG_PUSH(argument ? argument : "d:t:S:i:O,/tmp/mysqltest.trace");
  10. #endif
  11.     break;
  12.   case 'r':
  13.     record = 1;
  14.     break;
  15.   case (int)OPT_MANAGER_PASSWD:
  16.     my_free(manager_pass,MYF(MY_ALLOW_ZERO_PTR));
  17.     manager_pass=my_strdup(argument, MYF(MY_FAE));
  18.     while (*argument) *argument++= 'x'; /* Destroy argument */
  19.     break;
  20.   case 'x':
  21.     {
  22.       char buff[FN_REFLEN];
  23.       if (!test_if_hard_path(argument))
  24.       {
  25. strxmov(buff, opt_basedir, argument, NullS);
  26. argument= buff;
  27.       }
  28.       fn_format(buff, argument, "", "", 4);
  29.       DBUG_ASSERT(cur_file == file_stack && cur_file->file == 0);
  30.       if (!(cur_file->file=
  31.             my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(0))))
  32. die("Could not open %s: errno = %d", buff, errno);
  33.       cur_file->file_name= my_strdup(buff, MYF(MY_FAE));
  34.       break;
  35.     }
  36.   case 'm':
  37.     {
  38.       static char buff[FN_REFLEN];
  39.       if (!test_if_hard_path(argument))
  40.       {
  41. strxmov(buff, opt_basedir, argument, NullS);
  42. argument= buff;
  43.       }
  44.       fn_format(buff, argument, "", "", 4);
  45.       timer_file= buff;
  46.       unlink(timer_file);      /* Ignore error, may not exist */
  47.       break;
  48.     }
  49.   case 'p':
  50.     if (argument)
  51.     {
  52.       my_free(pass, MYF(MY_ALLOW_ZERO_PTR));
  53.       pass= my_strdup(argument, MYF(MY_FAE));
  54.       while (*argument) *argument++= 'x'; /* Destroy argument */
  55.       tty_password= 0;
  56.     }
  57.     else
  58.       tty_password= 1;
  59.     break;
  60. #include <sslopt-case.h>
  61.   case 't':
  62.     strnmov(TMPDIR, argument, sizeof(TMPDIR));
  63.     break;
  64.   case 'A':
  65.     if (!embedded_server_arg_count)
  66.     {
  67.       embedded_server_arg_count=1;
  68.       embedded_server_args[0]= (char*) "";
  69.     }
  70.     if (embedded_server_arg_count == MAX_SERVER_ARGS-1 ||
  71.         !(embedded_server_args[embedded_server_arg_count++]=
  72.           my_strdup(argument, MYF(MY_FAE))))
  73.     {
  74.       die("Can't use server argument");
  75.     }
  76.     break;
  77.   case 'F':
  78.     if (read_server_arguments(argument))
  79.       die(NullS);
  80.     break;
  81.   case OPT_SKIP_SAFEMALLOC:
  82. #ifdef SAFEMALLOC
  83.     sf_malloc_quick=1;
  84. #endif
  85.     break;
  86.   case 'V':
  87.     print_version();
  88.     exit(0);
  89.   case '?':
  90.     usage();
  91.     exit(1);
  92.   }
  93.   return 0;
  94. }
  95. int parse_args(int argc, char **argv)
  96. {
  97.   load_defaults("my",load_default_groups,&argc,&argv);
  98.   default_argv= argv;
  99.   if ((handle_options(&argc, &argv, my_long_options, get_one_option)))
  100.     exit(1);
  101.   if (argc > 1)
  102.   {
  103.     usage();
  104.     exit(1);
  105.   }
  106.   if (argc == 1)
  107.     db= *argv;
  108.   if (tty_password)
  109.     pass=get_tty_password(NullS);
  110.   return 0;
  111. }
  112. char* safe_str_append(char *buf, const char *str, int size)
  113. {
  114.   int i,c ;
  115.   for (i = 0; (c = *str++) &&  i < size - 1; i++)
  116.     *buf++ = c;
  117.   *buf = 0;
  118.   return buf;
  119. }
  120. void str_to_file(const char *fname, char *str, int size)
  121. {
  122.   int fd;
  123.   char buff[FN_REFLEN];
  124.   if (!test_if_hard_path(fname))
  125.   {
  126.     strxmov(buff, opt_basedir, fname, NullS);
  127.     fname=buff;
  128.   }
  129.   fn_format(buff,fname,"","",4);
  130.   
  131.   if ((fd = my_open(buff, O_WRONLY | O_CREAT | O_TRUNC,
  132.     MYF(MY_WME | MY_FFNF))) < 0)
  133.     die("Could not open %s: errno = %d", buff, errno);
  134.   if (my_write(fd, (byte*)str, size, MYF(MY_WME|MY_FNABP)))
  135.     die("write failed");
  136.   my_close(fd, MYF(0));
  137. }
  138. void reject_dump(const char *record_file, char *buf, int size)
  139. {
  140.   char reject_file[FN_REFLEN];
  141.   str_to_file(fn_format(reject_file, record_file,"",".reject",2), buf, size);
  142. }
  143. /* Append the string to ds, with optional replace */
  144. static void replace_dynstr_append_mem(DYNAMIC_STRING *ds, const char *val,
  145.       int len)
  146. {
  147.   if (glob_replace)
  148.   {
  149.     len=(int) replace_strings(glob_replace, &out_buff, &out_length, val);
  150.     if (len == -1)
  151.       die("Out of memory in replace");
  152.     val=out_buff;
  153.   }
  154.   dynstr_append_mem(ds, val, len);
  155. }
  156. /*
  157.   Append all results to the dynamic string separated with 't'
  158.   Values may be converted with 'replace_column'
  159. */
  160. static void append_result(DYNAMIC_STRING *ds, MYSQL_RES *res)
  161. {
  162.   MYSQL_ROW row;
  163.   uint num_fields= mysql_num_fields(res);
  164.   MYSQL_FIELD *fields= !display_result_vertically ? 0 : mysql_fetch_fields(res);
  165.   unsigned long *lengths;
  166.   while ((row = mysql_fetch_row(res)))
  167.   {
  168.     uint i;
  169.     lengths = mysql_fetch_lengths(res);
  170.     for (i = 0; i < num_fields; i++)
  171.     {
  172.       const char *val= row[i];
  173.       ulonglong len= lengths[i];
  174.       if (i < max_replace_column && replace_column[i])
  175.       {
  176. val= replace_column[i];
  177. len= strlen(val);
  178.       }
  179.       if (!val)
  180.       {
  181. val= "NULL";
  182. len= 4;
  183.       }
  184.       if (!display_result_vertically)
  185.       {
  186. if (i)
  187.   dynstr_append_mem(ds, "t", 1);
  188. replace_dynstr_append_mem(ds, val, len);
  189.       }
  190.       else
  191.       {
  192. dynstr_append(ds, fields[i].name);
  193. dynstr_append_mem(ds, "t", 1);
  194. replace_dynstr_append_mem(ds, val, len);
  195. dynstr_append_mem(ds, "n", 1);
  196.       }
  197.     }
  198.     if (!display_result_vertically)
  199.       dynstr_append_mem(ds, "n", 1);
  200.   }
  201.   free_replace_column();
  202. }
  203. /*
  204. * flags control the phased/stages of query execution to be performed
  205. * if QUERY_SEND bit is on, the query will be sent. If QUERY_REAP is on
  206. * the result will be read - for regular query, both bits must be on
  207. */
  208. static int run_query_normal(MYSQL *mysql, struct st_query *q, int flags);
  209. static int run_query_stmt  (MYSQL *mysql, struct st_query *q, int flags);
  210. static void run_query_stmt_handle_warnings(MYSQL *mysql, DYNAMIC_STRING *ds);
  211. static int run_query_stmt_handle_error(char *query, struct st_query *q,
  212.                                        MYSQL_STMT *stmt, DYNAMIC_STRING *ds);
  213. static void run_query_display_metadata(MYSQL_FIELD *field, uint num_fields,
  214.                                        DYNAMIC_STRING *ds);
  215. static int run_query(MYSQL *mysql, struct st_query *q, int flags)
  216. {
  217.   /*
  218.     Try to find out if we can run this statement using the prepared
  219.     statement protocol.
  220.     We don't have a mysql_stmt_send_execute() so we only handle
  221.     complete SEND+REAP.
  222.     If it is a '?' in the query it may be a SQL level prepared
  223.      statement already and we can't do it twice
  224.   */
  225.   if (ps_protocol_enabled && disable_info &&
  226.       (flags & QUERY_SEND) && (flags & QUERY_REAP) && ps_match_re(q->query))
  227.     return run_query_stmt(mysql, q, flags);
  228.   return run_query_normal(mysql, q, flags);
  229. }
  230. static int run_query_normal(MYSQL* mysql, struct st_query* q, int flags)
  231. {
  232.   MYSQL_RES* res= 0;
  233.   uint i;
  234.   int error= 0, err= 0, counter= 0;
  235.   DYNAMIC_STRING *ds;
  236.   DYNAMIC_STRING ds_tmp;
  237.   DYNAMIC_STRING eval_query;
  238.   char* query;
  239.   int query_len, got_error_on_send= 0;
  240.   DBUG_ENTER("run_query_normal");
  241.   DBUG_PRINT("enter",("flags: %d", flags));
  242.   
  243.   if (q->type != Q_EVAL)
  244.   {
  245.     query = q->query;
  246.     query_len = strlen(query);
  247.   }
  248.   else
  249.   {
  250.     init_dynamic_string(&eval_query, "", 16384, 65536);
  251.     do_eval(&eval_query, q->query);
  252.     query = eval_query.str;
  253.     query_len = eval_query.length;
  254.   }
  255.   DBUG_PRINT("enter", ("query: '%-.60s'", query));
  256.   if (q->record_file[0])
  257.   {
  258.     init_dynamic_string(&ds_tmp, "", 16384, 65536);
  259.     ds = &ds_tmp;
  260.   }
  261.   else
  262.     ds= &ds_res;
  263.   if (flags & QUERY_SEND)
  264.   {
  265.     got_error_on_send= mysql_send_query(mysql, query, query_len);
  266.     if (got_error_on_send && q->expected_errno[0].type == ERR_EMPTY)
  267.       die("unable to send query '%s' (mysql_errno=%d , errno=%d)",
  268.   query, mysql_errno(mysql), errno);
  269.   }
  270.   do
  271.   {
  272.     if ((flags & QUERY_SEND) && !disable_query_log && !counter)
  273.     {
  274.       replace_dynstr_append_mem(ds,query, query_len);
  275.       dynstr_append_mem(ds, delimiter, delimiter_length);
  276.       dynstr_append_mem(ds, "n", 1);
  277.     }
  278.     if (!(flags & QUERY_REAP))
  279.       DBUG_RETURN(0);
  280.     if (got_error_on_send ||
  281. (!counter && (*mysql->methods->read_query_result)(mysql)) ||
  282.  (!(last_result= res= mysql_store_result(mysql)) &&
  283.   mysql_field_count(mysql)))
  284.     {
  285.       if (q->require_file)
  286.       {
  287. abort_not_supported_test();
  288.       }
  289.       if (q->abort_on_error)
  290. die("query '%s' failed: %d: %s", query,
  291.     mysql_errno(mysql), mysql_error(mysql));
  292.       for (i=0 ; (uint) i < q->expected_errors ; i++)
  293.       {
  294.         if (((q->expected_errno[i].type == ERR_ERRNO) &&
  295.              (q->expected_errno[i].code.errnum == mysql_errno(mysql))) ||
  296.             ((q->expected_errno[i].type == ERR_SQLSTATE) &&
  297.              (strcmp(q->expected_errno[i].code.sqlstate,mysql_sqlstate(mysql)) == 0)))
  298.         {
  299.           if (i == 0 && q->expected_errors == 1)
  300.           {
  301.             /* Only log error if there is one possible error */
  302.             dynstr_append_mem(ds,"ERROR ",6);
  303.             replace_dynstr_append_mem(ds, mysql_sqlstate(mysql),
  304.                                       strlen(mysql_sqlstate(mysql)));
  305.             dynstr_append_mem(ds, ": ", 2);
  306.             replace_dynstr_append_mem(ds,mysql_error(mysql),
  307.                                       strlen(mysql_error(mysql)));
  308.             dynstr_append_mem(ds,"n",1);
  309.           }
  310.           /* Don't log error if we may not get an error */
  311.           else if (q->expected_errno[0].type == ERR_SQLSTATE ||
  312.                    (q->expected_errno[0].type == ERR_ERRNO &&
  313.                     q->expected_errno[0].code.errnum != 0))
  314.             dynstr_append(ds,"Got one of the listed errorsn");
  315.           goto end; /* Ok */
  316.         }
  317.       }
  318.       DBUG_PRINT("info",("i: %d  expected_errors: %d", i,
  319.                          q->expected_errors));
  320.       dynstr_append_mem(ds, "ERROR ",6);
  321.       replace_dynstr_append_mem(ds, mysql_sqlstate(mysql),
  322.                                 strlen(mysql_sqlstate(mysql)));
  323.       dynstr_append_mem(ds,": ",2);
  324.       replace_dynstr_append_mem(ds, mysql_error(mysql),
  325.                                 strlen(mysql_error(mysql)));
  326.       dynstr_append_mem(ds,"n",1);
  327.       if (i)
  328.       {
  329.         if (q->expected_errno[0].type == ERR_ERRNO)
  330.           verbose_msg("query '%s' failed with wrong errno %d instead of %d...",
  331.                       q->query, mysql_errno(mysql), q->expected_errno[0].code.errnum);
  332.         else
  333.           verbose_msg("query '%s' failed with wrong sqlstate %s instead of %s...",
  334.                       q->query, mysql_sqlstate(mysql), q->expected_errno[0].code.sqlstate);
  335.         error= 1;
  336.         goto end;
  337.       }
  338.       verbose_msg("query '%s' failed: %d: %s", q->query, mysql_errno(mysql),
  339.                   mysql_error(mysql));
  340.       /*
  341.         if we do not abort on error, failure to run the query does
  342.         not fail the whole test case
  343.       */
  344.       goto end;
  345.     }
  346.     if (handle_no_error(q))
  347.     {
  348.       error= 1;
  349.       goto end;
  350.     }
  351.     if (!disable_result_log)
  352.     {
  353.       ulong affected_rows;    /* Ok to be undef if 'disable_info' is set */
  354.       LINT_INIT(affected_rows);
  355.       if (res)
  356.       {
  357. MYSQL_FIELD *field= mysql_fetch_fields(res);
  358. uint num_fields= mysql_num_fields(res);
  359. if (display_metadata)
  360.           run_query_display_metadata(field, num_fields, ds);
  361. if (!display_result_vertically)
  362. {
  363.   for (i = 0; i < num_fields; i++)
  364.   {
  365.     if (i)
  366.       dynstr_append_mem(ds, "t", 1);
  367.     replace_dynstr_append_mem(ds, field[i].name,
  368.       strlen(field[i].name));
  369.   }
  370.   dynstr_append_mem(ds, "n", 1);
  371. }
  372. append_result(ds, res);
  373.       }
  374.       /*
  375.         Need to call mysql_affected_rows() before the new
  376.         query to find the warnings
  377.       */
  378.       if (!disable_info)
  379.         affected_rows= (ulong)mysql_affected_rows(mysql);
  380.       /* Add all warnings to the result */
  381.       if (!disable_warnings && mysql_warning_count(mysql))
  382.       {
  383. MYSQL_RES *warn_res=0;
  384. uint count= mysql_warning_count(mysql);
  385. if (!mysql_real_query(mysql, "SHOW WARNINGS", 13))
  386. {
  387.   warn_res= mysql_store_result(mysql);
  388. }
  389. if (!warn_res)
  390.   verbose_msg("Warning count is %u but didn't get any warningsn",
  391.       count);
  392. else
  393. {
  394.   dynstr_append_mem(ds, "Warnings:n", 10);
  395.   append_result(ds, warn_res);
  396.   mysql_free_result(warn_res);
  397. }
  398.       }
  399.       if (!disable_info)
  400.       {
  401. char buf[40];
  402. sprintf(buf,"affected rows: %lun", affected_rows);
  403. dynstr_append(ds, buf);
  404. if (mysql_info(mysql))
  405. {
  406.   dynstr_append(ds, "info: ");
  407.   dynstr_append(ds, mysql_info(mysql));
  408.   dynstr_append_mem(ds, "n", 1);
  409. }
  410.       }
  411.     }
  412.     if (record)
  413.     {
  414.       if (!q->record_file[0] && !result_file)
  415. die("Missing result file");
  416.       if (!result_file)
  417. str_to_file(q->record_file, ds->str, ds->length);
  418.     }
  419.     else if (q->record_file[0])
  420.     {
  421.       error = check_result(ds, q->record_file, q->require_file);
  422.     }
  423.     if (res)
  424.       mysql_free_result(res);
  425.     last_result= 0;
  426.     counter++;
  427.   } while (!(err= mysql_next_result(mysql)));
  428.   if (err >= 1)
  429.     mysql_error(mysql);
  430. end:
  431.   free_replace();
  432.   last_result=0;
  433.   if (ds == &ds_tmp)
  434.     dynstr_free(&ds_tmp);
  435.   if (q->type == Q_EVAL)
  436.     dynstr_free(&eval_query);
  437.   /*
  438.     We save the return code (mysql_errno(mysql)) from the last call sent
  439.     to the server into the mysqltest builtin variable $mysql_errno. This
  440.     variable then can be used from the test case itself.
  441.   */
  442.   var_set_errno(mysql_errno(mysql));
  443.   DBUG_RETURN(error);
  444. }
  445. /****************************************************************************
  446.  *  If --ps-protocol run ordinary statements using prepared statemnt C API
  447. ****************************************************************************/
  448. /*
  449.   We don't have a mysql_stmt_send_execute() so we only handle
  450.   complete SEND+REAP
  451. */
  452. static int run_query_stmt(MYSQL *mysql, struct st_query *q, int flags)
  453. {
  454.   int error= 0;             /* Function return code if "goto end;" */
  455.   int err;                  /* Temporary storage of return code from calls */
  456.   int query_len, got_error_on_execute;
  457.   uint num_rows;
  458.   char *query;
  459.   MYSQL_RES *res= NULL;     /* Note that here 'res' is meta data result set */
  460.   DYNAMIC_STRING *ds;
  461.   DYNAMIC_STRING ds_tmp;
  462.   DYNAMIC_STRING eval_query;
  463.   MYSQL_STMT *stmt;
  464.   DBUG_ENTER("run_query_stmt");
  465.   /*
  466.     We must allocate a new stmt for each query in this program becasue this
  467.     may be a new connection.
  468.   */
  469.   if (!(stmt= mysql_stmt_init(mysql)))
  470.     die("unable init stmt structure");
  471.   
  472.   if (q->type != Q_EVAL)
  473.   {
  474.     query= q->query;
  475.     query_len= strlen(query);
  476.   }
  477.   else
  478.   {
  479.     init_dynamic_string(&eval_query, "", 16384, 65536);
  480.     do_eval(&eval_query, q->query);
  481.     query= eval_query.str;
  482.     query_len= eval_query.length;
  483.   }
  484.   DBUG_PRINT("query", ("'%-.60s'", query));
  485.   if (q->record_file[0])
  486.   {
  487.     init_dynamic_string(&ds_tmp, "", 16384, 65536);
  488.     ds= &ds_tmp;
  489.   }
  490.   else
  491.     ds= &ds_res;
  492.   /* Store the query into the output buffer if not disabled */
  493.   if (!disable_query_log)
  494.   {
  495.     replace_dynstr_append_mem(ds,query, query_len);
  496.     dynstr_append_mem(ds, delimiter, delimiter_length);
  497.     dynstr_append_mem(ds, "n", 1);
  498.   }
  499.   /*
  500.     We use the prepared statement interface but there is actually no
  501.     '?' in the query. If unpreparable we fall back to use normal
  502.     C API.
  503.   */
  504.   if ((err= mysql_stmt_prepare(stmt, query, query_len)) == CR_NO_PREPARE_STMT)
  505.     return run_query_normal(mysql, q, flags);
  506.   if (err != 0)
  507.   {
  508.     if (q->abort_on_error)
  509.     {
  510. die("query '%s' failed: %d: %s", query,
  511.     mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
  512.     }
  513.     else
  514.     {
  515.       /*
  516.         Preparing is part of normal execution and some errors may be expected
  517.       */
  518.       error= run_query_stmt_handle_error(query, q, stmt, ds);
  519.       goto end;
  520.     }
  521.   }
  522.   /* We may have got warnings already, collect them if any */
  523.   /* FIXME we only want this if the statement succeeds I think */ 
  524.   run_query_stmt_handle_warnings(mysql, ds);
  525.   /*
  526.     No need to call mysql_stmt_bind_param() because we have no 
  527.     parameter markers.
  528.     To optimize performance we use a global 'stmt' that is initiated
  529.     once. A new prepare will implicitely close the old one. When we
  530.     terminate we will lose the connection, this also closes the last
  531.     prepared statement.
  532.   */
  533.   if ((got_error_on_execute= mysql_stmt_execute(stmt)) != 0) /* 0 == Success */
  534.   {
  535.     if (q->abort_on_error)
  536.     {
  537.       /* We got an error, unexpected */
  538.       die("unable to execute statement '%s': "
  539.           "%s (mysql_stmt_errno=%d returned=%d)",
  540.           query, mysql_stmt_error(stmt),
  541.           mysql_stmt_errno(stmt), got_error_on_execute);
  542.     }
  543.     else
  544.     {
  545.       /* We got an error, maybe expected */
  546.       error= run_query_stmt_handle_error(query, q, stmt, ds);
  547.       goto end;
  548.     }
  549.   }
  550.   /*
  551.     We instruct that we want to update the "max_length" field in
  552.      mysql_stmt_store_result(), this is our only way to know how much
  553.      buffer to allocate for result data
  554.   */
  555.   {
  556.     my_bool one= 1;
  557.     if (mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH,
  558.                             (void*) &one) != 0)
  559.       die("unable to set stmt attribute "
  560.           "'STMT_ATTR_UPDATE_MAX_LENGTH': %s (returned=%d)",
  561.           query, err);
  562.   }
  563.   /*
  564.     If we got here the statement succeeded and was expected to do so,
  565.     get data. Note that this can still give errors found during execution!
  566.   */
  567.   if ((err= mysql_stmt_store_result(stmt)) != 0)
  568.   {
  569.     if (q->abort_on_error)
  570.     {
  571.       /* We got an error, unexpected */
  572.       die("unable to execute statement '%s': "
  573.           "%s (mysql_stmt_errno=%d returned=%d)",
  574.           query, mysql_stmt_error(stmt),
  575.           mysql_stmt_errno(stmt), got_error_on_execute);
  576.     }
  577.     else
  578.     {
  579.       /* We got an error, maybe expected */
  580.       error= run_query_stmt_handle_error(query, q, stmt, ds);
  581.       goto end;
  582.     }
  583.   }
  584.   /* If we got here the statement was both executed and read succeesfully */
  585.   if (handle_no_error(q))
  586.   {
  587.     error= 1;
  588.     goto end;
  589.   }
  590.   num_rows= mysql_stmt_num_rows(stmt);
  591.   /*
  592.     Not all statements creates a result set. If there is one we can
  593.     now create another normal result set that contains the meta
  594.     data. This set can be handled almost like any other non prepared
  595.     statement result set.
  596.   */
  597.   if (!disable_result_log && ((res= mysql_stmt_result_metadata(stmt)) != NULL))
  598.   {
  599.     /* Take the column count from meta info */
  600.     MYSQL_FIELD *field= mysql_fetch_fields(res);
  601.     uint num_fields= mysql_num_fields(res);
  602.     /* FIXME check error from the above? */
  603.     if (display_metadata)
  604.       run_query_display_metadata(field, num_fields, ds);
  605.     if (!display_result_vertically)
  606.     {
  607.       /* Display the table heading with the names tab separated */
  608.       uint col_idx;
  609.       for (col_idx= 0; col_idx < num_fields; col_idx++)
  610.       {
  611.         if (col_idx)
  612.           dynstr_append_mem(ds, "t", 1);
  613.         replace_dynstr_append_mem(ds, field[col_idx].name,
  614.                                   strlen(field[col_idx].name));
  615.       }
  616.       dynstr_append_mem(ds, "n", 1);
  617.     }
  618.     /* Now we are to put the real result into the output buffer */
  619.     /* FIXME when it works, create function append_stmt_result() */
  620.     {
  621.       MYSQL_BIND *bind;
  622.       my_bool *is_null;
  623.       unsigned long *length;
  624.       /* FIXME we don't handle vertical display ..... */
  625.       uint col_idx, row_idx;
  626.       /* Allocate array with bind structs, lengths and NULL flags */
  627.       bind= (MYSQL_BIND*)      my_malloc(num_fields * sizeof(MYSQL_BIND),
  628.                                          MYF(MY_WME | MY_FAE));
  629.       length= (unsigned long*) my_malloc(num_fields * sizeof(unsigned long),
  630.                                          MYF(MY_WME | MY_FAE));
  631.       is_null= (my_bool*)      my_malloc(num_fields * sizeof(my_bool),
  632.                                          MYF(MY_WME | MY_FAE));
  633.       for (col_idx= 0; col_idx < num_fields; col_idx++)
  634.       {
  635.         /* Allocate data for output */
  636.         /*
  637.           FIXME it may be a bug that for non string/blob types
  638.           'max_length' is 0, should try out 'length' in that case
  639.         */
  640.         uint max_length= max(field[col_idx].max_length + 1, 1024);
  641.         char *str_data= (char *) my_malloc(max_length, MYF(MY_WME | MY_FAE));
  642.         bind[col_idx].buffer_type= MYSQL_TYPE_STRING;
  643.         bind[col_idx].buffer= (char *)str_data;
  644.         bind[col_idx].buffer_length= max_length;
  645.         bind[col_idx].is_null= &is_null[col_idx];
  646.         bind[col_idx].length= &length[col_idx];
  647.       }
  648.       /* Fill in the data into the structures created above */
  649.       if ((err= mysql_stmt_bind_result(stmt, bind)) != 0)
  650.         die("unable to bind result to statement '%s': "
  651.             "%s (mysql_stmt_errno=%d returned=%d)",
  652.             query,
  653.             mysql_stmt_error(stmt), mysql_stmt_errno(stmt), err);
  654.       /* Read result from each row */
  655.       for (row_idx= 0; row_idx < num_rows; row_idx++)
  656.       {
  657.         if ((err= mysql_stmt_fetch(stmt)) != 0)
  658.           die("unable to fetch all rows from statement '%s': "
  659.               "%s (mysql_stmt_errno=%d returned=%d)",
  660.               query,
  661.               mysql_stmt_error(stmt), mysql_stmt_errno(stmt), err);
  662.         /* Read result from each column */
  663.         for (col_idx= 0; col_idx < num_fields; col_idx++)
  664.         {
  665.           const char *val;
  666.           ulonglong len;
  667.           if (col_idx < max_replace_column && replace_column[col_idx])
  668.           {
  669.             val= replace_column[col_idx];
  670.             len= strlen(val);
  671.           }
  672.           else if (*bind[col_idx].is_null)
  673.           {
  674.             val= "NULL";
  675.             len= 4;
  676.           }
  677.           else
  678.           {
  679.             /* FIXME is string terminated? */
  680.             val= (const char *) bind[col_idx].buffer;
  681.             len= *bind[col_idx].length;
  682.           }
  683.           if (!display_result_vertically)
  684.           {
  685.             if (col_idx)                      /* No tab before first col */
  686.               dynstr_append_mem(ds, "t", 1);
  687.             replace_dynstr_append_mem(ds, val, len);
  688.           }
  689.           else
  690.           {
  691.             dynstr_append(ds, field[col_idx].name);
  692.             dynstr_append_mem(ds, "t", 1);
  693.             replace_dynstr_append_mem(ds, val, len);
  694.             dynstr_append_mem(ds, "n", 1);
  695.           }
  696.         }
  697.         if (!display_result_vertically)
  698.           dynstr_append_mem(ds, "n", 1);
  699.       }
  700.       if ((err= mysql_stmt_fetch(stmt)) != MYSQL_NO_DATA)
  701.         die("fetch didn't end with MYSQL_NO_DATA from statement "
  702.             "'%s': %s (mysql_stmt_errno=%d returned=%d)",
  703.             query,
  704.             mysql_stmt_error(stmt), mysql_stmt_errno(stmt), err);
  705.       free_replace_column();
  706.       for (col_idx= 0; col_idx < num_fields; col_idx++)
  707.       {
  708.         /* Free data for output */
  709.         my_free((gptr)bind[col_idx].buffer, MYF(MY_WME | MY_FAE));
  710.       }
  711.       /* Free array with bind structs, lengths and NULL flags */
  712.       my_free((gptr)bind    , MYF(MY_WME | MY_FAE));
  713.       my_free((gptr)length  , MYF(MY_WME | MY_FAE));
  714.       my_free((gptr)is_null , MYF(MY_WME | MY_FAE));
  715.     }
  716.     /* Add all warnings to the result */
  717.     run_query_stmt_handle_warnings(mysql, ds);
  718.     if (!disable_info)
  719.     {
  720.       char buf[40];
  721.       sprintf(buf,"affected rows: %lun",(ulong) mysql_affected_rows(mysql));
  722.       dynstr_append(ds, buf);
  723.       if (mysql_info(mysql))
  724.       {
  725.         dynstr_append(ds, "info: ");
  726.         dynstr_append(ds, mysql_info(mysql));
  727.         dynstr_append_mem(ds, "n", 1);
  728.       }
  729.     }
  730.   }
  731.   run_query_stmt_handle_warnings(mysql, ds);
  732.   if (record)
  733.   {
  734.     if (!q->record_file[0] && !result_file)
  735.       die("Missing result file");
  736.     if (!result_file)
  737.       str_to_file(q->record_file, ds->str, ds->length);
  738.   }
  739.   else if (q->record_file[0])
  740.   {
  741.     error= check_result(ds, q->record_file, q->require_file);
  742.   }
  743.   if (res)
  744.     mysql_free_result(res);     /* Free normal result set with meta data */
  745.   last_result= 0;               /* FIXME have no idea what this is about... */
  746.   if (err >= 1)
  747.     mysql_error(mysql);         /* FIXME strange, has no effect... */
  748. end:
  749.   free_replace();
  750.   last_result=0;
  751.   if (ds == &ds_tmp)
  752.     dynstr_free(&ds_tmp);
  753.   if (q->type == Q_EVAL)
  754.     dynstr_free(&eval_query);
  755.   var_set_errno(mysql_stmt_errno(stmt));
  756.   mysql_stmt_close(stmt);
  757.   DBUG_RETURN(error);
  758. }
  759. /****************************************************************************
  760.  *  Broken out sub functions to run_query_stmt()
  761. ****************************************************************************/
  762. static void run_query_display_metadata(MYSQL_FIELD *field, uint num_fields,
  763.                                        DYNAMIC_STRING *ds)
  764. {
  765.   MYSQL_FIELD *field_end;
  766.   dynstr_append(ds,"CatalogtDatabasetTabletTable_aliastColumnt"
  767.                 "Column_aliastTypetLengthtMax lengthtIs_nullt"
  768.                 "FlagstDecimalstCharsetnrn");
  769.   for (field_end= field+num_fields ;
  770.        field < field_end ;
  771.        field++)
  772.   {
  773.     char buff[22];
  774.     dynstr_append_mem(ds, field->catalog,
  775.                           field->catalog_length);
  776.     dynstr_append_mem(ds, "t", 1);
  777.     dynstr_append_mem(ds, field->db, field->db_length);
  778.     dynstr_append_mem(ds, "t", 1);
  779.     dynstr_append_mem(ds, field->org_table,
  780.                           field->org_table_length);
  781.     dynstr_append_mem(ds, "t", 1);
  782.     dynstr_append_mem(ds, field->table,
  783.                           field->table_length);
  784.     dynstr_append_mem(ds, "t", 1);
  785.     dynstr_append_mem(ds, field->org_name,
  786.                           field->org_name_length);
  787.     dynstr_append_mem(ds, "t", 1);
  788.     dynstr_append_mem(ds, field->name, field->name_length);
  789.     dynstr_append_mem(ds, "t", 1);
  790.     int10_to_str((int) field->type, buff, 10);
  791.     dynstr_append(ds, buff);
  792.     dynstr_append_mem(ds, "t", 1);
  793.     longlong10_to_str((unsigned int) field->length, buff, 10);
  794.     dynstr_append(ds, buff);
  795.     dynstr_append_mem(ds, "t", 1);
  796.     longlong10_to_str((unsigned int) field->max_length, buff, 10);
  797.     dynstr_append(ds, buff);
  798.     dynstr_append_mem(ds, "t", 1);
  799.     dynstr_append_mem(ds, (char*) (IS_NOT_NULL(field->flags) ?
  800.                                    "N" : "Y"), 1);
  801.     dynstr_append_mem(ds, "t", 1);
  802.     int10_to_str((int) field->flags, buff, 10);
  803.     dynstr_append(ds, buff);
  804.     dynstr_append_mem(ds, "t", 1);
  805.     int10_to_str((int) field->decimals, buff, 10);
  806.     dynstr_append(ds, buff);
  807.     dynstr_append_mem(ds, "t", 1);
  808.     int10_to_str((int) field->charsetnr, buff, 10);
  809.     dynstr_append(ds, buff);
  810.     dynstr_append_mem(ds, "n", 1);
  811.   }
  812. }
  813. static void run_query_stmt_handle_warnings(MYSQL *mysql, DYNAMIC_STRING *ds)
  814. {
  815.   uint count;
  816.   DBUG_ENTER("run_query_stmt_handle_warnings");
  817.   if (!disable_warnings && (count= mysql_warning_count(mysql)))
  818.   {
  819.     if (mysql_real_query(mysql, "SHOW WARNINGS", 13) == 0)
  820.     {
  821.       MYSQL_RES *warn_res= mysql_store_result(mysql);
  822.       if (!warn_res)
  823.         verbose_msg("Warning count is %u but didn't get any warningsn",
  824.                     count);
  825.       else
  826.       {
  827.         dynstr_append_mem(ds, "Warnings:n", 10);
  828.         append_result(ds, warn_res);
  829.         mysql_free_result(warn_res);
  830.       }
  831.     }
  832.   }
  833.   DBUG_VOID_RETURN;
  834. }
  835. static int run_query_stmt_handle_error(char *query, struct st_query *q,
  836.                                        MYSQL_STMT *stmt, DYNAMIC_STRING *ds)
  837. {
  838.   if (q->require_file)              /* FIXME don't understand this one */
  839.   {
  840.     abort_not_supported_test();
  841.   }
  842.   if (q->abort_on_error)
  843.     die("query '%s' failed: %d: %s", query,
  844.         mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
  845.   else
  846.   {
  847.     int i;
  848.     for (i=0 ; (uint) i < q->expected_errors ; i++)
  849.     {
  850.       if (((q->expected_errno[i].type == ERR_ERRNO) &&
  851.            (q->expected_errno[i].code.errnum == mysql_stmt_errno(stmt))) ||
  852.           ((q->expected_errno[i].type == ERR_SQLSTATE) &&
  853.            (strcmp(q->expected_errno[i].code.sqlstate,
  854.                    mysql_stmt_sqlstate(stmt)) == 0)))
  855.       {
  856.         if (i == 0 && q->expected_errors == 1)
  857.         {
  858.           /* Only log error if there is one possible error */
  859.           dynstr_append_mem(ds,"ERROR ",6);
  860.           replace_dynstr_append_mem(ds, mysql_stmt_sqlstate(stmt),
  861.                                     strlen(mysql_stmt_sqlstate(stmt)));
  862.           dynstr_append_mem(ds, ": ", 2);
  863.           replace_dynstr_append_mem(ds,mysql_stmt_error(stmt),
  864.                                     strlen(mysql_stmt_error(stmt)));
  865.           dynstr_append_mem(ds,"n",1);
  866.         }
  867.         /* Don't log error if we may not get an error */
  868.         else if (q->expected_errno[0].type == ERR_SQLSTATE ||
  869.                  (q->expected_errno[0].type == ERR_ERRNO &&
  870.                   q->expected_errno[0].code.errnum != 0))
  871.           dynstr_append(ds,"Got one of the listed errorsn");
  872.         return 0; /* Ok */
  873.       }
  874.     }
  875.     DBUG_PRINT("info",("i: %d  expected_errors: %d", i,
  876.                        q->expected_errors));
  877.     dynstr_append_mem(ds, "ERROR ",6);
  878.     replace_dynstr_append_mem(ds, mysql_stmt_sqlstate(stmt),
  879.                               strlen(mysql_stmt_sqlstate(stmt)));
  880.     dynstr_append_mem(ds,": ",2);
  881.     replace_dynstr_append_mem(ds, mysql_stmt_error(stmt),
  882.                               strlen(mysql_stmt_error(stmt)));
  883.     dynstr_append_mem(ds,"n",1);
  884.     if (i)
  885.     {
  886.       if (q->expected_errno[0].type == ERR_ERRNO)
  887.         verbose_msg("query '%s' failed with wrong errno %d instead of %d...",
  888.                     q->query, mysql_stmt_errno(stmt),
  889.                     q->expected_errno[0].code.errnum);
  890.       else
  891.         verbose_msg("query '%s' failed with wrong sqlstate %s instead of %s...",
  892.                       q->query, mysql_stmt_sqlstate(stmt),
  893.                       q->expected_errno[0].code.sqlstate);
  894.       return 1; /* Error */
  895.     }
  896.     verbose_msg("query '%s' failed: %d: %s", q->query, mysql_stmt_errno(stmt),
  897.                 mysql_stmt_error(stmt));
  898.     /*
  899.       if we do not abort on error, failure to run the query does
  900.       not fail the whole test case
  901.     */
  902.     return 0;
  903.   }
  904.   return 0;
  905. }
  906. /*
  907.   Handle absence of errors after execution
  908.   SYNOPSIS
  909.     handle_no_error()
  910.       q - context of query
  911.   RETURN VALUE
  912.     0 - OK
  913.     1 - Some error was expected from this query.
  914. */
  915. static int handle_no_error(struct st_query *q)
  916. {
  917.   DBUG_ENTER("handle_no_error");
  918.   if (q->expected_errno[0].type == ERR_ERRNO &&
  919.       q->expected_errno[0].code.errnum != 0)
  920.   {
  921.     /* Error code we wanted was != 0, i.e. not an expected success */
  922.     verbose_msg("query '%s' succeeded - should have failed with errno %d...",
  923.                 q->query, q->expected_errno[0].code.errnum);
  924.     DBUG_RETURN(1);
  925.   }
  926.   else if (q->expected_errno[0].type == ERR_SQLSTATE &&
  927.            strcmp(q->expected_errno[0].code.sqlstate,"00000") != 0)
  928.   {
  929.     /* SQLSTATE we wanted was != "00000", i.e. not an expected success */
  930.     verbose_msg("query '%s' succeeded - should have failed with sqlstate %s...",
  931.                 q->query, q->expected_errno[0].code.sqlstate);
  932.     DBUG_RETURN(1);
  933.   }
  934.   DBUG_RETURN(0);
  935. }
  936. /****************************************************************************
  937.  *  Functions to match SQL statements that can be prepared
  938. ****************************************************************************/
  939. static void ps_init_re(void)
  940. {
  941.   const char *ps_re_str =
  942.     "^("
  943.     "[[:space:]]*REPLACE[[:space:]]|"
  944.     "[[:space:]]*INSERT[[:space:]]|"
  945.     "[[:space:]]*UPDATE[[:space:]]|"
  946.     "[[:space:]]*DELETE[[:space:]]|"
  947.     "[[:space:]]*SELECT[[:space:]]|"
  948.     "[[:space:]]*CREATE[[:space:]]+TABLE[[:space:]]|"
  949.     "[[:space:]]*DO[[:space:]]|"
  950.     "[[:space:]]*SET[[:space:]]+OPTION[[:space:]]|"
  951.     "[[:space:]]*DELETE[[:space:]]+MULTI[[:space:]]|"
  952.     "[[:space:]]*UPDATE[[:space:]]+MULTI[[:space:]]|"
  953.     "[[:space:]]*INSERT[[:space:]]+SELECT[[:space:]])";
  954.   int err= my_regcomp(&ps_re, ps_re_str,
  955.                       (REG_EXTENDED | REG_ICASE | REG_NOSUB),
  956.                       &my_charset_latin1);
  957.   if (err)
  958.   {
  959.     char erbuf[100];
  960.     int len= my_regerror(err, &ps_re, erbuf, sizeof(erbuf));
  961.     fprintf(stderr, "error %s, %d/%d `%s'n",
  962.             ps_eprint(err), len, (int)sizeof(erbuf), erbuf);
  963.     exit(1);
  964.   }
  965. }
  966. static int ps_match_re(char *stmt_str)
  967. {
  968.   int err= my_regexec(&ps_re, stmt_str, (size_t)0, NULL, 0);
  969.   if (err == 0)
  970.     return 1;
  971.   else if (err == REG_NOMATCH)
  972.     return 0;
  973.   else
  974.   {
  975.     char erbuf[100];
  976.     int len= my_regerror(err, &ps_re, erbuf, sizeof(erbuf));
  977.     fprintf(stderr, "error %s, %d/%d `%s'n",
  978.             ps_eprint(err), len, (int)sizeof(erbuf), erbuf);
  979.     exit(1);
  980.   }
  981. }
  982. static char *ps_eprint(int err)
  983. {
  984.   static char epbuf[100];
  985.   size_t len= my_regerror(REG_ITOA|err, (my_regex_t *)NULL, epbuf, sizeof(epbuf));
  986.   assert(len <= sizeof(epbuf));
  987.   return(epbuf);
  988. }
  989. static void ps_free_reg(void)
  990. {
  991.   my_regfree(&ps_re);
  992. }
  993. /****************************************************************************/
  994. void get_query_type(struct st_query* q)
  995. {
  996.   char save;
  997.   uint type;
  998.   DBUG_ENTER("get_query_type");
  999.   if (*q->query == '}')
  1000.   {
  1001.     q->type = Q_END_BLOCK;
  1002.     DBUG_VOID_RETURN;
  1003.   }
  1004.   if (q->type != Q_COMMENT_WITH_COMMAND)
  1005.     q->type = Q_QUERY;
  1006.   save=q->query[q->first_word_len];
  1007.   q->query[q->first_word_len]=0;
  1008.   type=find_type(q->query, &command_typelib, 1+2);
  1009.   q->query[q->first_word_len]=save;
  1010.   if (type > 0)
  1011.     q->type=(enum enum_commands) type; /* Found command */
  1012.   DBUG_VOID_RETURN;
  1013. }
  1014. static byte *get_var_key(const byte* var, uint* len,
  1015.  my_bool __attribute__((unused)) t)
  1016. {
  1017.   register char* key;
  1018.   key = ((VAR*)var)->name;
  1019.   *len = ((VAR*)var)->name_len;
  1020.   return (byte*)key;
  1021. }
  1022. static VAR *var_init(VAR *v, const char *name, int name_len, const char *val,
  1023.      int val_len)
  1024. {
  1025.   int val_alloc_len;
  1026.   VAR *tmp_var;
  1027.   if (!name_len && name)
  1028.     name_len = strlen(name);
  1029.   if (!val_len && val)
  1030.     val_len = strlen(val) ;
  1031.   val_alloc_len = val_len + 16; /* room to grow */
  1032.   if (!(tmp_var=v) && !(tmp_var = (VAR*)my_malloc(sizeof(*tmp_var)
  1033.  + name_len, MYF(MY_WME))))
  1034.     die("Out of memory");
  1035.   tmp_var->name = (name) ? (char*) tmp_var + sizeof(*tmp_var) : 0;
  1036.   tmp_var->alloced = (v == 0);
  1037.   if (!(tmp_var->str_val = my_malloc(val_alloc_len+1, MYF(MY_WME))))
  1038.     die("Out of memory");
  1039.   memcpy(tmp_var->name, name, name_len);
  1040.   if (val)
  1041.   {
  1042.     memcpy(tmp_var->str_val, val, val_len);
  1043.     tmp_var->str_val[val_len]=0;
  1044.   }
  1045.   tmp_var->name_len = name_len;
  1046.   tmp_var->str_val_len = val_len;
  1047.   tmp_var->alloced_len = val_alloc_len;
  1048.   tmp_var->int_val = (val) ? atoi(val) : 0;
  1049.   tmp_var->int_dirty = 0;
  1050.   return tmp_var;
  1051. }
  1052. static void var_free(void *v)
  1053. {
  1054.   my_free(((VAR*) v)->str_val, MYF(MY_WME));
  1055.   if (((VAR*)v)->alloced)
  1056.    my_free((char*) v, MYF(MY_WME));
  1057. }
  1058. static VAR* var_from_env(const char *name, const char *def_val)
  1059. {
  1060.   const char *tmp;
  1061.   VAR *v;
  1062.   if (!(tmp = getenv(name)))
  1063.     tmp = def_val;
  1064.   v = var_init(0, name, strlen(name), tmp, strlen(tmp));
  1065.   my_hash_insert(&var_hash, (byte*)v);
  1066.   return v;
  1067. }
  1068. static void init_var_hash(MYSQL *mysql)
  1069. {
  1070.   VAR *v;
  1071.   DBUG_ENTER("init_var_hash");
  1072.   if (hash_init(&var_hash, charset_info, 
  1073.                 1024, 0, 0, get_var_key, var_free, MYF(0)))
  1074.     die("Variable hash initialization failed");
  1075.   if (opt_big_test)
  1076.     my_hash_insert(&var_hash, (byte*) var_init(0,"BIG_TEST", 0, "1",0));
  1077.   v= var_init(0,"MAX_TABLES", 0, (sizeof(ulong) == 4) ? "31" : "62",0);
  1078.   my_hash_insert(&var_hash, (byte*) v);
  1079.   v= var_init(0,"SERVER_VERSION", 0, mysql_get_server_info(mysql), 0);
  1080.   my_hash_insert(&var_hash, (byte*) v);
  1081.   v= var_init(0,"DB", 2, db, 0);
  1082.   my_hash_insert(&var_hash, (byte*) v);
  1083.   DBUG_VOID_RETURN;
  1084. }
  1085. int main(int argc, char **argv)
  1086. {
  1087.   int error = 0;
  1088.   struct st_query *q;
  1089.   my_bool require_file=0, q_send_flag=0, query_executed= 0, abort_flag= 0;
  1090.   char save_file[FN_REFLEN];
  1091.   MY_STAT res_info;
  1092.   MY_INIT(argv[0]);
  1093.   {
  1094.   DBUG_ENTER("main");
  1095.   DBUG_PROCESS(argv[0]);
  1096.   /* Use all time until exit if no explicit 'start_timer' */
  1097.   timer_start= timer_now();
  1098.   save_file[0]=0;
  1099.   TMPDIR[0]=0;
  1100.   memset(cons, 0, sizeof(cons));
  1101.   cons_end = cons + MAX_CONS;
  1102.   next_con = cons + 1;
  1103.   cur_con = cons;
  1104.   memset(file_stack, 0, sizeof(file_stack));
  1105.   memset(&master_pos, 0, sizeof(master_pos));
  1106.   file_stack_end= file_stack + MAX_INCLUDE_DEPTH - 1;
  1107.   cur_file= file_stack;
  1108.   lineno   = lineno_stack;
  1109.   my_init_dynamic_array(&q_lines, sizeof(struct st_query*), INIT_Q_LINES,
  1110.      INIT_Q_LINES);
  1111.   memset(block_stack, 0, sizeof(block_stack));
  1112.   block_stack_end= block_stack + BLOCK_STACK_DEPTH - 1;
  1113.   cur_block= block_stack;
  1114.   cur_block->ok= TRUE; /* Outer block should always be executed */
  1115.   cur_block->cmd= Q_UNKNOWN;
  1116.   init_dynamic_string(&ds_res, "", 0, 65536);
  1117.   parse_args(argc, argv);
  1118.   if (mysql_server_init(embedded_server_arg_count,
  1119. embedded_server_args,
  1120. (char**) embedded_server_groups))
  1121.     die("Can't initialize MySQL server");
  1122.   if (cur_file == file_stack && cur_file->file == 0)
  1123.   {
  1124.     cur_file->file= stdin;
  1125.     cur_file->file_name= my_strdup("<stdin>", MYF(MY_WME));
  1126.   }
  1127.   *lineno=1;
  1128. #ifndef EMBEDDED_LIBRARY
  1129.   if (manager_host)
  1130.     init_manager();
  1131. #endif
  1132.   if (ps_protocol)
  1133.   {
  1134.     ps_protocol_enabled= 1;
  1135.     ps_init_re();
  1136.   }
  1137.   if (!( mysql_init(&cur_con->mysql)))
  1138.     die("Failed in mysql_init()");
  1139.   if (opt_compress)
  1140.     mysql_options(&cur_con->mysql,MYSQL_OPT_COMPRESS,NullS);
  1141.   mysql_options(&cur_con->mysql, MYSQL_OPT_LOCAL_INFILE, 0);
  1142.   mysql_options(&cur_con->mysql, MYSQL_SET_CHARSET_NAME, charset_name);
  1143. #ifdef HAVE_OPENSSL
  1144.   if (opt_use_ssl)
  1145.     mysql_ssl_set(&cur_con->mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca,
  1146.   opt_ssl_capath, opt_ssl_cipher);
  1147. #endif
  1148.   if (!(cur_con->name = my_strdup("default", MYF(MY_WME))))
  1149.     die("Out of memory");
  1150.   if (safe_connect(&cur_con->mysql, host, user, pass, db, port, unix_sock))
  1151.     die("Failed in mysql_real_connect(): %s", mysql_error(&cur_con->mysql));
  1152.   init_var_hash(&cur_con->mysql);
  1153.   /*
  1154.     Initialize $mysql_errno with -1, so we can
  1155.     - distinguish it from valid values ( >= 0 ) and
  1156.     - detect if there was never a command sent to the server
  1157.   */
  1158.   var_set_errno(-1);
  1159.   while (!abort_flag && !read_query(&q))
  1160.   {
  1161.     int current_line_inc = 1, processed = 0;
  1162.     if (q->type == Q_UNKNOWN || q->type == Q_COMMENT_WITH_COMMAND)
  1163.       get_query_type(q);
  1164.     if (cur_block->ok)
  1165.     {
  1166.       q->last_argument= q->first_argument;
  1167.       processed = 1;
  1168.       switch (q->type) {
  1169.       case Q_CONNECT: do_connect(q); break;
  1170.       case Q_CONNECTION: select_connection(q); break;
  1171.       case Q_DISCONNECT:
  1172.       case Q_DIRTY_CLOSE:
  1173. close_connection(q); break;
  1174.       case Q_RPL_PROBE: do_rpl_probe(q); break;
  1175.       case Q_ENABLE_RPL_PARSE:  do_enable_rpl_parse(q); break;
  1176.       case Q_DISABLE_RPL_PARSE:  do_disable_rpl_parse(q); break;
  1177.       case Q_ENABLE_QUERY_LOG:   disable_query_log=0; break;
  1178.       case Q_DISABLE_QUERY_LOG:  disable_query_log=1; break;
  1179.       case Q_ENABLE_ABORT_ON_ERROR:  abort_on_error=1; break;
  1180.       case Q_DISABLE_ABORT_ON_ERROR: abort_on_error=0; break;
  1181.       case Q_ENABLE_RESULT_LOG:  disable_result_log=0; break;
  1182.       case Q_DISABLE_RESULT_LOG: disable_result_log=1; break;
  1183.       case Q_ENABLE_WARNINGS:    disable_warnings=0; break;
  1184.       case Q_DISABLE_WARNINGS:   disable_warnings=1; break;
  1185.       case Q_ENABLE_INFO:        disable_info=0; break;
  1186.       case Q_DISABLE_INFO:       disable_info=1; break;
  1187.       case Q_ENABLE_METADATA:    display_metadata=1; break;
  1188.       case Q_DISABLE_METADATA:   display_metadata=0; break;
  1189.       case Q_SOURCE: do_source(q); break;
  1190.       case Q_SLEEP: do_sleep(q, 0); break;
  1191.       case Q_REAL_SLEEP: do_sleep(q, 1); break;
  1192.       case Q_WAIT_FOR_SLAVE_TO_STOP: do_wait_for_slave_to_stop(q); break;
  1193.       case Q_REQUIRE_MANAGER: do_require_manager(q); break;
  1194. #ifndef EMBEDDED_LIBRARY
  1195.       case Q_SERVER_START: do_server_start(q); break;
  1196.       case Q_SERVER_STOP: do_server_stop(q); break;
  1197. #endif
  1198.       case Q_INC: do_modify_var(q, "inc", DO_INC); break;
  1199.       case Q_DEC: do_modify_var(q, "dec", DO_DEC); break;
  1200.       case Q_ECHO: do_echo(q); break;
  1201.       case Q_SYSTEM: do_system(q); break;
  1202.       case Q_DELIMITER:
  1203. strmake(delimiter, q->first_argument, sizeof(delimiter) - 1);
  1204. delimiter_length= strlen(delimiter);
  1205.         q->last_argument= q->first_argument+delimiter_length;
  1206. break;
  1207.       case Q_DISPLAY_VERTICAL_RESULTS:
  1208.         display_result_vertically= TRUE;
  1209.         break;
  1210.       case Q_DISPLAY_HORIZONTAL_RESULTS:
  1211. display_result_vertically= FALSE;
  1212.         break;
  1213.       case Q_LET: do_let(q); break;
  1214.       case Q_EVAL_RESULT:
  1215.         eval_result = 1; break;
  1216.       case Q_EVAL:
  1217. if (q->query == q->query_buf)
  1218.         {
  1219.   q->query= q->first_argument;
  1220.           q->first_word_len= 0;
  1221.         }
  1222. /* fall through */
  1223.       case Q_QUERY_VERTICAL:
  1224.       case Q_QUERY_HORIZONTAL:
  1225.       {
  1226. my_bool old_display_result_vertically= display_result_vertically;
  1227. if (!q->query[q->first_word_len])
  1228. {
  1229.   /* This happens when we use 'query_..' on it's own line */
  1230.   q_send_flag=1;
  1231.           DBUG_PRINT("info",
  1232.                      ("query: '%s' first_word_len: %d  send_flag=1",
  1233.                       q->query, q->first_word_len));
  1234.   break;
  1235. }
  1236. /* fix up query pointer if this is * first iteration for this line */
  1237. if (q->query == q->query_buf)
  1238.   q->query += q->first_word_len + 1;
  1239. display_result_vertically= (q->type==Q_QUERY_VERTICAL);
  1240. if (save_file[0])
  1241. {
  1242.   strmov(q->record_file,save_file);
  1243.   q->require_file=require_file;
  1244.   save_file[0]=0;
  1245. }
  1246. error|= run_query(&cur_con->mysql, q, QUERY_REAP|QUERY_SEND);
  1247. display_result_vertically= old_display_result_vertically;
  1248.         q->last_argument= q->end;
  1249.         query_executed= 1;
  1250. break;
  1251.       }
  1252.       case Q_QUERY:
  1253.       case Q_REAP:
  1254.       {
  1255. /*
  1256.   We read the result always regardless of the mode for both full
  1257.   query and read-result only (reap)
  1258. */
  1259. int flags = QUERY_REAP;
  1260. if (q->type != Q_REAP) /* for a full query, enable the send stage */
  1261.   flags |= QUERY_SEND;
  1262. if (q_send_flag)
  1263. {
  1264.   flags= QUERY_SEND;
  1265.   q_send_flag=0;
  1266. }
  1267. if (save_file[0])
  1268. {
  1269.   strmov(q->record_file,save_file);
  1270.   q->require_file=require_file;
  1271.   save_file[0]=0;
  1272. }
  1273. error |= run_query(&cur_con->mysql, q, flags);
  1274. query_executed= 1;
  1275.         q->last_argument= q->end;
  1276. break;
  1277.       }
  1278.       case Q_SEND:
  1279. if (!q->query[q->first_word_len])
  1280. {
  1281.   /* This happens when we use 'send' on it's own line */
  1282.   q_send_flag=1;
  1283.   break;
  1284. }
  1285. /* fix up query pointer if this is * first iteration for this line */
  1286. if (q->query == q->query_buf)
  1287.   q->query += q->first_word_len;
  1288. /*
  1289.   run_query() can execute a query partially, depending on the flags
  1290.   QUERY_SEND flag without QUERY_REAP tells it to just send the
  1291.   query and read the result some time later when reap instruction
  1292.   is given on this connection.
  1293.  */
  1294. error |= run_query(&cur_con->mysql, q, QUERY_SEND);
  1295. query_executed= 1;
  1296.         q->last_argument= q->end;
  1297. break;
  1298.       case Q_RESULT:
  1299. get_file_name(save_file,q);
  1300. require_file=0;
  1301. break;
  1302.       case Q_ERROR:
  1303.         global_expected_errors=get_errcodes(global_expected_errno,q);
  1304. break;
  1305.       case Q_REQUIRE:
  1306. get_file_name(save_file,q);
  1307. require_file=1;
  1308. break;
  1309.       case Q_REPLACE:
  1310. get_replace(q);
  1311. break;
  1312.       case Q_REPLACE_COLUMN:
  1313. get_replace_column(q);
  1314. break;
  1315.       case Q_SAVE_MASTER_POS: do_save_master_pos(); break;
  1316.       case Q_SYNC_WITH_MASTER: do_sync_with_master(q); break;
  1317.       case Q_SYNC_SLAVE_WITH_MASTER:
  1318.       {
  1319. do_save_master_pos();
  1320. if (*q->first_argument)
  1321.   select_connection(q);
  1322. else
  1323.   select_connection_name("slave");
  1324. do_sync_with_master2(0);
  1325. break;
  1326.       }
  1327.       case Q_COMMENT: /* Ignore row */
  1328.       case Q_COMMENT_WITH_COMMAND:
  1329.         q->last_argument= q->end;
  1330. break;
  1331.       case Q_PING:
  1332. (void) mysql_ping(&cur_con->mysql);
  1333. break;
  1334.       case Q_EXEC:
  1335. do_exec(q);
  1336. query_executed= 1;
  1337. break;
  1338.       case Q_START_TIMER:
  1339. /* Overwrite possible earlier start of timer */
  1340. timer_start= timer_now();
  1341. break;
  1342.       case Q_END_TIMER:
  1343. /* End timer before ending mysqltest */
  1344. timer_output();
  1345. got_end_timer= TRUE;
  1346. break;
  1347.       case Q_CHARACTER_SET:
  1348. set_charset(q);
  1349. break;
  1350.       case Q_DISABLE_PS_PROTOCOL:
  1351.         ps_protocol_enabled= 0;
  1352.         break;
  1353.       case Q_ENABLE_PS_PROTOCOL:
  1354.         ps_protocol_enabled= ps_protocol;
  1355.         break;
  1356.       case Q_DISABLE_RECONNECT:
  1357.         cur_con->mysql.reconnect= 0;
  1358.         break;
  1359.       case Q_ENABLE_RECONNECT:
  1360.         cur_con->mysql.reconnect= 1;
  1361.         break;
  1362.       case Q_EXIT:
  1363.         abort_flag= 1;
  1364.         break;
  1365.       default: processed = 0; break;
  1366.       }
  1367.     }
  1368.     if (!processed)
  1369.     {
  1370.       current_line_inc = 0;
  1371.       switch (q->type) {
  1372.       case Q_WHILE: do_block(Q_WHILE, q); break;
  1373.       case Q_IF: do_block(Q_IF, q); break;
  1374.       case Q_END_BLOCK: do_done(q); break;
  1375.       default: current_line_inc = 1; break;
  1376.       }
  1377.     }
  1378.     else
  1379.       check_eol_junk(q->last_argument);
  1380.     if (q->type != Q_ERROR)
  1381.     {
  1382.       /*
  1383.         As soon as any non "error" command has been executed,
  1384.         the array with expected errors should be cleared
  1385.       */
  1386.       global_expected_errors= 0;
  1387.       bzero((gptr) global_expected_errno, sizeof(global_expected_errno));
  1388.     }
  1389.     parser.current_line += current_line_inc;
  1390.   }
  1391.   if (!query_executed && result_file && my_stat(result_file, &res_info, 0))
  1392.   {
  1393.     /*
  1394.       my_stat() successful on result file. Check if we have not run a
  1395.       single query, but we do have a result file that contains data.
  1396.       Note that we don't care, if my_stat() fails. For example for
  1397.       non-existing or non-readable file we assume it's fine to have
  1398.       no query output from the test file, e.g. regarded as no error.
  1399.     */
  1400.     if (res_info.st_size)
  1401.       error|= (RESULT_CONTENT_MISMATCH | RESULT_LENGTH_MISMATCH);
  1402.   }
  1403.   if (ds_res.length && !error)
  1404.   {
  1405.     if (result_file)
  1406.     {
  1407.       if (!record)
  1408.         error |= check_result(&ds_res, result_file, q->require_file);
  1409.       else
  1410.         str_to_file(result_file, ds_res.str, ds_res.length);
  1411.     }
  1412.     else
  1413.     {
  1414.       /* Print the result to stdout */
  1415.       printf("%s", ds_res.str);
  1416.     }
  1417.   }
  1418.   dynstr_free(&ds_res);
  1419.   if (!silent)
  1420.   {
  1421.     if (error)
  1422.       printf("not okn");
  1423.     else
  1424.       printf("okn");
  1425.   }
  1426.   if (!got_end_timer)
  1427.     timer_output(); /* No end_timer cmd, end it */
  1428.   free_used_memory();
  1429.   my_end(MY_CHECK_ERROR);
  1430.   exit(error ? 1 : 0);
  1431.   return error ? 1 : 0; /* Keep compiler happy */
  1432.   }
  1433. }
  1434. /*
  1435.   Read arguments for embedded server and put them into
  1436.   embedded_server_args_count and embedded_server_args[]
  1437. */
  1438. static int read_server_arguments(const char *name)
  1439. {
  1440.   char argument[1024],buff[FN_REFLEN], *str=0;
  1441.   FILE *file;
  1442.   if (!test_if_hard_path(name))
  1443.   {
  1444.     strxmov(buff, opt_basedir, name, NullS);
  1445.     name=buff;
  1446.   }
  1447.   fn_format(buff,name,"","",4);
  1448.   if (!embedded_server_arg_count)
  1449.   {
  1450.     embedded_server_arg_count=1;
  1451.     embedded_server_args[0]= (char*) ""; /* Progname */
  1452.   }
  1453.   if (!(file=my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(MY_WME))))
  1454.     return 1;
  1455.   while (embedded_server_arg_count < MAX_SERVER_ARGS &&
  1456.  (str=fgets(argument,sizeof(argument), file)))
  1457.   {
  1458.     *(strend(str)-1)=0; /* Remove end newline */
  1459.     if (!(embedded_server_args[embedded_server_arg_count]=
  1460.   (char*) my_strdup(str,MYF(MY_WME))))
  1461.     {
  1462.       my_fclose(file,MYF(0));
  1463.       return 1;
  1464.     }
  1465.     embedded_server_arg_count++;
  1466.   }
  1467.   my_fclose(file,MYF(0));
  1468.   if (str)
  1469.   {
  1470.     fprintf(stderr,"Too many arguments in option file: %sn",name);
  1471.     return 1;
  1472.   }
  1473.   return 0;
  1474. }
  1475. /****************************************************************************
  1476.  *
  1477.  *  A primitive timer that give results in milliseconds if the
  1478.  *  --timer-file=<filename> is given. The timer result is written
  1479.  *  to that file when the result is available. To not confuse
  1480.  *  mysql-test-run with an old obsolete result, we remove the file
  1481.  *  before executing any commands. The time we measure is
  1482.  *
  1483.  *    - If no explicit 'start_timer' or 'end_timer' is given in the
  1484.  *      test case, the timer measure how long we execute in mysqltest.
  1485.  *
  1486.  *    - If only 'start_timer' is given we measure how long we execute
  1487.  *      from that point until we terminate mysqltest.
  1488.  *
  1489.  *    - If only 'end_timer' is given we measure how long we execute
  1490.  *      from that we enter mysqltest to the 'end_timer' is command is
  1491.  *      executed.
  1492.  *
  1493.  *    - If both 'start_timer' and 'end_timer' are given we measure
  1494.  *      the time between executing the two commands.
  1495.  *
  1496. ****************************************************************************/
  1497. static void timer_output(void)
  1498. {
  1499.   if (timer_file)
  1500.   {
  1501.     char buf[32], *end;
  1502.     ulonglong timer= timer_now() - timer_start;
  1503.     end= longlong2str(timer, buf, 10);
  1504.     str_to_file(timer_file,buf, (int) (end-buf));
  1505.   }
  1506. }
  1507. static ulonglong timer_now(void)
  1508. {
  1509.   return my_getsystime() / 10000;
  1510. }
  1511. /****************************************************************************
  1512. * Handle replacement of strings
  1513. ****************************************************************************/
  1514. #define PC_MALLOC 256 /* Bytes for pointers */
  1515. #define PS_MALLOC 512 /* Bytes for data */
  1516. #define SPACE_CHAR 256
  1517. #define START_OF_LINE 257
  1518. #define END_OF_LINE 258
  1519. #define LAST_CHAR_CODE 259
  1520. typedef struct st_replace {
  1521.   bool  found;
  1522.   struct st_replace *next[256];
  1523. } REPLACE;
  1524. typedef struct st_replace_found {
  1525.   bool found;
  1526.   char *replace_string;
  1527.   uint to_offset;
  1528.   int from_offset;
  1529. } REPLACE_STRING;
  1530. #ifndef WORD_BIT
  1531. #define WORD_BIT (8*sizeof(uint))
  1532. #endif
  1533. static int insert_pointer_name(reg1 POINTER_ARRAY *pa,my_string name)
  1534. {
  1535.   uint i,length,old_count;
  1536.   byte *new_pos;
  1537.   const char **new_array;
  1538.   DBUG_ENTER("insert_pointer_name");
  1539.   if (! pa->typelib.count)
  1540.   {
  1541.     if (!(pa->typelib.type_names=(const char **)
  1542.   my_malloc(((PC_MALLOC-MALLOC_OVERHEAD)/
  1543.      (sizeof(my_string)+sizeof(*pa->flag))*
  1544.      (sizeof(my_string)+sizeof(*pa->flag))),MYF(MY_WME))))
  1545.       DBUG_RETURN(-1);
  1546.     if (!(pa->str= (byte*) my_malloc((uint) (PS_MALLOC-MALLOC_OVERHEAD),
  1547.      MYF(MY_WME))))
  1548.     {
  1549.       my_free((gptr) pa->typelib.type_names,MYF(0));
  1550.       DBUG_RETURN (-1);
  1551.     }
  1552.     pa->max_count=(PC_MALLOC-MALLOC_OVERHEAD)/(sizeof(byte*)+
  1553.        sizeof(*pa->flag));
  1554.     pa->flag= (int7*) (pa->typelib.type_names+pa->max_count);
  1555.     pa->length=0;
  1556.     pa->max_length=PS_MALLOC-MALLOC_OVERHEAD;
  1557.     pa->array_allocs=1;
  1558.   }
  1559.   length=(uint) strlen(name)+1;
  1560.   if (pa->length+length >= pa->max_length)
  1561.   {
  1562.     if (!(new_pos= (byte*) my_realloc((gptr) pa->str,
  1563.       (uint) (pa->max_length+PS_MALLOC),
  1564.       MYF(MY_WME))))
  1565.       DBUG_RETURN(1);
  1566.     if (new_pos != pa->str)
  1567.     {
  1568.       my_ptrdiff_t diff=PTR_BYTE_DIFF(new_pos,pa->str);
  1569.       for (i=0 ; i < pa->typelib.count ; i++)
  1570. pa->typelib.type_names[i]= ADD_TO_PTR(pa->typelib.type_names[i],diff,
  1571.       char*);
  1572.       pa->str=new_pos;
  1573.     }
  1574.     pa->max_length+=PS_MALLOC;
  1575.   }
  1576.   if (pa->typelib.count >= pa->max_count-1)
  1577.   {
  1578.     int len;
  1579.     pa->array_allocs++;
  1580.     len=(PC_MALLOC*pa->array_allocs - MALLOC_OVERHEAD);
  1581.     if (!(new_array=(const char **) my_realloc((gptr) pa->typelib.type_names,
  1582.        (uint) len/
  1583.  (sizeof(byte*)+sizeof(*pa->flag))*
  1584.  (sizeof(byte*)+sizeof(*pa->flag)),
  1585.  MYF(MY_WME))))
  1586.       DBUG_RETURN(1);
  1587.     pa->typelib.type_names=new_array;
  1588.     old_count=pa->max_count;
  1589.     pa->max_count=len/(sizeof(byte*) + sizeof(*pa->flag));
  1590.     pa->flag= (int7*) (pa->typelib.type_names+pa->max_count);
  1591.     memcpy((byte*) pa->flag,(my_string) (pa->typelib.type_names+old_count),
  1592.    old_count*sizeof(*pa->flag));
  1593.   }
  1594.   pa->flag[pa->typelib.count]=0; /* Reset flag */
  1595.   pa->typelib.type_names[pa->typelib.count++]= pa->str+pa->length;
  1596.   pa->typelib.type_names[pa->typelib.count]= NullS; /* Put end-mark */
  1597.   VOID(strmov(pa->str+pa->length,name));
  1598.   pa->length+=length;
  1599.   DBUG_RETURN(0);
  1600. } /* insert_pointer_name */
  1601. /* free pointer array */
  1602. void free_pointer_array(POINTER_ARRAY *pa)
  1603. {
  1604.   if (pa->typelib.count)
  1605.   {
  1606.     pa->typelib.count=0;
  1607.     my_free((gptr) pa->typelib.type_names,MYF(0));
  1608.     pa->typelib.type_names=0;
  1609.     my_free((gptr) pa->str,MYF(0));
  1610.   }
  1611. } /* free_pointer_array */
  1612. /* Code for replace rutines */
  1613. #define SET_MALLOC_HUNC 64
  1614. typedef struct st_rep_set {
  1615.   uint *bits; /* Pointer to used sets */
  1616.   short next[LAST_CHAR_CODE]; /* Pointer to next sets */
  1617.   uint found_len; /* Best match to date */
  1618.   int found_offset;
  1619.   uint table_offset;
  1620.   uint size_of_bits; /* For convinience */
  1621. } REP_SET;
  1622. typedef struct st_rep_sets {
  1623.   uint count; /* Number of sets */
  1624.   uint extra; /* Extra sets in buffer */
  1625.   uint invisible; /* Sets not chown */
  1626.   uint size_of_bits;
  1627.   REP_SET *set,*set_buffer;
  1628.   uint *bit_buffer;
  1629. } REP_SETS;
  1630. typedef struct st_found_set {
  1631.   uint table_offset;
  1632.   int found_offset;
  1633. } FOUND_SET;
  1634. typedef struct st_follow {
  1635.   int chr;
  1636.   uint table_offset;
  1637.   uint len;
  1638. } FOLLOWS;
  1639. static int init_sets(REP_SETS *sets,uint states);
  1640. static REP_SET *make_new_set(REP_SETS *sets);
  1641. static void make_sets_invisible(REP_SETS *sets);
  1642. static void free_last_set(REP_SETS *sets);
  1643. static void free_sets(REP_SETS *sets);
  1644. static void internal_set_bit(REP_SET *set, uint bit);
  1645. static void internal_clear_bit(REP_SET *set, uint bit);
  1646. static void or_bits(REP_SET *to,REP_SET *from);
  1647. static void copy_bits(REP_SET *to,REP_SET *from);
  1648. static int cmp_bits(REP_SET *set1,REP_SET *set2);
  1649. static int get_next_bit(REP_SET *set,uint lastpos);
  1650. static int find_set(REP_SETS *sets,REP_SET *find);
  1651. static int find_found(FOUND_SET *found_set,uint table_offset,
  1652.   int found_offset);
  1653. static uint start_at_word(my_string pos);
  1654. static uint end_of_word(my_string pos);
  1655. static uint replace_len(my_string pos);
  1656. static uint found_sets=0;
  1657. /* Init a replace structure for further calls */
  1658. REPLACE *init_replace(my_string *from, my_string *to,uint count,
  1659.       my_string word_end_chars)
  1660. {
  1661.   uint i,j,states,set_nr,len,result_len,max_length,found_end,bits_set,bit_nr;
  1662.   int used_sets,chr,default_state;
  1663.   char used_chars[LAST_CHAR_CODE],is_word_end[256];
  1664.   my_string pos,to_pos,*to_array;
  1665.   REP_SETS sets;
  1666.   REP_SET *set,*start_states,*word_states,*new_set;
  1667.   FOLLOWS *follow,*follow_ptr;
  1668.   REPLACE *replace;
  1669.   FOUND_SET *found_set;
  1670.   REPLACE_STRING *rep_str;
  1671.   DBUG_ENTER("init_replace");
  1672.   /* Count number of states */
  1673.   for (i=result_len=max_length=0 , states=2 ; i < count ; i++)
  1674.   {
  1675.     len=replace_len(from[i]);
  1676.     if (!len)
  1677.     {
  1678.       errno=EINVAL;
  1679.       my_message(0,"No to-string for last from-string",MYF(ME_BELL));
  1680.       DBUG_RETURN(0);
  1681.     }
  1682.     states+=len+1;
  1683.     result_len+=(uint) strlen(to[i])+1;
  1684.     if (len > max_length)
  1685.       max_length=len;
  1686.   }
  1687.   bzero((char*) is_word_end,sizeof(is_word_end));
  1688.   for (i=0 ; word_end_chars[i] ; i++)
  1689.     is_word_end[(uchar) word_end_chars[i]]=1;
  1690.   if (init_sets(&sets,states))
  1691.     DBUG_RETURN(0);
  1692.   found_sets=0;
  1693.   if (!(found_set= (FOUND_SET*) my_malloc(sizeof(FOUND_SET)*max_length*count,
  1694.   MYF(MY_WME))))
  1695.   {
  1696.     free_sets(&sets);
  1697.     DBUG_RETURN(0);
  1698.   }
  1699.   VOID(make_new_set(&sets)); /* Set starting set */
  1700.   make_sets_invisible(&sets); /* Hide previus sets */
  1701.   used_sets=-1;
  1702.   word_states=make_new_set(&sets); /* Start of new word */
  1703.   start_states=make_new_set(&sets); /* This is first state */
  1704.   if (!(follow=(FOLLOWS*) my_malloc((states+2)*sizeof(FOLLOWS),MYF(MY_WME))))
  1705.   {
  1706.     free_sets(&sets);
  1707.     my_free((gptr) found_set,MYF(0));
  1708.     DBUG_RETURN(0);
  1709.   }
  1710. /* Init follow_ptr[] */
  1711.   for (i=0, states=1, follow_ptr=follow+1 ; i < count ; i++)
  1712.   {
  1713.     if (from[i][0] == '\' && from[i][1] == '^')
  1714.     {
  1715.       internal_set_bit(start_states,states+1);
  1716.       if (!from[i][2])
  1717.       {
  1718. start_states->table_offset=i;
  1719. start_states->found_offset=1;
  1720.       }
  1721.     }
  1722.     else if (from[i][0] == '\' && from[i][1] == '$')
  1723.     {
  1724.       internal_set_bit(start_states,states);
  1725.       internal_set_bit(word_states,states);
  1726.       if (!from[i][2] && start_states->table_offset == (uint) ~0)
  1727.       {
  1728. start_states->table_offset=i;
  1729. start_states->found_offset=0;
  1730.       }
  1731.     }
  1732.     else
  1733.     {
  1734.       internal_set_bit(word_states,states);
  1735.       if (from[i][0] == '\' && (from[i][1] == 'b' && from[i][2]))
  1736. internal_set_bit(start_states,states+1);
  1737.       else
  1738. internal_set_bit(start_states,states);
  1739.     }
  1740.     for (pos=from[i], len=0; *pos ; pos++)
  1741.     {
  1742.       if (*pos == '\' && *(pos+1))
  1743.       {
  1744. pos++;
  1745. switch (*pos) {
  1746. case 'b':
  1747.   follow_ptr->chr = SPACE_CHAR;
  1748.   break;
  1749. case '^':
  1750.   follow_ptr->chr = START_OF_LINE;
  1751.   break;
  1752. case '$':
  1753.   follow_ptr->chr = END_OF_LINE;
  1754.   break;
  1755. case 'r':
  1756.   follow_ptr->chr = 'r';
  1757.   break;
  1758. case 't':
  1759.   follow_ptr->chr = 't';
  1760.   break;
  1761. case 'v':
  1762.   follow_ptr->chr = 'v';
  1763.   break;
  1764. default:
  1765.   follow_ptr->chr = (uchar) *pos;
  1766.   break;
  1767. }
  1768.       }
  1769.       else
  1770. follow_ptr->chr= (uchar) *pos;
  1771.       follow_ptr->table_offset=i;
  1772.       follow_ptr->len= ++len;
  1773.       follow_ptr++;
  1774.     }
  1775.     follow_ptr->chr=0;
  1776.     follow_ptr->table_offset=i;
  1777.     follow_ptr->len=len;
  1778.     follow_ptr++;
  1779.     states+=(uint) len+1;
  1780.   }
  1781.   for (set_nr=0,pos=0 ; set_nr < sets.count ; set_nr++)
  1782.   {
  1783.     set=sets.set+set_nr;
  1784.     default_state= 0; /* Start from beginning */
  1785.     /* If end of found-string not found or start-set with current set */
  1786.     for (i= (uint) ~0; (i=get_next_bit(set,i)) ;)
  1787.     {
  1788.       if (!follow[i].chr)
  1789.       {
  1790. if (! default_state)
  1791.   default_state= find_found(found_set,set->table_offset,
  1792.     set->found_offset+1);
  1793.       }
  1794.     }
  1795.     copy_bits(sets.set+used_sets,set); /* Save set for changes */
  1796.     if (!default_state)
  1797.       or_bits(sets.set+used_sets,sets.set); /* Can restart from start */
  1798.     /* Find all chars that follows current sets */
  1799.     bzero((char*) used_chars,sizeof(used_chars));
  1800.     for (i= (uint) ~0; (i=get_next_bit(sets.set+used_sets,i)) ;)
  1801.     {
  1802.       used_chars[follow[i].chr]=1;
  1803.       if ((follow[i].chr == SPACE_CHAR && !follow[i+1].chr &&
  1804.    follow[i].len > 1) || follow[i].chr == END_OF_LINE)
  1805. used_chars[0]=1;
  1806.     }
  1807.     /* Mark word_chars used if b is in state */
  1808.     if (used_chars[SPACE_CHAR])
  1809.       for (pos= word_end_chars ; *pos ; pos++)
  1810. used_chars[(int) (uchar) *pos] = 1;
  1811.     /* Handle other used characters */
  1812.     for (chr= 0 ; chr < 256 ; chr++)
  1813.     {
  1814.       if (! used_chars[chr])
  1815. set->next[chr]= chr ? default_state : -1;
  1816.       else
  1817.       {
  1818. new_set=make_new_set(&sets);
  1819. set=sets.set+set_nr; /* if realloc */
  1820. new_set->table_offset=set->table_offset;
  1821. new_set->found_len=set->found_len;
  1822. new_set->found_offset=set->found_offset+1;
  1823. found_end=0;
  1824. for (i= (uint) ~0 ; (i=get_next_bit(sets.set+used_sets,i)) ; )
  1825. {
  1826.   if (!follow[i].chr || follow[i].chr == chr ||
  1827.       (follow[i].chr == SPACE_CHAR &&
  1828.        (is_word_end[chr] ||
  1829. (!chr && follow[i].len > 1 && ! follow[i+1].chr))) ||
  1830.       (follow[i].chr == END_OF_LINE && ! chr))
  1831.   {
  1832.     if ((! chr || (follow[i].chr && !follow[i+1].chr)) &&
  1833. follow[i].len > found_end)
  1834.       found_end=follow[i].len;
  1835.     if (chr && follow[i].chr)
  1836.       internal_set_bit(new_set,i+1); /* To next set */
  1837.     else
  1838.       internal_set_bit(new_set,i);
  1839.   }
  1840. }
  1841. if (found_end)
  1842. {
  1843.   new_set->found_len=0; /* Set for testing if first */
  1844.   bits_set=0;
  1845.   for (i= (uint) ~0; (i=get_next_bit(new_set,i)) ;)
  1846.   {
  1847.     if ((follow[i].chr == SPACE_CHAR ||
  1848.  follow[i].chr == END_OF_LINE) && ! chr)
  1849.       bit_nr=i+1;
  1850.     else
  1851.       bit_nr=i;
  1852.     if (follow[bit_nr-1].len < found_end ||
  1853. (new_set->found_len &&
  1854.  (chr == 0 || !follow[bit_nr].chr)))
  1855.       internal_clear_bit(new_set,i);
  1856.     else
  1857.     {
  1858.       if (chr == 0 || !follow[bit_nr].chr)
  1859.       { /* best match  */
  1860. new_set->table_offset=follow[bit_nr].table_offset;
  1861. if (chr || (follow[i].chr == SPACE_CHAR ||
  1862.     follow[i].chr == END_OF_LINE))
  1863.   new_set->found_offset=found_end; /* New match */
  1864. new_set->found_len=found_end;
  1865.       }
  1866.       bits_set++;
  1867.     }
  1868.   }
  1869.   if (bits_set == 1)
  1870.   {
  1871.     set->next[chr] = find_found(found_set,
  1872. new_set->table_offset,
  1873. new_set->found_offset);
  1874.     free_last_set(&sets);
  1875.   }
  1876.   else
  1877.     set->next[chr] = find_set(&sets,new_set);
  1878. }
  1879. else
  1880.   set->next[chr] = find_set(&sets,new_set);
  1881.       }
  1882.     }
  1883.   }
  1884. /* Alloc replace structure for the replace-state-machine */
  1885.   if ((replace=(REPLACE*) my_malloc(sizeof(REPLACE)*(sets.count)+
  1886.     sizeof(REPLACE_STRING)*(found_sets+1)+
  1887.     sizeof(my_string)*count+result_len,
  1888.     MYF(MY_WME | MY_ZEROFILL))))
  1889.   {
  1890.     rep_str=(REPLACE_STRING*) (replace+sets.count);
  1891.     to_array=(my_string*) (rep_str+found_sets+1);
  1892.     to_pos=(my_string) (to_array+count);
  1893.     for (i=0 ; i < count ; i++)
  1894.     {
  1895.       to_array[i]=to_pos;
  1896.       to_pos=strmov(to_pos,to[i])+1;
  1897.     }
  1898.     rep_str[0].found=1;
  1899.     rep_str[0].replace_string=0;
  1900.     for (i=1 ; i <= found_sets ; i++)
  1901.     {
  1902.       pos=from[found_set[i-1].table_offset];
  1903.       rep_str[i].found= !bcmp(pos,"\^",3) ? 2 : 1;
  1904.       rep_str[i].replace_string=to_array[found_set[i-1].table_offset];
  1905.       rep_str[i].to_offset=found_set[i-1].found_offset-start_at_word(pos);
  1906.       rep_str[i].from_offset=found_set[i-1].found_offset-replace_len(pos)+
  1907. end_of_word(pos);
  1908.     }
  1909.     for (i=0 ; i < sets.count ; i++)
  1910.     {
  1911.       for (j=0 ; j < 256 ; j++)
  1912. if (sets.set[i].next[j] >= 0)
  1913.   replace[i].next[j]=replace+sets.set[i].next[j];
  1914. else
  1915.   replace[i].next[j]=(REPLACE*) (rep_str+(-sets.set[i].next[j]-1));
  1916.     }
  1917.   }
  1918.   my_free((gptr) follow,MYF(0));
  1919.   free_sets(&sets);
  1920.   my_free((gptr) found_set,MYF(0));
  1921.   DBUG_PRINT("exit",("Replace table has %d states",sets.count));
  1922.   DBUG_RETURN(replace);
  1923. }
  1924. static int init_sets(REP_SETS *sets,uint states)
  1925. {
  1926.   bzero((char*) sets,sizeof(*sets));
  1927.   sets->size_of_bits=((states+7)/8);
  1928.   if (!(sets->set_buffer=(REP_SET*) my_malloc(sizeof(REP_SET)*SET_MALLOC_HUNC,
  1929.       MYF(MY_WME))))
  1930.     return 1;
  1931.   if (!(sets->bit_buffer=(uint*) my_malloc(sizeof(uint)*sets->size_of_bits*
  1932.    SET_MALLOC_HUNC,MYF(MY_WME))))
  1933.   {
  1934.     my_free((gptr) sets->set,MYF(0));
  1935.     return 1;
  1936.   }
  1937.   return 0;
  1938. }
  1939. /* Make help sets invisible for nicer codeing */
  1940. static void make_sets_invisible(REP_SETS *sets)
  1941. {
  1942.   sets->invisible=sets->count;
  1943.   sets->set+=sets->count;
  1944.   sets->count=0;
  1945. }
  1946. static REP_SET *make_new_set(REP_SETS *sets)
  1947. {
  1948.   uint i,count,*bit_buffer;
  1949.   REP_SET *set;
  1950.   if (sets->extra)
  1951.   {
  1952.     sets->extra--;
  1953.     set=sets->set+ sets->count++;
  1954.     bzero((char*) set->bits,sizeof(uint)*sets->size_of_bits);
  1955.     bzero((char*) &set->next[0],sizeof(set->next[0])*LAST_CHAR_CODE);
  1956.     set->found_offset=0;
  1957.     set->found_len=0;
  1958.     set->table_offset= (uint) ~0;
  1959.     set->size_of_bits=sets->size_of_bits;
  1960.     return set;
  1961.   }
  1962.   count=sets->count+sets->invisible+SET_MALLOC_HUNC;
  1963.   if (!(set=(REP_SET*) my_realloc((gptr) sets->set_buffer,
  1964.    sizeof(REP_SET)*count,
  1965.   MYF(MY_WME))))
  1966.     return 0;
  1967.   sets->set_buffer=set;
  1968.   sets->set=set+sets->invisible;
  1969.   if (!(bit_buffer=(uint*) my_realloc((gptr) sets->bit_buffer,
  1970.       (sizeof(uint)*sets->size_of_bits)*count,
  1971.       MYF(MY_WME))))
  1972.     return 0;
  1973.   sets->bit_buffer=bit_buffer;
  1974.   for (i=0 ; i < count ; i++)
  1975.   {
  1976.     sets->set_buffer[i].bits=bit_buffer;
  1977.     bit_buffer+=sets->size_of_bits;
  1978.   }
  1979.   sets->extra=SET_MALLOC_HUNC;
  1980.   return make_new_set(sets);
  1981. }
  1982. static void free_last_set(REP_SETS *sets)
  1983. {
  1984.   sets->count--;
  1985.   sets->extra++;
  1986.   return;
  1987. }
  1988. static void free_sets(REP_SETS *sets)
  1989. {
  1990.   my_free((gptr)sets->set_buffer,MYF(0));
  1991.   my_free((gptr)sets->bit_buffer,MYF(0));
  1992.   return;
  1993. }
  1994. static void internal_set_bit(REP_SET *set, uint bit)
  1995. {
  1996.   set->bits[bit / WORD_BIT] |= 1 << (bit % WORD_BIT);
  1997.   return;
  1998. }
  1999. static void internal_clear_bit(REP_SET *set, uint bit)
  2000. {
  2001.   set->bits[bit / WORD_BIT] &= ~ (1 << (bit % WORD_BIT));
  2002.   return;
  2003. }
  2004. static void or_bits(REP_SET *to,REP_SET *from)
  2005. {
  2006.   reg1 uint i;
  2007.   for (i=0 ; i < to->size_of_bits ; i++)
  2008.     to->bits[i]|=from->bits[i];
  2009.   return;
  2010. }
  2011. static void copy_bits(REP_SET *to,REP_SET *from)
  2012. {
  2013.   memcpy((byte*) to->bits,(byte*) from->bits,
  2014.  (size_t) (sizeof(uint) * to->size_of_bits));
  2015. }
  2016. static int cmp_bits(REP_SET *set1,REP_SET *set2)
  2017. {
  2018.   return bcmp((byte*) set1->bits,(byte*) set2->bits,
  2019.       sizeof(uint) * set1->size_of_bits);
  2020. }
  2021. /* Get next set bit from set. */
  2022. static int get_next_bit(REP_SET *set,uint lastpos)
  2023. {
  2024.   uint pos,*start,*end,bits;
  2025.   start=set->bits+ ((lastpos+1) / WORD_BIT);
  2026.   end=set->bits + set->size_of_bits;
  2027.   bits=start[0] & ~((1 << ((lastpos+1) % WORD_BIT)) -1);
  2028.   while (! bits && ++start < end)
  2029.     bits=start[0];
  2030.   if (!bits)
  2031.     return 0;
  2032.   pos=(uint) (start-set->bits)*WORD_BIT;
  2033.   while (! (bits & 1))
  2034.   {
  2035.     bits>>=1;
  2036.     pos++;
  2037.   }
  2038.   return pos;
  2039. }
  2040. /* find if there is a same set in sets. If there is, use it and
  2041.    free given set, else put in given set in sets and return it's
  2042.    position */
  2043. static int find_set(REP_SETS *sets,REP_SET *find)
  2044. {
  2045.   uint i;
  2046.   for (i=0 ; i < sets->count-1 ; i++)
  2047.   {
  2048.     if (!cmp_bits(sets->set+i,find))
  2049.     {
  2050.       free_last_set(sets);
  2051.       return i;
  2052.     }
  2053.   }
  2054.   return i; /* return new postion */
  2055. }
  2056. /* find if there is a found_set with same table_offset & found_offset
  2057.    If there is return offset to it, else add new offset and return pos.
  2058.    Pos returned is -offset-2 in found_set_structure because it's is
  2059.    saved in set->next and set->next[] >= 0 points to next set and
  2060.    set->next[] == -1 is reserved for end without replaces.
  2061.    */
  2062. static int find_found(FOUND_SET *found_set,uint table_offset, int found_offset)
  2063. {
  2064.   int i;
  2065.   for (i=0 ; (uint) i < found_sets ; i++)
  2066.     if (found_set[i].table_offset == table_offset &&
  2067. found_set[i].found_offset == found_offset)
  2068.       return -i-2;
  2069.   found_set[i].table_offset=table_offset;
  2070.   found_set[i].found_offset=found_offset;
  2071.   found_sets++;
  2072.   return -i-2; /* return new postion */
  2073. }
  2074. /* Return 1 if regexp starts with b or ends with b*/
  2075. static uint start_at_word(my_string pos)
  2076. {
  2077.   return (((!bcmp(pos,"\b",2) && pos[2]) || !bcmp(pos,"\^",2)) ? 1 : 0);
  2078. }
  2079. static uint end_of_word(my_string pos)
  2080. {
  2081.   my_string end=strend(pos);
  2082.   return ((end > pos+2 && !bcmp(end-2,"\b",2)) ||
  2083.   (end >= pos+2 && !bcmp(end-2,"\$",2))) ?
  2084.     1 : 0;
  2085. }
  2086. static uint replace_len(my_string str)
  2087. {
  2088.   uint len=0;
  2089.   while (*str)
  2090.   {
  2091.     if (str[0] == '\' && str[1])
  2092.       str++;
  2093.     str++;
  2094.     len++;
  2095.   }
  2096.   return len;
  2097. }
  2098. /* Replace strings;  Return length of result string */
  2099. uint replace_strings(REPLACE *rep, my_string *start,uint *max_length,
  2100.      const char *from)
  2101. {
  2102.   reg1 REPLACE *rep_pos;
  2103.   reg2 REPLACE_STRING *rep_str;
  2104.   my_string to,end,pos,new_str;
  2105.   end=(to= *start) + *max_length-1;
  2106.   rep_pos=rep+1;
  2107.   for (;;)
  2108.   {
  2109.     while (!rep_pos->found)
  2110.     {
  2111.       rep_pos= rep_pos->next[(uchar) *from];
  2112.       if (to == end)
  2113.       {
  2114. (*max_length)+=8192;
  2115. if (!(new_str=my_realloc(*start,*max_length,MYF(MY_WME))))
  2116.   return (uint) -1;
  2117. to=new_str+(to - *start);
  2118. end=(*start=new_str)+ *max_length-1;
  2119.       }
  2120.       *to++= *from++;
  2121.     }
  2122.     if (!(rep_str = ((REPLACE_STRING*) rep_pos))->replace_string)
  2123.       return (uint) (to - *start)-1;
  2124.     to-=rep_str->to_offset;
  2125.     for (pos=rep_str->replace_string; *pos ; pos++)
  2126.     {
  2127.       if (to == end)
  2128.       {
  2129. (*max_length)*=2;
  2130. if (!(new_str=my_realloc(*start,*max_length,MYF(MY_WME))))
  2131.   return (uint) -1;
  2132. to=new_str+(to - *start);
  2133. end=(*start=new_str)+ *max_length-1;
  2134.       }
  2135.       *to++= *pos;
  2136.     }
  2137.     if (!*(from-=rep_str->from_offset) && rep_pos->found != 2)
  2138.       return (uint) (to - *start);
  2139.     rep_pos=rep;
  2140.   }
  2141. }
  2142. static int initialize_replace_buffer(void)
  2143. {
  2144.   out_length=8192;
  2145.   if (!(out_buff=my_malloc(out_length,MYF(MY_WME))))
  2146.     return(1);
  2147.   return 0;
  2148. }
  2149. static void free_replace_buffer(void)
  2150. {
  2151.   my_free(out_buff,MYF(MY_WME));
  2152. }
  2153. /****************************************************************************
  2154.  Replace results for a column
  2155. *****************************************************************************/
  2156. static void free_replace_column()
  2157. {
  2158.   uint i;
  2159.   for (i=0 ; i < max_replace_column ; i++)
  2160.   {
  2161.     if (replace_column[i])
  2162.     {
  2163.       my_free(replace_column[i], 0);
  2164.       replace_column[i]= 0;
  2165.     }
  2166.   }
  2167.   max_replace_column= 0;
  2168. }
  2169. /*
  2170.   Get arguments for replace_columns. The syntax is:
  2171.   replace-column column_number to_string [column_number to_string ...]
  2172.   Where each argument may be quoted with ' or "
  2173.   A argument may also be a variable, in which case the value of the
  2174.   variable is replaced.
  2175. */
  2176. static void get_replace_column(struct st_query *q)
  2177. {
  2178.   char *from=q->first_argument;
  2179.   char *buff,*start;
  2180.   DBUG_ENTER("get_replace_columns");
  2181.   free_replace_column();
  2182.   if (!*from)
  2183.     die("Missing argument in %s", q->query);
  2184.   /* Allocate a buffer for results */
  2185.   start=buff=my_malloc(strlen(from)+1,MYF(MY_WME | MY_FAE));
  2186.   while (*from)
  2187.   {
  2188.     char *to;
  2189.     uint column_number;
  2190.     to= get_string(&buff, &from, q);
  2191.     if (!(column_number= atoi(to)) || column_number > MAX_COLUMNS)
  2192.       die("Wrong column number to replace_column in '%s'", q->query);
  2193.     if (!*from)
  2194.       die("Wrong number of arguments to replace_column in '%s'", q->query);
  2195.     to= get_string(&buff, &from, q);
  2196.     my_free(replace_column[column_number-1], MY_ALLOW_ZERO_PTR);
  2197.     replace_column[column_number-1]= my_strdup(to, MYF(MY_WME | MY_FAE));
  2198.     set_if_bigger(max_replace_column, column_number);
  2199.   }
  2200.   my_free(start, MYF(0));
  2201.   q->last_argument= q->end;
  2202. }
  2203. #if defined(__NETWARE__) || defined(__WIN__)
  2204. /*
  2205.   Substitute environment variables with text.
  2206.   SYNOPSIS
  2207.     subst_env_var()
  2208.     arg String that should be substitute
  2209.   DESCRIPTION
  2210.     This function takes a string as an input and replaces the
  2211.     environment variables, that starts with '$' character, with it value.
  2212.   NOTES
  2213.     Return string must be freed with my_free()
  2214.   RETURN
  2215.     String with environment variables replaced.
  2216. */
  2217. static char *subst_env_var(const char *str)
  2218. {
  2219.   char *result;
  2220.   char *pos;
  2221.   result= pos= my_malloc(MAX_QUERY, MYF(MY_FAE));
  2222.   while (*str)
  2223.   {
  2224.     /*
  2225.       need this only when we want to provide the functionality of
  2226.       escaping through  'backslash'
  2227.       if ((result == pos && *str=='$') ||
  2228.           (result != pos && *str=='$' && str[-1] !='\'))
  2229.     */
  2230.     if (*str == '$')
  2231.     {
  2232.       char env_var[256], *env_pos= env_var, *subst;
  2233.       /* Search for end of environment variable */
  2234.       for (str++;
  2235.            *str && !isspace(*str) && *str != '\' && *str != '/' &&
  2236.              *str != '$';
  2237.            str++)
  2238.         *env_pos++= *str;
  2239.       *env_pos= 0;
  2240.       if (!(subst= getenv(env_var)))
  2241.       {
  2242.         my_free(result, MYF(0));
  2243.         die("MYSQLTEST.NLM: Environment variable %s is not defined",
  2244.             env_var);
  2245.       }
  2246.       /* get the string to be substitued for env_var  */
  2247.       pos= strmov(pos, subst);
  2248.       /* Process delimiter in *str again */
  2249.     }
  2250.     else
  2251.       *pos++= *str++;
  2252.   }
  2253.   *pos= 0;
  2254.   return result;
  2255. }
  2256. /*
  2257.   popen replacement for Netware
  2258.   SYNPOSIS
  2259.     my_popen()
  2260.     name Command to execute (with possible env variables)
  2261.     mode Mode for popen.
  2262.   NOTES
  2263.     Environment variable expansion does not take place for popen function
  2264.     on NetWare, so we use this function to wrap around popen to do this.
  2265.     For the moment we ignore 'mode' and always use 'r0'
  2266.   RETURN
  2267.     # >= 0 File handle
  2268.     -1 Error
  2269. */
  2270. #undef popen                                    /* Remove wrapper */
  2271. #ifdef __WIN__ 
  2272. #define popen _popen                           /* redefine for windows */
  2273. #endif
  2274. FILE *my_popen(const char *cmd, const char *mode __attribute__((unused)))
  2275. {
  2276.   char *subst_cmd;
  2277.   FILE *res_file;
  2278.   subst_cmd= subst_env_var(cmd);
  2279.   res_file= popen(subst_cmd, "r0");
  2280.   my_free(subst_cmd, MYF(0));
  2281.   return res_file;
  2282. }
  2283. #endif /* __NETWARE__ or  __WIN__*/