2001-12-06 13:10:51 +01:00
|
|
|
/* Copyright (C) 2000 MySQL AB
|
2000-12-22 20:10:35 +01:00
|
|
|
|
2000-10-10 10:42:07 +02:00
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
|
|
(at your option) any later version.
|
2000-12-22 20:10:35 +01:00
|
|
|
|
2000-10-10 10:42:07 +02:00
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
2000-12-22 20:10:35 +01:00
|
|
|
|
2000-10-10 10:42:07 +02:00
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|
|
|
|
|
|
|
/* mysqltest test tool
|
2002-01-25 06:49:47 +01:00
|
|
|
* See the manual for more information
|
|
|
|
* TODO: document better how mysqltest works
|
2000-10-10 10:42:07 +02:00
|
|
|
*
|
|
|
|
* Written by:
|
|
|
|
* Sasha Pachev <sasha@mysql.com>
|
|
|
|
* Matt Wagner <matt@mysql.com>
|
2000-12-28 02:56:38 +01:00
|
|
|
* Monty
|
2002-05-21 23:05:05 +02:00
|
|
|
* Jani
|
2000-10-10 10:42:07 +02:00
|
|
|
**/
|
|
|
|
|
2001-01-07 23:04:30 +01:00
|
|
|
/**********************************************************************
|
|
|
|
TODO:
|
|
|
|
|
|
|
|
- Do comparison line by line, instead of doing a full comparison of
|
|
|
|
the text file. This will save space as we don't need to keep many
|
|
|
|
results in memory. It will also make it possible to do simple
|
|
|
|
'comparison' fixes like accepting the result even if a float differed
|
|
|
|
in the last decimals.
|
|
|
|
|
|
|
|
- Don't buffer lines from the test that you don't expect to need
|
|
|
|
again.
|
|
|
|
|
|
|
|
- Change 'read_line' to be faster by using the readline.cc code;
|
|
|
|
We can do better than calling feof() for each character!
|
|
|
|
|
|
|
|
**********************************************************************/
|
|
|
|
|
2003-12-16 05:09:44 +01:00
|
|
|
#define MTEST_VERSION "2.1"
|
2001-01-07 23:04:30 +01:00
|
|
|
|
2001-09-14 01:54:33 +02:00
|
|
|
#include <my_global.h>
|
2001-09-22 16:40:59 +02:00
|
|
|
#include <mysql_embed.h>
|
2001-01-07 23:04:30 +01:00
|
|
|
#include <my_sys.h>
|
|
|
|
#include <m_string.h>
|
|
|
|
#include <mysql.h>
|
|
|
|
#include <mysql_version.h>
|
2001-09-22 16:40:59 +02:00
|
|
|
#include <mysqld_error.h>
|
2001-01-07 23:04:30 +01:00
|
|
|
#include <m_ctype.h>
|
|
|
|
#include <my_dir.h>
|
2001-04-18 07:06:00 +02:00
|
|
|
#include <hash.h>
|
2002-05-21 23:05:05 +02:00
|
|
|
#include <my_getopt.h>
|
2000-10-10 10:42:07 +02:00
|
|
|
#include <stdarg.h>
|
|
|
|
#include <sys/stat.h>
|
2001-01-20 06:40:16 +01:00
|
|
|
#include <violite.h>
|
2000-10-10 10:42:07 +02:00
|
|
|
|
2004-06-11 15:25:18 +02:00
|
|
|
#define MAX_QUERY 131072
|
2004-06-21 09:21:20 +02:00
|
|
|
#define MAX_VAR_NAME 256
|
2003-06-27 15:29:10 +02:00
|
|
|
#define MAX_COLUMNS 256
|
2000-12-27 02:31:06 +01:00
|
|
|
#define PAD_SIZE 128
|
2003-06-27 15:29:10 +02:00
|
|
|
#define MAX_CONS 128
|
2000-10-10 10:42:07 +02:00
|
|
|
#define MAX_INCLUDE_DEPTH 16
|
2000-11-22 00:41:29 +01:00
|
|
|
#define LAZY_GUESS_BUF_SIZE 8192
|
2000-12-27 02:31:06 +01:00
|
|
|
#define INIT_Q_LINES 1024
|
|
|
|
#define MIN_VAR_ALLOC 32
|
2000-12-02 18:11:50 +01:00
|
|
|
#define BLOCK_STACK_DEPTH 32
|
2001-01-16 05:02:05 +01:00
|
|
|
#define MAX_EXPECTED_ERRORS 10
|
2001-01-20 06:40:16 +01:00
|
|
|
#define QUERY_SEND 1
|
|
|
|
#define QUERY_REAP 2
|
2001-10-02 00:13:55 +02:00
|
|
|
#ifndef MYSQL_MANAGER_PORT
|
|
|
|
#define MYSQL_MANAGER_PORT 23546
|
|
|
|
#endif
|
2003-09-29 11:31:35 +02:00
|
|
|
#define MAX_SERVER_ARGS 20
|
2001-10-02 00:13:55 +02:00
|
|
|
|
2001-12-10 17:16:51 +01:00
|
|
|
/*
|
|
|
|
Sometimes in a test the client starts before
|
|
|
|
the server - to solve the problem, we try again
|
|
|
|
after some sleep if connection fails the first
|
|
|
|
time
|
|
|
|
*/
|
|
|
|
#define CON_RETRY_SLEEP 2
|
2002-08-08 02:12:02 +02:00
|
|
|
#define MAX_CON_TRIES 5
|
2001-12-10 17:16:51 +01:00
|
|
|
|
2002-01-25 06:49:47 +01:00
|
|
|
#define SLAVE_POLL_INTERVAL 300000 /* 0.3 of a sec */
|
2003-12-16 05:09:44 +01:00
|
|
|
#define DEFAULT_DELIMITER ";"
|
|
|
|
#define MAX_DELIMITER 16
|
2002-01-25 06:49:47 +01:00
|
|
|
|
2001-10-02 00:13:55 +02:00
|
|
|
enum {OPT_MANAGER_USER=256,OPT_MANAGER_HOST,OPT_MANAGER_PASSWD,
|
2003-03-11 10:41:53 +01:00
|
|
|
OPT_MANAGER_PORT,OPT_MANAGER_WAIT_TIMEOUT, OPT_SKIP_SAFEMALLOC,
|
|
|
|
OPT_SSL_SSL, OPT_SSL_KEY, OPT_SSL_CERT, OPT_SSL_CA, OPT_SSL_CAPATH,
|
|
|
|
OPT_SSL_CIPHER};
|
2000-10-10 10:42:07 +02:00
|
|
|
|
2002-05-21 23:05:05 +02:00
|
|
|
static int record = 0, opt_sleep=0;
|
2000-10-10 10:42:07 +02:00
|
|
|
static char *db = 0, *pass=0;
|
2001-10-08 03:58:07 +02:00
|
|
|
const char* user = 0, *host = 0, *unix_sock = 0, *opt_basedir="./";
|
2002-05-21 23:05:05 +02:00
|
|
|
static int port = 0;
|
|
|
|
static my_bool opt_big_test= 0, opt_compress= 0, silent= 0, verbose = 0,
|
2002-08-08 02:12:02 +02:00
|
|
|
tty_password= 0;
|
2000-12-28 02:56:38 +01:00
|
|
|
static uint start_lineno, *lineno;
|
2001-10-10 23:36:35 +02:00
|
|
|
const char* manager_user="root",*manager_host=0;
|
2001-10-02 00:13:55 +02:00
|
|
|
char *manager_pass=0;
|
|
|
|
int manager_port=MYSQL_MANAGER_PORT;
|
|
|
|
int manager_wait_timeout=3;
|
|
|
|
MYSQL_MANAGER* manager=0;
|
2000-12-28 02:56:38 +01:00
|
|
|
|
2001-01-07 23:04:30 +01:00
|
|
|
static char **default_argv;
|
2000-10-10 10:42:07 +02:00
|
|
|
static const char *load_default_groups[]= { "mysqltest","client",0 };
|
2004-05-19 04:09:10 +02:00
|
|
|
static char line_buffer[MAX_DELIMITER], *line_buffer_pos= line_buffer;
|
2000-10-10 10:42:07 +02:00
|
|
|
|
2001-01-15 21:56:44 +01:00
|
|
|
static FILE* file_stack[MAX_INCLUDE_DEPTH];
|
|
|
|
static FILE** cur_file;
|
|
|
|
static FILE** file_stack_end;
|
|
|
|
static uint lineno_stack[MAX_INCLUDE_DEPTH];
|
|
|
|
static char TMPDIR[FN_REFLEN];
|
2003-12-16 05:09:44 +01:00
|
|
|
static char delimiter[MAX_DELIMITER]= DEFAULT_DELIMITER;
|
|
|
|
static uint delimiter_length= 1;
|
2000-10-10 10:42:07 +02:00
|
|
|
|
2001-01-15 21:56:44 +01:00
|
|
|
static int *cur_block, *block_stack_end;
|
2001-06-12 01:28:26 +02:00
|
|
|
static int block_stack[BLOCK_STACK_DEPTH];
|
|
|
|
|
|
|
|
static int block_ok_stack[BLOCK_STACK_DEPTH];
|
2001-11-04 14:43:41 +01:00
|
|
|
static uint global_expected_errno[MAX_EXPECTED_ERRORS], global_expected_errors;
|
2003-10-08 11:01:58 +02:00
|
|
|
static CHARSET_INFO *charset_info= &my_charset_latin1;
|
2000-12-02 18:11:50 +01:00
|
|
|
|
2003-09-29 11:31:35 +02:00
|
|
|
static int embedded_server_arg_count=0;
|
|
|
|
static char *embedded_server_args[MAX_SERVER_ARGS];
|
|
|
|
|
2004-03-30 18:24:28 +02:00
|
|
|
static my_bool display_result_vertically= FALSE, display_metadata= FALSE;
|
2004-03-04 18:08:14 +01:00
|
|
|
|
2003-09-29 11:31:35 +02:00
|
|
|
static const char *embedded_server_groups[] = {
|
|
|
|
"server",
|
|
|
|
"embedded",
|
|
|
|
"mysqltest_SERVER",
|
|
|
|
NullS
|
|
|
|
};
|
|
|
|
|
2000-12-02 18:11:50 +01:00
|
|
|
DYNAMIC_ARRAY q_lines;
|
|
|
|
|
2003-03-16 18:17:54 +01:00
|
|
|
#include "sslopt-vars.h"
|
|
|
|
|
2002-08-08 02:12:02 +02:00
|
|
|
typedef struct
|
2001-01-17 13:47:33 +01:00
|
|
|
{
|
|
|
|
char file[FN_REFLEN];
|
|
|
|
ulong pos;
|
|
|
|
} MASTER_POS ;
|
|
|
|
|
2000-10-10 10:42:07 +02:00
|
|
|
struct connection
|
|
|
|
{
|
|
|
|
MYSQL mysql;
|
|
|
|
char *name;
|
|
|
|
};
|
|
|
|
|
2001-02-13 11:39:50 +01:00
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
int read_lines,current_line;
|
|
|
|
} PARSER;
|
2000-12-02 18:11:50 +01:00
|
|
|
|
2001-10-08 03:58:07 +02:00
|
|
|
MYSQL_RES *last_result=0;
|
|
|
|
|
2000-12-02 18:11:50 +01:00
|
|
|
PARSER parser;
|
2001-01-17 13:47:33 +01:00
|
|
|
MASTER_POS master_pos;
|
2001-12-09 23:08:24 +01:00
|
|
|
int *block_ok; /* set to 0 if the current block should not be executed */
|
2000-12-02 18:11:50 +01:00
|
|
|
int false_block_depth = 0;
|
2001-12-09 23:08:24 +01:00
|
|
|
/* if set, all results are concated and compared against this file */
|
|
|
|
const char *result_file = 0;
|
2000-12-02 18:11:50 +01:00
|
|
|
|
2000-12-22 20:10:35 +01:00
|
|
|
typedef struct
|
2000-12-02 18:11:50 +01:00
|
|
|
{
|
2001-12-09 23:08:24 +01:00
|
|
|
char *name;
|
2001-04-18 07:06:00 +02:00
|
|
|
int name_len;
|
2001-12-09 23:08:24 +01:00
|
|
|
char *str_val;
|
2000-12-02 18:11:50 +01:00
|
|
|
int str_val_len;
|
|
|
|
int int_val;
|
|
|
|
int alloced_len;
|
|
|
|
int int_dirty; /* do not update string if int is updated until first read */
|
2001-09-28 07:05:54 +02:00
|
|
|
int alloced;
|
2000-12-02 18:11:50 +01:00
|
|
|
} VAR;
|
|
|
|
|
2004-05-25 21:00:14 +02:00
|
|
|
#ifdef __NETWARE__
|
|
|
|
/*
|
|
|
|
Netware doesn't proved environment variable substitution that is done
|
|
|
|
by the shell in unix environments. We do this in the following function:
|
|
|
|
*/
|
|
|
|
|
|
|
|
static char *subst_env_var(const char *cmd);
|
2004-06-01 22:35:09 +02:00
|
|
|
static FILE *my_popen(const char *cmd, const char *mode);
|
2004-05-25 21:00:14 +02:00
|
|
|
#define popen(A,B) my_popen((A),(B))
|
|
|
|
#endif /* __NETWARE__ */
|
|
|
|
|
2000-12-02 18:11:50 +01:00
|
|
|
VAR var_reg[10];
|
|
|
|
/*Perl/shell-like variable registers */
|
2001-04-18 07:06:00 +02:00
|
|
|
HASH var_hash;
|
2003-01-06 00:48:59 +01:00
|
|
|
my_bool disable_query_log=0, disable_result_log=0, disable_warnings=0;
|
|
|
|
my_bool disable_info= 1; /* By default off */
|
2000-12-02 18:11:50 +01:00
|
|
|
|
2000-10-10 10:42:07 +02:00
|
|
|
struct connection cons[MAX_CONS];
|
|
|
|
struct connection* cur_con, *next_con, *cons_end;
|
|
|
|
|
2001-04-18 03:23:14 +02:00
|
|
|
/* Add new commands before Q_UNKNOWN !*/
|
|
|
|
|
|
|
|
enum enum_commands {
|
2002-08-08 02:12:02 +02:00
|
|
|
Q_CONNECTION=1, Q_QUERY,
|
2002-08-18 15:04:26 +02:00
|
|
|
Q_CONNECT, Q_SLEEP, Q_REAL_SLEEP,
|
2002-08-08 02:12:02 +02:00
|
|
|
Q_INC, Q_DEC,
|
|
|
|
Q_SOURCE, Q_DISCONNECT,
|
|
|
|
Q_LET, Q_ECHO,
|
|
|
|
Q_WHILE, Q_END_BLOCK,
|
|
|
|
Q_SYSTEM, Q_RESULT,
|
|
|
|
Q_REQUIRE, Q_SAVE_MASTER_POS,
|
2002-08-29 20:08:54 +02:00
|
|
|
Q_SYNC_WITH_MASTER,
|
|
|
|
Q_SYNC_SLAVE_WITH_MASTER,
|
|
|
|
Q_ERROR,
|
2002-08-08 02:12:02 +02:00
|
|
|
Q_SEND, Q_REAP,
|
2003-06-27 15:29:10 +02:00
|
|
|
Q_DIRTY_CLOSE, Q_REPLACE, Q_REPLACE_COLUMN,
|
2002-08-08 02:12:02 +02:00
|
|
|
Q_PING, Q_EVAL,
|
|
|
|
Q_RPL_PROBE, Q_ENABLE_RPL_PARSE,
|
2001-06-30 21:08:35 +02:00
|
|
|
Q_DISABLE_RPL_PARSE, Q_EVAL_RESULT,
|
2001-09-28 07:05:54 +02:00
|
|
|
Q_ENABLE_QUERY_LOG, Q_DISABLE_QUERY_LOG,
|
2001-12-09 23:08:24 +01:00
|
|
|
Q_ENABLE_RESULT_LOG, Q_DISABLE_RESULT_LOG,
|
2001-10-11 21:54:06 +02:00
|
|
|
Q_SERVER_START, Q_SERVER_STOP,Q_REQUIRE_MANAGER,
|
2002-01-25 06:49:47 +01:00
|
|
|
Q_WAIT_FOR_SLAVE_TO_STOP,
|
Robustness feature.
Won't be pushed as is - separate email sent for internal review.
WL#1717 "binlog-innodb consistency".
Now when mysqld starts, if InnoDB does a crash recovery, we use the binlog name
and position retrieved from InnoDB (corresponding to the last transaction
successfully committed by InnoDB) to cut any rolled back transaction from
the binary log. This is triggered by the --innodb-safe-binlog option.
Provided you configure mysqld to fsync() InnoDB at every commit (using
flush_log_at_trx_commit) and to fsync() the binlog at every write
(using --sync-binlog=1), this behaviour guarantees that a master always has
consistency between binlog and InnoDB, whenever the crash happens.
6 tests to verify that it works.
client/mysqltest.c:
New command require_os (only "unix" accepted for now).
innobase/include/trx0sys.h:
when InnoDB does crash recovery, we now save the binlog coords it prints, into variables for later use.
innobase/trx/trx0sys.c:
when InnoDB does crash recovery, we now save the binlog coords it prints, into variables for later use.
mysql-test/mysql-test-run.sh:
The tests which check that the binlog is cut at restart, need to not delete those binlogs, of course.
And not delete replication info, so that we can test that the slave does not receive anything
wrong from the cut binlog.
sql/ha_innodb.cc:
methods to read from InnoDB the binlog coords stored into it
sql/ha_innodb.h:
ethods to read from InnoDB the binlog coords stored into it
sql/log.cc:
Added my_sync() when we create a binlog (my_sync of the binlog and of the index file);
this is always done, whether --sync-binlog or not (binlog creation is rare, so no speed
problem, and I like to have the existence of the binlog always reliably recorded, even if
later content is not).
If --crash-binlog-innodb, crash between the binlog write and the InnoDB commit.
New methods:
- report_pos_in_innodb() to store the binlog name and position into InnoDB (used only when
we create a new binlog: at startup and at FLUSH LOGS)
- cut_spurious_tail() to possibly cut the tail of a binlog based on the info we read
from InnoDB (does something only if InnoDB has just done a crash recovery).
sql/mysql_priv.h:
new option, to crash (use for testing only)
sql/mysqld.cc:
New option --innodb-safe-binlog and --crash-binlog-innodb (the latter is for testing, it makes mysqld crash).
Just after opening the logs and opening the storage engines, cut any wrong statement from the binlog, based
on info read from InnoDB.
sql/sql_class.h:
new methods for MYSQL_LOG.
2004-06-20 19:11:02 +02:00
|
|
|
Q_REQUIRE_VERSION, Q_REQUIRE_OS,
|
2003-01-06 00:48:59 +01:00
|
|
|
Q_ENABLE_WARNINGS, Q_DISABLE_WARNINGS,
|
|
|
|
Q_ENABLE_INFO, Q_DISABLE_INFO,
|
2004-03-30 18:24:28 +02:00
|
|
|
Q_ENABLE_METADATA, Q_DISABLE_METADATA,
|
2003-12-16 05:09:44 +01:00
|
|
|
Q_EXEC, Q_DELIMITER,
|
2004-03-06 17:00:54 +01:00
|
|
|
Q_DISPLAY_VERTICAL_RESULTS, Q_DISPLAY_HORIZONTAL_RESULTS,
|
|
|
|
Q_QUERY_VERTICAL, Q_QUERY_HORIZONTAL,
|
2003-12-16 05:09:44 +01:00
|
|
|
|
2002-08-08 02:12:02 +02:00
|
|
|
Q_UNKNOWN, /* Unknown command. */
|
|
|
|
Q_COMMENT, /* Comments, ignored. */
|
2001-04-18 03:23:14 +02:00
|
|
|
Q_COMMENT_WITH_COMMAND
|
|
|
|
};
|
|
|
|
|
2000-12-27 02:31:06 +01:00
|
|
|
/* this should really be called command */
|
2001-01-07 23:04:30 +01:00
|
|
|
struct st_query
|
2000-10-10 10:42:07 +02:00
|
|
|
{
|
2001-06-30 21:08:35 +02:00
|
|
|
char *query, *query_buf,*first_argument,*end;
|
2000-10-10 10:42:07 +02:00
|
|
|
int first_word_len;
|
2000-12-27 02:31:06 +01:00
|
|
|
my_bool abort_on_error, require_file;
|
2001-01-16 05:02:05 +01:00
|
|
|
uint expected_errno[MAX_EXPECTED_ERRORS];
|
2001-11-04 14:43:41 +01:00
|
|
|
uint expected_errors;
|
2000-12-15 15:12:31 +01:00
|
|
|
char record_file[FN_REFLEN];
|
2001-04-18 03:23:14 +02:00
|
|
|
enum enum_commands type;
|
2000-10-10 10:42:07 +02:00
|
|
|
};
|
|
|
|
|
2002-08-18 15:04:26 +02:00
|
|
|
const char *command_names[]=
|
|
|
|
{
|
|
|
|
"connection",
|
|
|
|
"query",
|
|
|
|
"connect",
|
2003-08-20 12:27:06 +02:00
|
|
|
/* the difference between sleep and real_sleep is that sleep will use
|
|
|
|
the delay from command line (--sleep) if there is one.
|
|
|
|
real_sleep always uses delay from it's argument.
|
|
|
|
the logic is that sometimes delays are cpu-dependent (and --sleep
|
|
|
|
can be used to set this delay. real_sleep is used for cpu-independent
|
|
|
|
delays
|
|
|
|
*/
|
2002-08-18 15:04:26 +02:00
|
|
|
"sleep",
|
|
|
|
"real_sleep",
|
|
|
|
"inc",
|
|
|
|
"dec",
|
|
|
|
"source",
|
|
|
|
"disconnect",
|
|
|
|
"let",
|
|
|
|
"echo",
|
|
|
|
"while",
|
|
|
|
"end",
|
|
|
|
"system",
|
|
|
|
"result",
|
|
|
|
"require",
|
|
|
|
"save_master_pos",
|
|
|
|
"sync_with_master",
|
2002-08-29 20:08:54 +02:00
|
|
|
"sync_slave_with_master",
|
2002-08-18 15:04:26 +02:00
|
|
|
"error",
|
|
|
|
"send",
|
|
|
|
"reap",
|
|
|
|
"dirty_close",
|
|
|
|
"replace_result",
|
2003-06-27 15:29:10 +02:00
|
|
|
"replace_column",
|
2002-08-18 15:04:26 +02:00
|
|
|
"ping",
|
|
|
|
"eval",
|
|
|
|
"rpl_probe",
|
|
|
|
"enable_rpl_parse",
|
|
|
|
"disable_rpl_parse",
|
|
|
|
"eval_result",
|
|
|
|
"enable_query_log",
|
|
|
|
"disable_query_log",
|
|
|
|
"enable_result_log",
|
|
|
|
"disable_result_log",
|
|
|
|
"server_start",
|
|
|
|
"server_stop",
|
|
|
|
"require_manager",
|
|
|
|
"wait_for_slave_to_stop",
|
2002-02-09 21:58:53 +01:00
|
|
|
"require_version",
|
Robustness feature.
Won't be pushed as is - separate email sent for internal review.
WL#1717 "binlog-innodb consistency".
Now when mysqld starts, if InnoDB does a crash recovery, we use the binlog name
and position retrieved from InnoDB (corresponding to the last transaction
successfully committed by InnoDB) to cut any rolled back transaction from
the binary log. This is triggered by the --innodb-safe-binlog option.
Provided you configure mysqld to fsync() InnoDB at every commit (using
flush_log_at_trx_commit) and to fsync() the binlog at every write
(using --sync-binlog=1), this behaviour guarantees that a master always has
consistency between binlog and InnoDB, whenever the crash happens.
6 tests to verify that it works.
client/mysqltest.c:
New command require_os (only "unix" accepted for now).
innobase/include/trx0sys.h:
when InnoDB does crash recovery, we now save the binlog coords it prints, into variables for later use.
innobase/trx/trx0sys.c:
when InnoDB does crash recovery, we now save the binlog coords it prints, into variables for later use.
mysql-test/mysql-test-run.sh:
The tests which check that the binlog is cut at restart, need to not delete those binlogs, of course.
And not delete replication info, so that we can test that the slave does not receive anything
wrong from the cut binlog.
sql/ha_innodb.cc:
methods to read from InnoDB the binlog coords stored into it
sql/ha_innodb.h:
ethods to read from InnoDB the binlog coords stored into it
sql/log.cc:
Added my_sync() when we create a binlog (my_sync of the binlog and of the index file);
this is always done, whether --sync-binlog or not (binlog creation is rare, so no speed
problem, and I like to have the existence of the binlog always reliably recorded, even if
later content is not).
If --crash-binlog-innodb, crash between the binlog write and the InnoDB commit.
New methods:
- report_pos_in_innodb() to store the binlog name and position into InnoDB (used only when
we create a new binlog: at startup and at FLUSH LOGS)
- cut_spurious_tail() to possibly cut the tail of a binlog based on the info we read
from InnoDB (does something only if InnoDB has just done a crash recovery).
sql/mysql_priv.h:
new option, to crash (use for testing only)
sql/mysqld.cc:
New option --innodb-safe-binlog and --crash-binlog-innodb (the latter is for testing, it makes mysqld crash).
Just after opening the logs and opening the storage engines, cut any wrong statement from the binlog, based
on info read from InnoDB.
sql/sql_class.h:
new methods for MYSQL_LOG.
2004-06-20 19:11:02 +02:00
|
|
|
"require_os",
|
2003-01-06 00:48:59 +01:00
|
|
|
"enable_warnings",
|
|
|
|
"disable_warnings",
|
|
|
|
"enable_info",
|
2003-02-19 11:10:23 +01:00
|
|
|
"disable_info",
|
2004-03-30 18:24:28 +02:00
|
|
|
"enable_metadata",
|
|
|
|
"disable_metadata",
|
2003-09-08 07:53:51 +02:00
|
|
|
"exec",
|
2003-12-16 05:09:44 +01:00
|
|
|
"delimiter",
|
2004-03-04 18:08:14 +01:00
|
|
|
"vertical_results",
|
2004-03-06 17:00:54 +01:00
|
|
|
"horizontal_results",
|
2004-03-06 00:00:21 +01:00
|
|
|
"query_vertical",
|
2004-03-06 17:00:54 +01:00
|
|
|
"query_horizontal",
|
2001-02-21 03:39:48 +01:00
|
|
|
0
|
2000-12-27 02:31:06 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
TYPELIB command_typelib= {array_elements(command_names),"",
|
|
|
|
command_names};
|
|
|
|
|
2001-01-07 23:04:30 +01:00
|
|
|
DYNAMIC_STRING ds_res;
|
2001-12-09 23:08:24 +01:00
|
|
|
static void die(const char *fmt, ...);
|
2001-04-18 07:06:00 +02:00
|
|
|
static void init_var_hash();
|
2004-06-11 18:26:13 +02:00
|
|
|
static VAR* var_from_env(const char *, const char *);
|
2001-04-18 07:06:00 +02:00
|
|
|
static byte* get_var_key(const byte* rec, uint* len,
|
2001-04-18 07:42:24 +02:00
|
|
|
my_bool __attribute__((unused)) t);
|
2001-12-09 23:08:24 +01:00
|
|
|
static VAR* var_init(VAR* v, const char *name, int name_len, const char *val,
|
2001-04-18 07:06:00 +02:00
|
|
|
int val_len);
|
|
|
|
|
|
|
|
static void var_free(void* v);
|
2000-12-27 02:31:06 +01:00
|
|
|
|
2001-12-09 23:08:24 +01:00
|
|
|
int dyn_string_cmp(DYNAMIC_STRING* ds, const char *fname);
|
|
|
|
void reject_dump(const char *record_file, char *buf, int size);
|
2000-12-12 20:12:41 +01:00
|
|
|
|
2001-01-07 23:04:30 +01:00
|
|
|
int close_connection(struct st_query* q);
|
2001-12-15 03:41:20 +01:00
|
|
|
VAR* var_get(const char *var_name, const char** var_name_end, my_bool raw,
|
|
|
|
my_bool ignore_not_existing);
|
2001-12-09 23:08:24 +01:00
|
|
|
int eval_expr(VAR* v, const char *p, const char** p_end);
|
|
|
|
static int read_server_arguments(const char *name);
|
2000-12-27 02:31:06 +01:00
|
|
|
|
2003-06-27 15:29:10 +02:00
|
|
|
/* Definitions for replace result */
|
2001-02-13 11:39:50 +01:00
|
|
|
|
|
|
|
typedef struct st_pointer_array { /* when using array-strings */
|
|
|
|
TYPELIB typelib; /* Pointer to strings */
|
|
|
|
byte *str; /* Strings is here */
|
|
|
|
int7 *flag; /* Flag about each var. */
|
2002-08-08 02:12:02 +02:00
|
|
|
uint array_allocs,max_count,length,max_length;
|
2001-02-13 11:39:50 +01:00
|
|
|
} POINTER_ARRAY;
|
|
|
|
|
|
|
|
struct st_replace;
|
|
|
|
struct st_replace *init_replace(my_string *from, my_string *to, uint count,
|
|
|
|
my_string word_end_chars);
|
|
|
|
uint replace_strings(struct st_replace *rep, my_string *start,
|
2002-04-02 16:54:57 +02:00
|
|
|
uint *max_length, const char *from);
|
2001-12-15 03:41:20 +01:00
|
|
|
void free_replace();
|
2001-02-13 11:39:50 +01:00
|
|
|
static int insert_pointer_name(reg1 POINTER_ARRAY *pa,my_string name);
|
|
|
|
void free_pointer_array(POINTER_ARRAY *pa);
|
|
|
|
static int initialize_replace_buffer(void);
|
|
|
|
static void free_replace_buffer(void);
|
2001-12-09 23:08:24 +01:00
|
|
|
static void do_eval(DYNAMIC_STRING* query_eval, const char *query);
|
|
|
|
void str_to_file(const char *fname, char *str, int size);
|
|
|
|
int do_server_op(struct st_query* q,const char *op);
|
2001-02-13 11:39:50 +01:00
|
|
|
|
|
|
|
struct st_replace *glob_replace;
|
|
|
|
static char *out_buff;
|
|
|
|
static uint out_length;
|
2001-06-30 21:08:35 +02:00
|
|
|
static int eval_result = 0;
|
2001-02-13 11:39:50 +01:00
|
|
|
|
2003-06-27 15:29:10 +02:00
|
|
|
/* For column replace */
|
|
|
|
char *replace_column[MAX_COLUMNS];
|
|
|
|
uint max_replace_column= 0;
|
|
|
|
|
|
|
|
static void get_replace_column(struct st_query *q);
|
|
|
|
static void free_replace_column();
|
|
|
|
|
2001-08-13 16:59:40 +02:00
|
|
|
/* Disable functions that only exist in MySQL 4.0 */
|
2003-06-17 18:32:31 +02:00
|
|
|
#if MYSQL_VERSION_ID < 40000
|
2001-10-04 01:44:18 +02:00
|
|
|
void mysql_enable_rpl_parse(MYSQL* mysql __attribute__((unused))) {}
|
|
|
|
void mysql_disable_rpl_parse(MYSQL* mysql __attribute__((unused))) {}
|
|
|
|
int mysql_rpl_parse_enabled(MYSQL* mysql __attribute__((unused))) { return 1; }
|
2002-10-02 12:33:08 +02:00
|
|
|
my_bool mysql_rpl_probe(MYSQL *mysql __attribute__((unused))) { return 1; }
|
2001-08-13 16:59:40 +02:00
|
|
|
#endif
|
2003-09-29 11:31:35 +02:00
|
|
|
static void replace_dynstr_append_mem(DYNAMIC_STRING *ds, const char *val,
|
|
|
|
int len);
|
2001-10-08 03:58:07 +02:00
|
|
|
|
2001-03-27 19:23:04 +02:00
|
|
|
static void do_eval(DYNAMIC_STRING* query_eval, const char* query)
|
|
|
|
{
|
|
|
|
const char* p;
|
|
|
|
register char c;
|
|
|
|
register int escaped = 0;
|
|
|
|
VAR* v;
|
2004-06-21 09:21:20 +02:00
|
|
|
DBUG_ENTER("do_eval");
|
2001-12-06 13:10:51 +01:00
|
|
|
|
2002-08-08 02:12:02 +02:00
|
|
|
for (p= query; (c = *p); ++p)
|
|
|
|
{
|
|
|
|
switch(c) {
|
|
|
|
case '$':
|
|
|
|
if (escaped)
|
|
|
|
{
|
|
|
|
escaped = 0;
|
|
|
|
dynstr_append_mem(query_eval, p, 1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (!(v = var_get(p, &p, 0, 0)))
|
|
|
|
die("Bad variable in eval");
|
|
|
|
dynstr_append_mem(query_eval, v->str_val, v->str_val_len);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case '\\':
|
|
|
|
if (escaped)
|
|
|
|
{
|
|
|
|
escaped = 0;
|
|
|
|
dynstr_append_mem(query_eval, p, 1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
escaped = 1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
dynstr_append_mem(query_eval, p, 1);
|
|
|
|
break;
|
2001-03-27 19:23:04 +02:00
|
|
|
}
|
2002-08-08 02:12:02 +02:00
|
|
|
}
|
2004-06-21 09:21:20 +02:00
|
|
|
DBUG_VOID_RETURN;
|
2001-03-27 19:23:04 +02:00
|
|
|
}
|
2001-02-13 11:39:50 +01:00
|
|
|
|
2002-08-08 02:12:02 +02:00
|
|
|
|
2000-12-27 02:31:06 +01:00
|
|
|
static void close_cons()
|
|
|
|
{
|
2001-01-07 23:04:30 +01:00
|
|
|
DBUG_ENTER("close_cons");
|
2001-10-08 03:58:07 +02:00
|
|
|
if (last_result)
|
|
|
|
mysql_free_result(last_result);
|
2001-01-07 23:04:30 +01:00
|
|
|
for (--next_con; next_con >= cons; --next_con)
|
|
|
|
{
|
|
|
|
mysql_close(&next_con->mysql);
|
|
|
|
my_free(next_con->name, MYF(MY_ALLOW_ZERO_PTR));
|
|
|
|
}
|
|
|
|
DBUG_VOID_RETURN;
|
|
|
|
}
|
|
|
|
|
2002-08-08 02:12:02 +02:00
|
|
|
|
2001-01-07 23:04:30 +01:00
|
|
|
static void close_files()
|
|
|
|
{
|
2001-10-02 04:53:00 +02:00
|
|
|
DBUG_ENTER("close_files");
|
|
|
|
for (; cur_file != file_stack ; cur_file--)
|
2001-01-07 23:04:30 +01:00
|
|
|
{
|
2001-06-30 21:08:35 +02:00
|
|
|
if (*cur_file != stdin && *cur_file)
|
2001-01-07 23:04:30 +01:00
|
|
|
my_fclose(*cur_file,MYF(0));
|
2001-10-02 04:53:00 +02:00
|
|
|
}
|
|
|
|
DBUG_VOID_RETURN;
|
2001-01-07 23:04:30 +01:00
|
|
|
}
|
|
|
|
|
2002-08-08 02:12:02 +02:00
|
|
|
|
2001-01-07 23:04:30 +01:00
|
|
|
static void free_used_memory()
|
|
|
|
{
|
|
|
|
uint i;
|
|
|
|
DBUG_ENTER("free_used_memory");
|
2002-08-08 02:12:02 +02:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2001-10-02 00:13:55 +02:00
|
|
|
if (manager)
|
|
|
|
mysql_manager_close(manager);
|
2002-08-08 02:12:02 +02:00
|
|
|
#endif
|
2001-01-07 23:04:30 +01:00
|
|
|
close_cons();
|
|
|
|
close_files();
|
2001-04-18 07:06:00 +02:00
|
|
|
hash_free(&var_hash);
|
2001-12-06 13:10:51 +01:00
|
|
|
|
2001-01-07 23:04:30 +01:00
|
|
|
for (i=0 ; i < q_lines.elements ; i++)
|
|
|
|
{
|
|
|
|
struct st_query **q= dynamic_element(&q_lines, i, struct st_query**);
|
2001-03-24 03:59:30 +01:00
|
|
|
my_free((gptr) (*q)->query_buf,MYF(MY_ALLOW_ZERO_PTR));
|
2001-01-07 23:04:30 +01:00
|
|
|
my_free((gptr) (*q),MYF(0));
|
|
|
|
}
|
2002-08-08 02:12:02 +02:00
|
|
|
for (i=0; i < 10; i++)
|
2003-12-30 12:14:21 +01:00
|
|
|
{
|
|
|
|
if (var_reg[i].alloced_len)
|
|
|
|
my_free(var_reg[i].str_val, MYF(MY_WME));
|
|
|
|
}
|
2001-10-08 03:58:07 +02:00
|
|
|
while (embedded_server_arg_count > 1)
|
|
|
|
my_free(embedded_server_args[--embedded_server_arg_count],MYF(0));
|
2001-01-07 23:04:30 +01:00
|
|
|
delete_dynamic(&q_lines);
|
|
|
|
dynstr_free(&ds_res);
|
2002-09-11 05:40:08 +02:00
|
|
|
free_replace();
|
2003-06-27 15:29:10 +02:00
|
|
|
free_replace_column();
|
2001-01-07 23:04:30 +01:00
|
|
|
my_free(pass,MYF(MY_ALLOW_ZERO_PTR));
|
|
|
|
free_defaults(default_argv);
|
2001-10-02 04:53:00 +02:00
|
|
|
mysql_server_end();
|
2001-01-07 23:04:30 +01:00
|
|
|
my_end(MY_CHECK_ERROR);
|
|
|
|
DBUG_VOID_RETURN;
|
2000-12-27 02:31:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void die(const char* fmt, ...)
|
|
|
|
{
|
|
|
|
va_list args;
|
2001-01-07 23:04:30 +01:00
|
|
|
DBUG_ENTER("die");
|
2000-12-27 02:31:06 +01:00
|
|
|
va_start(args, fmt);
|
2001-01-07 23:04:30 +01:00
|
|
|
if (fmt)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "%s: ", my_progname);
|
|
|
|
vfprintf(stderr, fmt, args);
|
|
|
|
fprintf(stderr, "\n");
|
2001-03-25 00:02:26 +01:00
|
|
|
fflush(stderr);
|
2001-01-07 23:04:30 +01:00
|
|
|
}
|
2000-12-27 02:31:06 +01:00
|
|
|
va_end(args);
|
2001-01-07 23:04:30 +01:00
|
|
|
free_used_memory();
|
2000-12-27 02:31:06 +01:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2001-10-08 03:58:07 +02:00
|
|
|
/* Note that we will get some memory leaks when calling this! */
|
|
|
|
|
2000-12-27 02:31:06 +01:00
|
|
|
static void abort_not_supported_test()
|
|
|
|
{
|
2001-01-07 23:04:30 +01:00
|
|
|
DBUG_ENTER("abort_not_supported_test");
|
2000-12-27 02:31:06 +01:00
|
|
|
fprintf(stderr, "This test is not supported by this installation\n");
|
|
|
|
if (!silent)
|
|
|
|
printf("skipped\n");
|
2001-01-07 23:04:30 +01:00
|
|
|
free_used_memory();
|
2000-12-27 02:31:06 +01:00
|
|
|
exit(2);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void verbose_msg(const char* fmt, ...)
|
|
|
|
{
|
|
|
|
va_list args;
|
2001-06-05 02:38:10 +02:00
|
|
|
DBUG_ENTER("verbose_msg");
|
|
|
|
if (!verbose)
|
|
|
|
DBUG_VOID_RETURN;
|
2000-12-27 02:31:06 +01:00
|
|
|
|
|
|
|
va_start(args, fmt);
|
|
|
|
|
2000-12-28 02:56:38 +01:00
|
|
|
fprintf(stderr, "%s: At line %u: ", my_progname, start_lineno);
|
2000-12-27 02:31:06 +01:00
|
|
|
vfprintf(stderr, fmt, args);
|
|
|
|
fprintf(stderr, "\n");
|
|
|
|
va_end(args);
|
2001-06-05 02:38:10 +02:00
|
|
|
DBUG_VOID_RETURN;
|
2000-12-27 02:31:06 +01:00
|
|
|
}
|
|
|
|
|
2000-10-10 10:42:07 +02:00
|
|
|
|
2000-12-02 18:11:50 +01:00
|
|
|
void init_parser()
|
|
|
|
{
|
|
|
|
parser.current_line = parser.read_lines = 0;
|
|
|
|
memset(&var_reg,0, sizeof(var_reg));
|
|
|
|
}
|
2000-10-10 10:42:07 +02:00
|
|
|
|
|
|
|
|
2001-01-07 23:04:30 +01:00
|
|
|
int dyn_string_cmp(DYNAMIC_STRING* ds, const char* fname)
|
2000-12-12 20:12:41 +01:00
|
|
|
{
|
|
|
|
MY_STAT stat_info;
|
2001-06-30 21:08:35 +02:00
|
|
|
char *tmp, *res_ptr;
|
|
|
|
char eval_file[FN_REFLEN];
|
2000-12-12 20:12:41 +01:00
|
|
|
int res;
|
2001-06-30 21:08:35 +02:00
|
|
|
uint res_len;
|
2000-12-12 20:12:41 +01:00
|
|
|
int fd;
|
2001-06-30 21:08:35 +02:00
|
|
|
DYNAMIC_STRING res_ds;
|
2001-01-07 23:04:30 +01:00
|
|
|
DBUG_ENTER("dyn_string_cmp");
|
|
|
|
|
2001-10-08 03:58:07 +02:00
|
|
|
if (!test_if_hard_path(fname))
|
|
|
|
{
|
|
|
|
strxmov(eval_file, opt_basedir, fname, NullS);
|
|
|
|
fn_format(eval_file, eval_file,"","",4);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
fn_format(eval_file, fname,"","",4);
|
|
|
|
|
|
|
|
if (!my_stat(eval_file, &stat_info, MYF(MY_WME)))
|
2001-01-07 23:04:30 +01:00
|
|
|
die(NullS);
|
2003-05-27 15:40:14 +02:00
|
|
|
if (!eval_result && (uint) stat_info.st_size != ds->length)
|
|
|
|
{
|
|
|
|
DBUG_PRINT("info",("Size differs: result size: %u file size: %u",
|
|
|
|
ds->length, stat_info.st_size));
|
2001-01-07 23:04:30 +01:00
|
|
|
DBUG_RETURN(2);
|
2003-05-27 15:40:14 +02:00
|
|
|
}
|
2001-06-30 21:08:35 +02:00
|
|
|
if (!(tmp = (char*) my_malloc(stat_info.st_size + 1, MYF(MY_WME))))
|
2001-01-07 23:04:30 +01:00
|
|
|
die(NullS);
|
2001-10-08 03:58:07 +02:00
|
|
|
|
|
|
|
if ((fd = my_open(eval_file, O_RDONLY, MYF(MY_WME))) < 0)
|
2001-01-07 23:04:30 +01:00
|
|
|
die(NullS);
|
2000-12-27 02:31:06 +01:00
|
|
|
if (my_read(fd, (byte*)tmp, stat_info.st_size, MYF(MY_WME|MY_NABP)))
|
2001-01-07 23:04:30 +01:00
|
|
|
die(NullS);
|
2001-06-30 21:08:35 +02:00
|
|
|
tmp[stat_info.st_size] = 0;
|
|
|
|
init_dynamic_string(&res_ds, "", 0, 65536);
|
|
|
|
if (eval_result)
|
|
|
|
{
|
|
|
|
do_eval(&res_ds, tmp);
|
|
|
|
res_ptr = res_ds.str;
|
2002-08-08 02:12:02 +02:00
|
|
|
if ((res_len = res_ds.length) != ds->length)
|
2001-06-30 21:08:35 +02:00
|
|
|
{
|
|
|
|
res = 2;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
res_ptr = tmp;
|
|
|
|
res_len = stat_info.st_size;
|
|
|
|
}
|
2001-12-06 13:10:51 +01:00
|
|
|
|
2001-06-30 21:08:35 +02:00
|
|
|
res = (memcmp(res_ptr, ds->str, res_len)) ? 1 : 0;
|
2001-12-06 13:10:51 +01:00
|
|
|
|
2002-08-08 02:12:02 +02:00
|
|
|
err:
|
|
|
|
if (res && eval_result)
|
|
|
|
str_to_file(fn_format(eval_file, fname, "", ".eval",2), res_ptr,
|
|
|
|
res_len);
|
2001-12-06 13:10:51 +01:00
|
|
|
|
2001-01-07 23:04:30 +01:00
|
|
|
my_free((gptr) tmp, MYF(0));
|
|
|
|
my_close(fd, MYF(MY_WME));
|
2001-06-30 21:08:35 +02:00
|
|
|
dynstr_free(&res_ds);
|
2001-12-06 13:10:51 +01:00
|
|
|
|
2001-01-07 23:04:30 +01:00
|
|
|
DBUG_RETURN(res);
|
2000-12-12 20:12:41 +01:00
|
|
|
}
|
|
|
|
|
2001-01-07 23:04:30 +01:00
|
|
|
static int check_result(DYNAMIC_STRING* ds, const char* fname,
|
2000-12-27 02:31:06 +01:00
|
|
|
my_bool require_option)
|
2000-12-12 20:12:41 +01:00
|
|
|
{
|
|
|
|
int error = 0;
|
2000-12-27 02:31:06 +01:00
|
|
|
int res=dyn_string_cmp(ds, fname);
|
|
|
|
|
|
|
|
if (res && require_option)
|
|
|
|
abort_not_supported_test();
|
2002-08-08 02:12:02 +02:00
|
|
|
switch (res) {
|
2000-12-27 02:31:06 +01:00
|
|
|
case 0:
|
|
|
|
break; /* ok */
|
|
|
|
case 2:
|
|
|
|
verbose_msg("Result length mismatch");
|
|
|
|
error = 1;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
verbose_msg("Result content mismatch");
|
|
|
|
error = 1;
|
|
|
|
break;
|
|
|
|
default: /* impossible */
|
|
|
|
die("Unknown error code from dyn_string_cmp()");
|
|
|
|
}
|
|
|
|
if (error)
|
2001-01-07 23:04:30 +01:00
|
|
|
reject_dump(fname, ds->str, ds->length);
|
2000-12-12 20:12:41 +01:00
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2004-06-18 02:02:29 +02:00
|
|
|
|
2001-12-15 03:41:20 +01:00
|
|
|
VAR* var_get(const char* var_name, const char** var_name_end, my_bool raw,
|
|
|
|
my_bool ignore_not_existing)
|
2000-12-02 18:11:50 +01:00
|
|
|
{
|
|
|
|
int digit;
|
|
|
|
VAR* v;
|
2001-12-15 03:41:20 +01:00
|
|
|
DBUG_ENTER("var_get");
|
|
|
|
DBUG_PRINT("enter",("var_name: %s",var_name));
|
|
|
|
|
|
|
|
if (*var_name != '$')
|
2000-12-27 02:31:06 +01:00
|
|
|
goto err;
|
2001-12-15 03:41:20 +01:00
|
|
|
digit = *++var_name - '0';
|
2000-12-27 02:31:06 +01:00
|
|
|
if (!(digit < 10 && digit >= 0))
|
|
|
|
{
|
2001-04-18 07:06:00 +02:00
|
|
|
const char* save_var_name = var_name, *end;
|
2004-06-18 02:02:29 +02:00
|
|
|
uint length;
|
2001-04-18 07:06:00 +02:00
|
|
|
end = (var_name_end) ? *var_name_end : 0;
|
2003-03-16 08:20:45 +01:00
|
|
|
while (my_isvar(charset_info,*var_name) && var_name != end)
|
2004-06-18 02:02:29 +02:00
|
|
|
var_name++;
|
2001-12-15 03:41:20 +01:00
|
|
|
if (var_name == save_var_name)
|
|
|
|
{
|
|
|
|
if (ignore_not_existing)
|
|
|
|
DBUG_RETURN(0);
|
2001-04-18 07:06:00 +02:00
|
|
|
die("Empty variable");
|
2001-12-15 03:41:20 +01:00
|
|
|
}
|
2004-06-18 02:02:29 +02:00
|
|
|
length= (uint) (var_name - save_var_name);
|
2001-12-06 13:10:51 +01:00
|
|
|
|
2004-06-18 02:02:29 +02:00
|
|
|
if (!(v = (VAR*) hash_search(&var_hash, save_var_name, length)) &&
|
|
|
|
length < MAX_VAR_NAME)
|
2001-12-15 03:41:20 +01:00
|
|
|
{
|
2004-06-18 02:02:29 +02:00
|
|
|
char buff[MAX_VAR_NAME+1];
|
|
|
|
strmake(buff, save_var_name, length);
|
|
|
|
v= var_from_env(buff, "");
|
2001-12-15 03:41:20 +01:00
|
|
|
}
|
2004-06-18 02:02:29 +02:00
|
|
|
var_name--; /* Point at last character */
|
2000-12-27 02:31:06 +01:00
|
|
|
}
|
2002-08-08 02:12:02 +02:00
|
|
|
else
|
2001-12-15 03:41:20 +01:00
|
|
|
v = var_reg + digit;
|
2001-12-06 13:10:51 +01:00
|
|
|
|
2000-12-27 02:31:06 +01:00
|
|
|
if (!raw && v->int_dirty)
|
|
|
|
{
|
|
|
|
sprintf(v->str_val, "%d", v->int_val);
|
|
|
|
v->int_dirty = 0;
|
2001-04-06 03:04:34 +02:00
|
|
|
v->str_val_len = strlen(v->str_val);
|
2000-12-27 02:31:06 +01:00
|
|
|
}
|
2001-12-15 03:41:20 +01:00
|
|
|
if (var_name_end)
|
2001-03-27 19:23:04 +02:00
|
|
|
*var_name_end = var_name ;
|
2001-12-15 03:41:20 +01:00
|
|
|
DBUG_RETURN(v);
|
2000-12-27 02:31:06 +01:00
|
|
|
err:
|
|
|
|
if (var_name_end)
|
2000-12-02 18:11:50 +01:00
|
|
|
*var_name_end = 0;
|
|
|
|
die("Unsupported variable name: %s", var_name);
|
2001-12-15 03:41:20 +01:00
|
|
|
DBUG_RETURN(0);
|
2000-12-02 18:11:50 +01:00
|
|
|
}
|
|
|
|
|
2001-04-18 07:06:00 +02:00
|
|
|
static VAR* var_obtain(char* name, int len)
|
|
|
|
{
|
|
|
|
VAR* v;
|
2002-08-08 02:12:02 +02:00
|
|
|
if ((v = (VAR*)hash_search(&var_hash, name, len)))
|
2001-04-18 07:06:00 +02:00
|
|
|
return v;
|
2001-07-12 04:59:17 +02:00
|
|
|
v = var_init(0, name, len, "", 0);
|
2003-09-19 11:44:31 +02:00
|
|
|
my_hash_insert(&var_hash, (byte*)v);
|
2001-04-18 07:06:00 +02:00
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
2000-12-02 18:11:50 +01:00
|
|
|
int var_set(char* var_name, char* var_name_end, char* var_val,
|
|
|
|
char* var_val_end)
|
|
|
|
{
|
|
|
|
int digit;
|
|
|
|
VAR* v;
|
2000-12-27 02:31:06 +01:00
|
|
|
if (*var_name++ != '$')
|
2000-12-02 18:11:50 +01:00
|
|
|
{
|
|
|
|
--var_name;
|
|
|
|
*var_name_end = 0;
|
|
|
|
die("Variable name in %s does not start with '$'", var_name);
|
|
|
|
}
|
|
|
|
digit = *var_name - '0';
|
2000-12-27 02:31:06 +01:00
|
|
|
if (!(digit < 10 && digit >= 0))
|
2000-12-02 18:11:50 +01:00
|
|
|
{
|
2001-04-18 07:06:00 +02:00
|
|
|
v = var_obtain(var_name, var_name_end - var_name);
|
2000-12-02 18:11:50 +01:00
|
|
|
}
|
2002-08-08 02:12:02 +02:00
|
|
|
else
|
2001-04-18 07:06:00 +02:00
|
|
|
v = var_reg + digit;
|
2001-12-06 13:10:51 +01:00
|
|
|
|
2001-06-30 21:08:35 +02:00
|
|
|
return eval_expr(v, var_val, (const char**)&var_val_end);
|
2000-12-02 18:11:50 +01:00
|
|
|
}
|
|
|
|
|
2000-10-10 10:42:07 +02:00
|
|
|
int open_file(const char* name)
|
|
|
|
{
|
2001-10-08 03:58:07 +02:00
|
|
|
char buff[FN_REFLEN];
|
|
|
|
if (!test_if_hard_path(name))
|
|
|
|
{
|
|
|
|
strxmov(buff, opt_basedir, name, NullS);
|
|
|
|
name=buff;
|
|
|
|
}
|
|
|
|
fn_format(buff,name,"","",4);
|
|
|
|
|
2001-01-07 23:04:30 +01:00
|
|
|
if (*cur_file && cur_file == file_stack_end)
|
2000-10-10 10:42:07 +02:00
|
|
|
die("Source directives are nesting too deep");
|
2003-01-28 07:38:28 +01:00
|
|
|
if (!(*(cur_file+1) = my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(MY_WME))))
|
2001-01-07 23:04:30 +01:00
|
|
|
die(NullS);
|
|
|
|
cur_file++;
|
2000-12-28 02:56:38 +01:00
|
|
|
*++lineno=1;
|
2000-12-22 20:10:35 +01:00
|
|
|
|
2000-10-10 10:42:07 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2002-09-11 05:40:08 +02:00
|
|
|
|
2002-01-25 06:49:47 +01:00
|
|
|
/* ugly long name, but we are following the convention */
|
2002-05-22 18:11:06 +02:00
|
|
|
int do_wait_for_slave_to_stop(struct st_query* q __attribute__((unused)))
|
2002-01-25 06:49:47 +01:00
|
|
|
{
|
|
|
|
MYSQL* mysql = &cur_con->mysql;
|
|
|
|
for (;;)
|
|
|
|
{
|
2003-10-08 11:01:58 +02:00
|
|
|
MYSQL_RES *res;
|
2002-01-25 06:49:47 +01:00
|
|
|
MYSQL_ROW row;
|
|
|
|
int done;
|
|
|
|
LINT_INIT(res);
|
2002-08-08 02:12:02 +02:00
|
|
|
|
2003-08-18 23:08:08 +02:00
|
|
|
if (mysql_query(mysql,"show status like 'Slave_running'") ||
|
|
|
|
!(res=mysql_store_result(mysql)))
|
2002-01-25 06:49:47 +01:00
|
|
|
die("Query failed while probing slave for stop: %s",
|
|
|
|
mysql_error(mysql));
|
|
|
|
if (!(row=mysql_fetch_row(res)) || !row[1])
|
|
|
|
{
|
|
|
|
mysql_free_result(res);
|
|
|
|
die("Strange result from query while probing slave for stop");
|
|
|
|
}
|
|
|
|
done = !strcmp(row[1],"OFF");
|
|
|
|
mysql_free_result(res);
|
|
|
|
if (done)
|
|
|
|
break;
|
2002-09-11 05:40:08 +02:00
|
|
|
my_sleep(SLAVE_POLL_INTERVAL);
|
2002-01-25 06:49:47 +01:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2002-05-22 18:11:06 +02:00
|
|
|
int do_require_manager(struct st_query* a __attribute__((unused)))
|
2001-10-11 21:54:06 +02:00
|
|
|
{
|
|
|
|
if (!manager)
|
|
|
|
abort_not_supported_test();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2001-10-04 23:42:25 +02:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2001-10-02 00:13:55 +02:00
|
|
|
int do_server_start(struct st_query* q)
|
|
|
|
{
|
|
|
|
return do_server_op(q,"start");
|
|
|
|
}
|
|
|
|
|
|
|
|
int do_server_stop(struct st_query* q)
|
|
|
|
{
|
|
|
|
return do_server_op(q,"stop");
|
|
|
|
}
|
|
|
|
|
|
|
|
int do_server_op(struct st_query* q,const char* op)
|
|
|
|
{
|
|
|
|
char* p=q->first_argument;
|
|
|
|
char com_buf[256],*com_p;
|
2001-10-10 23:36:35 +02:00
|
|
|
if (!manager)
|
|
|
|
{
|
|
|
|
die("Manager is not initialized, manager commands are not possible");
|
|
|
|
}
|
2001-10-02 00:13:55 +02:00
|
|
|
com_p=strmov(com_buf,op);
|
|
|
|
com_p=strmov(com_p,"_exec ");
|
|
|
|
if (!*p)
|
|
|
|
die("Missing server name in server_%s\n",op);
|
2003-03-16 08:20:45 +01:00
|
|
|
while (*p && !my_isspace(charset_info,*p))
|
2003-10-08 11:01:58 +02:00
|
|
|
*com_p++= *p++;
|
2001-10-02 00:13:55 +02:00
|
|
|
*com_p++=' ';
|
|
|
|
com_p=int10_to_str(manager_wait_timeout,com_p,10);
|
|
|
|
*com_p++ = '\n';
|
|
|
|
*com_p=0;
|
|
|
|
if (mysql_manager_command(manager,com_buf,(int)(com_p-com_buf)))
|
|
|
|
die("Error in command: %s(%d)",manager->last_error,manager->last_errno);
|
|
|
|
while (!manager->eof)
|
|
|
|
{
|
|
|
|
if (mysql_manager_fetch_line(manager,com_buf,sizeof(com_buf)))
|
|
|
|
die("Error fetching result line: %s(%d)", manager->last_error,
|
|
|
|
manager->last_errno);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2001-10-04 23:42:25 +02:00
|
|
|
#endif
|
2001-10-02 00:13:55 +02:00
|
|
|
|
2002-02-09 21:58:53 +01:00
|
|
|
int do_require_version(struct st_query* q)
|
|
|
|
{
|
|
|
|
MYSQL* mysql = &cur_con->mysql;
|
|
|
|
MYSQL_RES* res;
|
|
|
|
MYSQL_ROW row;
|
|
|
|
char* p=q->first_argument, *ver_arg;
|
|
|
|
uint ver_arg_len,ver_len;
|
|
|
|
LINT_INIT(res);
|
2002-08-08 02:12:02 +02:00
|
|
|
|
2002-02-09 21:58:53 +01:00
|
|
|
if (!*p)
|
|
|
|
die("Missing version argument in require_version\n");
|
|
|
|
ver_arg = p;
|
2003-03-16 08:20:45 +01:00
|
|
|
while (*p && !my_isspace(charset_info,*p))
|
2002-02-09 21:58:53 +01:00
|
|
|
p++;
|
|
|
|
*p = 0;
|
|
|
|
ver_arg_len = p - ver_arg;
|
2002-08-08 02:12:02 +02:00
|
|
|
|
2002-02-09 21:58:53 +01:00
|
|
|
if (mysql_query(mysql, "select version()") ||
|
|
|
|
!(res=mysql_store_result(mysql)))
|
|
|
|
die("Query failed while check server version: %s",
|
|
|
|
mysql_error(mysql));
|
|
|
|
if (!(row=mysql_fetch_row(res)) || !row[0])
|
|
|
|
{
|
|
|
|
mysql_free_result(res);
|
|
|
|
die("Strange result from query while checking version");
|
|
|
|
}
|
|
|
|
ver_len = strlen(row[0]);
|
|
|
|
if (ver_len < ver_arg_len || memcmp(row[0],ver_arg,ver_arg_len))
|
|
|
|
{
|
|
|
|
mysql_free_result(res);
|
|
|
|
abort_not_supported_test();
|
|
|
|
}
|
|
|
|
mysql_free_result(res);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
Robustness feature.
Won't be pushed as is - separate email sent for internal review.
WL#1717 "binlog-innodb consistency".
Now when mysqld starts, if InnoDB does a crash recovery, we use the binlog name
and position retrieved from InnoDB (corresponding to the last transaction
successfully committed by InnoDB) to cut any rolled back transaction from
the binary log. This is triggered by the --innodb-safe-binlog option.
Provided you configure mysqld to fsync() InnoDB at every commit (using
flush_log_at_trx_commit) and to fsync() the binlog at every write
(using --sync-binlog=1), this behaviour guarantees that a master always has
consistency between binlog and InnoDB, whenever the crash happens.
6 tests to verify that it works.
client/mysqltest.c:
New command require_os (only "unix" accepted for now).
innobase/include/trx0sys.h:
when InnoDB does crash recovery, we now save the binlog coords it prints, into variables for later use.
innobase/trx/trx0sys.c:
when InnoDB does crash recovery, we now save the binlog coords it prints, into variables for later use.
mysql-test/mysql-test-run.sh:
The tests which check that the binlog is cut at restart, need to not delete those binlogs, of course.
And not delete replication info, so that we can test that the slave does not receive anything
wrong from the cut binlog.
sql/ha_innodb.cc:
methods to read from InnoDB the binlog coords stored into it
sql/ha_innodb.h:
ethods to read from InnoDB the binlog coords stored into it
sql/log.cc:
Added my_sync() when we create a binlog (my_sync of the binlog and of the index file);
this is always done, whether --sync-binlog or not (binlog creation is rare, so no speed
problem, and I like to have the existence of the binlog always reliably recorded, even if
later content is not).
If --crash-binlog-innodb, crash between the binlog write and the InnoDB commit.
New methods:
- report_pos_in_innodb() to store the binlog name and position into InnoDB (used only when
we create a new binlog: at startup and at FLUSH LOGS)
- cut_spurious_tail() to possibly cut the tail of a binlog based on the info we read
from InnoDB (does something only if InnoDB has just done a crash recovery).
sql/mysql_priv.h:
new option, to crash (use for testing only)
sql/mysqld.cc:
New option --innodb-safe-binlog and --crash-binlog-innodb (the latter is for testing, it makes mysqld crash).
Just after opening the logs and opening the storage engines, cut any wrong statement from the binlog, based
on info read from InnoDB.
sql/sql_class.h:
new methods for MYSQL_LOG.
2004-06-20 19:11:02 +02:00
|
|
|
int do_require_os(struct st_query* q)
|
|
|
|
{
|
|
|
|
char *p=q->first_argument, *os_arg;
|
|
|
|
DBUG_ENTER("do_require_os");
|
|
|
|
|
|
|
|
if (!*p)
|
|
|
|
die("Missing version argument in require_os\n");
|
|
|
|
os_arg= p;
|
|
|
|
while (*p && !my_isspace(charset_info,*p))
|
|
|
|
p++;
|
|
|
|
*p = 0;
|
|
|
|
|
|
|
|
if (strcmp(os_arg, "unix"))
|
|
|
|
die("For now only testing of os=unix is implemented\n");
|
|
|
|
|
|
|
|
#if defined(__NETWARE__) || defined(__WIN__) || defined(__OS2__)
|
|
|
|
abort_not_supported_test();
|
|
|
|
#endif
|
|
|
|
DBUG_RETURN(0);
|
|
|
|
}
|
|
|
|
|
2001-01-07 23:04:30 +01:00
|
|
|
int do_source(struct st_query* q)
|
2000-10-10 10:42:07 +02:00
|
|
|
{
|
2001-01-07 23:04:30 +01:00
|
|
|
char* p=q->first_argument, *name;
|
2000-12-27 02:31:06 +01:00
|
|
|
if (!*p)
|
2000-10-10 10:42:07 +02:00
|
|
|
die("Missing file name in source\n");
|
|
|
|
name = p;
|
2003-03-16 08:20:45 +01:00
|
|
|
while (*p && !my_isspace(charset_info,*p))
|
2000-10-10 10:42:07 +02:00
|
|
|
p++;
|
|
|
|
*p = 0;
|
2000-12-22 20:10:35 +01:00
|
|
|
|
2000-10-10 10:42:07 +02:00
|
|
|
return open_file(name);
|
|
|
|
}
|
|
|
|
|
2003-09-08 07:53:51 +02:00
|
|
|
/*
|
|
|
|
Execute given command.
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
do_exec()
|
|
|
|
q called command
|
|
|
|
|
|
|
|
DESCRIPTION
|
|
|
|
If one uses --exec command [args] command in .test file
|
|
|
|
we will execute the command and record its output.
|
|
|
|
|
|
|
|
RETURN VALUES
|
|
|
|
0 ok
|
|
|
|
1 error
|
|
|
|
*/
|
|
|
|
|
|
|
|
int do_exec(struct st_query* q)
|
|
|
|
{
|
|
|
|
int error= 0;
|
|
|
|
DYNAMIC_STRING *ds;
|
|
|
|
DYNAMIC_STRING ds_tmp;
|
|
|
|
char buf[1024];
|
|
|
|
FILE *res_file;
|
|
|
|
char *cmd= q->first_argument;
|
2004-03-19 13:52:48 +01:00
|
|
|
DBUG_ENTER("do_exec");
|
2003-09-08 07:53:51 +02:00
|
|
|
|
2003-10-08 11:01:58 +02:00
|
|
|
while (*cmd && my_isspace(charset_info, *cmd))
|
2003-09-08 07:53:51 +02:00
|
|
|
cmd++;
|
|
|
|
if (!*cmd)
|
|
|
|
die("Missing argument in exec\n");
|
|
|
|
|
2004-03-19 13:52:48 +01:00
|
|
|
DBUG_PRINT("info", ("Executing '%s'", cmd));
|
|
|
|
|
|
|
|
if (!(res_file= popen(cmd, "r")) && q->abort_on_error)
|
|
|
|
die("popen() failed\n");
|
|
|
|
|
2004-03-10 19:54:07 +01:00
|
|
|
if (disable_result_log)
|
2003-09-08 07:53:51 +02:00
|
|
|
{
|
2004-03-19 13:52:48 +01:00
|
|
|
while (fgets(buf, sizeof(buf), res_file))
|
2004-03-19 17:33:38 +01:00
|
|
|
{
|
|
|
|
buf[strlen(buf)-1]=0;
|
|
|
|
DBUG_PRINT("exec_result",("%s", buf));
|
|
|
|
}
|
2003-09-08 07:53:51 +02:00
|
|
|
}
|
|
|
|
else
|
2004-03-10 19:54:07 +01:00
|
|
|
{
|
|
|
|
if (q->record_file[0])
|
|
|
|
{
|
|
|
|
init_dynamic_string(&ds_tmp, "", 16384, 65536);
|
|
|
|
ds= &ds_tmp;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ds= &ds_res;
|
2003-09-08 07:53:51 +02:00
|
|
|
|
2004-03-10 19:54:07 +01:00
|
|
|
while (fgets(buf, sizeof(buf), res_file))
|
|
|
|
replace_dynstr_append_mem(ds, buf, strlen(buf));
|
2004-05-25 21:00:14 +02:00
|
|
|
|
2004-03-10 19:54:07 +01:00
|
|
|
if (glob_replace)
|
|
|
|
free_replace();
|
2003-09-29 21:10:30 +02:00
|
|
|
|
2004-03-10 19:54:07 +01:00
|
|
|
if (record)
|
|
|
|
{
|
|
|
|
if (!q->record_file[0] && !result_file)
|
|
|
|
die("At line %u: Missing result file", start_lineno);
|
|
|
|
if (!result_file)
|
|
|
|
str_to_file(q->record_file, ds->str, ds->length);
|
|
|
|
}
|
|
|
|
else if (q->record_file[0])
|
|
|
|
{
|
|
|
|
error= check_result(ds, q->record_file, q->require_file);
|
|
|
|
}
|
|
|
|
if (ds == &ds_tmp)
|
|
|
|
dynstr_free(&ds_tmp);
|
2003-09-08 07:53:51 +02:00
|
|
|
}
|
2004-03-19 13:52:48 +01:00
|
|
|
pclose(res_file);
|
2003-09-08 07:53:51 +02:00
|
|
|
|
2004-03-19 13:52:48 +01:00
|
|
|
DBUG_RETURN(error);
|
2003-09-08 07:53:51 +02:00
|
|
|
}
|
|
|
|
|
2004-05-25 21:00:14 +02:00
|
|
|
|
2001-06-30 21:08:35 +02:00
|
|
|
int var_query_set(VAR* v, const char* p, const char** p_end)
|
|
|
|
{
|
|
|
|
char* end = (char*)((p_end && *p_end) ? *p_end : p + strlen(p));
|
|
|
|
MYSQL_RES *res;
|
|
|
|
MYSQL_ROW row;
|
|
|
|
MYSQL* mysql = &cur_con->mysql;
|
|
|
|
LINT_INIT(res);
|
2001-12-06 13:10:51 +01:00
|
|
|
|
2001-06-30 21:08:35 +02:00
|
|
|
while (end > p && *end != '`')
|
|
|
|
--end;
|
|
|
|
if (p == end)
|
|
|
|
die("Syntax error in query, missing '`'");
|
|
|
|
++p;
|
|
|
|
|
|
|
|
if (mysql_real_query(mysql, p, (int)(end - p)) ||
|
|
|
|
!(res = mysql_store_result(mysql)))
|
|
|
|
{
|
|
|
|
*end = 0;
|
|
|
|
die("Error running query '%s': %s", p, mysql_error(mysql));
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((row = mysql_fetch_row(res)) && row[0])
|
2003-08-22 03:07:40 +02:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
Concatenate all row results with tab in between to allow us to work
|
|
|
|
with results from many columns (for example from SHOW VARIABLES)
|
|
|
|
*/
|
|
|
|
DYNAMIC_STRING result;
|
|
|
|
uint i;
|
|
|
|
ulong *lengths;
|
|
|
|
char *end;
|
|
|
|
|
|
|
|
init_dynamic_string(&result, "", 16384, 65536);
|
|
|
|
lengths= mysql_fetch_lengths(res);
|
|
|
|
for (i=0; i < mysql_num_fields(res); i++)
|
|
|
|
{
|
|
|
|
if (row[0])
|
|
|
|
dynstr_append_mem(&result, row[i], lengths[i]);
|
|
|
|
dynstr_append_mem(&result, "\t", 1);
|
|
|
|
}
|
|
|
|
end= result.str + result.length-1;
|
|
|
|
eval_expr(v, result.str, (const char**) &end);
|
|
|
|
dynstr_free(&result);
|
|
|
|
}
|
2001-06-30 21:08:35 +02:00
|
|
|
else
|
|
|
|
eval_expr(v, "", 0);
|
|
|
|
|
|
|
|
mysql_free_result(res);
|
|
|
|
return 0;
|
|
|
|
}
|
2000-12-02 18:11:50 +01:00
|
|
|
|
2001-09-28 07:05:54 +02:00
|
|
|
void var_copy(VAR* dest, VAR* src)
|
|
|
|
{
|
|
|
|
dest->int_val=src->int_val;
|
|
|
|
dest->int_dirty=src->int_dirty;
|
|
|
|
if (dest->alloced_len < src->alloced_len &&
|
2001-12-15 03:41:20 +01:00
|
|
|
!(dest->str_val=my_realloc(dest->str_val,src->alloced_len+1,
|
2001-09-28 07:05:54 +02:00
|
|
|
MYF(MY_WME))))
|
|
|
|
die("Out of memory");
|
|
|
|
dest->str_val_len=src->str_val_len;
|
2001-12-15 03:41:20 +01:00
|
|
|
memcpy(dest->str_val,src->str_val,src->str_val_len+1);
|
2001-09-28 07:05:54 +02:00
|
|
|
}
|
|
|
|
|
2001-03-27 19:23:04 +02:00
|
|
|
int eval_expr(VAR* v, const char* p, const char** p_end)
|
2000-12-02 18:11:50 +01:00
|
|
|
{
|
|
|
|
VAR* vp;
|
2000-12-27 02:31:06 +01:00
|
|
|
if (*p == '$')
|
2001-12-15 03:41:20 +01:00
|
|
|
{
|
|
|
|
if ((vp = var_get(p,p_end,0,0)))
|
2000-12-02 18:11:50 +01:00
|
|
|
{
|
2001-12-15 03:41:20 +01:00
|
|
|
var_copy(v, vp);
|
|
|
|
return 0;
|
2000-12-15 01:43:13 +01:00
|
|
|
}
|
2001-12-15 03:41:20 +01:00
|
|
|
}
|
2002-08-08 02:12:02 +02:00
|
|
|
else if (*p == '`')
|
2001-06-30 21:08:35 +02:00
|
|
|
{
|
|
|
|
return var_query_set(v, p, p_end);
|
|
|
|
}
|
2000-12-15 01:43:13 +01:00
|
|
|
else
|
|
|
|
{
|
2001-06-30 21:08:35 +02:00
|
|
|
int new_val_len = (p_end && *p_end) ?
|
|
|
|
(int) (*p_end - p) : (int) strlen(p);
|
|
|
|
if (new_val_len + 1 >= v->alloced_len)
|
|
|
|
{
|
2002-08-08 02:12:02 +02:00
|
|
|
v->alloced_len = (new_val_len < MIN_VAR_ALLOC - 1) ?
|
2001-06-30 21:08:35 +02:00
|
|
|
MIN_VAR_ALLOC : new_val_len + 1;
|
|
|
|
if (!(v->str_val =
|
2001-12-15 03:41:20 +01:00
|
|
|
v->str_val ? my_realloc(v->str_val, v->alloced_len+1,
|
2001-06-30 21:08:35 +02:00
|
|
|
MYF(MY_WME)) :
|
2001-12-15 03:41:20 +01:00
|
|
|
my_malloc(v->alloced_len+1, MYF(MY_WME))))
|
2001-06-30 21:08:35 +02:00
|
|
|
die("Out of memory");
|
|
|
|
}
|
|
|
|
v->str_val_len = new_val_len;
|
|
|
|
memcpy(v->str_val, p, new_val_len);
|
|
|
|
v->str_val[new_val_len] = 0;
|
2001-04-18 03:23:14 +02:00
|
|
|
v->int_val=atoi(p);
|
|
|
|
v->int_dirty=0;
|
2000-12-02 18:11:50 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2000-12-22 20:10:35 +01:00
|
|
|
|
2000-12-02 18:11:50 +01:00
|
|
|
die("Invalid expr: %s", p);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2001-01-07 23:04:30 +01:00
|
|
|
int do_inc(struct st_query* q)
|
2000-12-02 18:11:50 +01:00
|
|
|
{
|
2001-01-07 23:04:30 +01:00
|
|
|
char* p=q->first_argument;
|
2000-12-02 18:11:50 +01:00
|
|
|
VAR* v;
|
2001-12-15 03:41:20 +01:00
|
|
|
v = var_get(p, 0, 1, 0);
|
2000-12-02 18:11:50 +01:00
|
|
|
v->int_val++;
|
|
|
|
v->int_dirty = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2001-01-07 23:04:30 +01:00
|
|
|
int do_dec(struct st_query* q)
|
2000-12-02 18:11:50 +01:00
|
|
|
{
|
2001-01-07 23:04:30 +01:00
|
|
|
char* p=q->first_argument;
|
2000-12-02 18:11:50 +01:00
|
|
|
VAR* v;
|
2001-12-15 03:41:20 +01:00
|
|
|
v = var_get(p, 0, 1, 0);
|
2000-12-02 18:11:50 +01:00
|
|
|
v->int_val--;
|
|
|
|
v->int_dirty = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2001-01-07 23:04:30 +01:00
|
|
|
int do_system(struct st_query* q)
|
2000-12-15 01:43:13 +01:00
|
|
|
{
|
2001-01-07 23:04:30 +01:00
|
|
|
char* p=q->first_argument;
|
2000-12-15 01:43:13 +01:00
|
|
|
VAR v;
|
2001-07-12 04:59:17 +02:00
|
|
|
var_init(&v, 0, 0, 0, 0);
|
2000-12-15 01:43:13 +01:00
|
|
|
eval_expr(&v, p, 0); /* NULL terminated */
|
2001-04-18 03:23:14 +02:00
|
|
|
if (v.str_val_len)
|
2003-05-21 20:39:58 +02:00
|
|
|
{
|
2004-03-19 13:52:48 +01:00
|
|
|
char expr_buf[1024];
|
2003-05-21 20:39:58 +02:00
|
|
|
if ((uint)v.str_val_len > sizeof(expr_buf) - 1)
|
|
|
|
v.str_val_len = sizeof(expr_buf) - 1;
|
|
|
|
memcpy(expr_buf, v.str_val, v.str_val_len);
|
|
|
|
expr_buf[v.str_val_len] = 0;
|
|
|
|
DBUG_PRINT("info", ("running system command '%s'", expr_buf));
|
|
|
|
if (system(expr_buf) && q->abort_on_error)
|
|
|
|
die("system command '%s' failed", expr_buf);
|
|
|
|
}
|
2001-09-28 07:05:54 +02:00
|
|
|
var_free(&v);
|
2000-12-15 01:43:13 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2000-12-02 18:11:50 +01:00
|
|
|
|
2001-01-07 23:04:30 +01:00
|
|
|
int do_echo(struct st_query* q)
|
2000-12-02 18:11:50 +01:00
|
|
|
{
|
2001-01-07 23:04:30 +01:00
|
|
|
char* p=q->first_argument;
|
2000-12-02 18:11:50 +01:00
|
|
|
VAR v;
|
2001-07-12 04:59:17 +02:00
|
|
|
var_init(&v,0,0,0,0);
|
2000-12-02 18:11:50 +01:00
|
|
|
eval_expr(&v, p, 0); /* NULL terminated */
|
2001-04-18 03:23:14 +02:00
|
|
|
if (v.str_val_len)
|
|
|
|
{
|
|
|
|
fflush(stdout);
|
|
|
|
write(1, v.str_val, v.str_val_len);
|
|
|
|
}
|
2000-12-02 18:11:50 +01:00
|
|
|
write(1, "\n", 1);
|
2001-09-28 07:05:54 +02:00
|
|
|
var_free(&v);
|
2000-12-02 18:11:50 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2002-08-08 02:12:02 +02:00
|
|
|
|
2002-08-29 20:08:54 +02:00
|
|
|
int do_sync_with_master2(const char* p)
|
2001-01-17 13:47:33 +01:00
|
|
|
{
|
|
|
|
MYSQL_RES* res;
|
|
|
|
MYSQL_ROW row;
|
|
|
|
MYSQL* mysql = &cur_con->mysql;
|
|
|
|
char query_buf[FN_REFLEN+128];
|
Replication testsuite: making the master-slave synchronization less likely to fail,
by adding sleep-and-retries (max 4 times) if MASTER_POS_WAIT() returns NULL
in sync_with_master and sync_slave_with_master.
The problem showed up only today, in MySQL 5.0 in rpl_server_id2.test,
but may affect 4.x as well, so fixing it here. Note that I am also fixing
5.0 too, with the same exact patch, because I don't want to leave 5.0 broken
until the next 4.0->4.1->5.0 merge.
client/mysqltest.c:
in sync_with_master (and sync_slave_with_master), if MASTER_POS_WAIT() returns NULL,
it may be that the slave SQL thread did not have time to start yes, so we sleep
1 sec and retry, 4 times at most.
mysql-test/r/rpl_server_id2.result:
result update
mysql-test/t/rpl_server_id2.test:
master_slave.inc already drops the table
2004-05-14 23:08:03 +02:00
|
|
|
int offset= 0, tries= 0;
|
2001-06-09 03:24:41 +02:00
|
|
|
int rpl_parse;
|
|
|
|
|
2002-08-08 02:12:02 +02:00
|
|
|
if (!master_pos.file[0])
|
|
|
|
{
|
|
|
|
die("Line %u: Calling 'sync_with_master' without calling 'save_master_pos'", start_lineno);
|
|
|
|
}
|
2001-06-09 03:24:41 +02:00
|
|
|
rpl_parse = mysql_rpl_parse_enabled(mysql);
|
|
|
|
mysql_disable_rpl_parse(mysql);
|
2001-12-06 13:10:51 +01:00
|
|
|
|
2002-08-08 02:12:02 +02:00
|
|
|
if (*p)
|
2001-01-24 20:47:09 +01:00
|
|
|
offset = atoi(p);
|
2001-12-06 13:10:51 +01:00
|
|
|
|
2001-01-17 13:47:33 +01:00
|
|
|
sprintf(query_buf, "select master_pos_wait('%s', %ld)", master_pos.file,
|
2001-01-24 20:47:09 +01:00
|
|
|
master_pos.pos + offset);
|
Replication testsuite: making the master-slave synchronization less likely to fail,
by adding sleep-and-retries (max 4 times) if MASTER_POS_WAIT() returns NULL
in sync_with_master and sync_slave_with_master.
The problem showed up only today, in MySQL 5.0 in rpl_server_id2.test,
but may affect 4.x as well, so fixing it here. Note that I am also fixing
5.0 too, with the same exact patch, because I don't want to leave 5.0 broken
until the next 4.0->4.1->5.0 merge.
client/mysqltest.c:
in sync_with_master (and sync_slave_with_master), if MASTER_POS_WAIT() returns NULL,
it may be that the slave SQL thread did not have time to start yes, so we sleep
1 sec and retry, 4 times at most.
mysql-test/r/rpl_server_id2.result:
result update
mysql-test/t/rpl_server_id2.test:
master_slave.inc already drops the table
2004-05-14 23:08:03 +02:00
|
|
|
|
|
|
|
wait_for_position:
|
|
|
|
|
2002-08-08 02:12:02 +02:00
|
|
|
if (mysql_query(mysql, query_buf))
|
|
|
|
die("line %u: failed in %s: %d: %s", start_lineno, query_buf,
|
2001-01-17 13:47:33 +01:00
|
|
|
mysql_errno(mysql), mysql_error(mysql));
|
|
|
|
|
2002-08-08 02:12:02 +02:00
|
|
|
if (!(last_result = res = mysql_store_result(mysql)))
|
2003-02-14 10:47:41 +01:00
|
|
|
die("line %u: mysql_store_result() returned NULL for '%s'", start_lineno,
|
|
|
|
query_buf);
|
2002-08-08 02:12:02 +02:00
|
|
|
if (!(row = mysql_fetch_row(res)))
|
2001-01-17 13:47:33 +01:00
|
|
|
die("line %u: empty result in %s", start_lineno, query_buf);
|
2002-08-08 02:12:02 +02:00
|
|
|
if (!row[0])
|
Replication testsuite: making the master-slave synchronization less likely to fail,
by adding sleep-and-retries (max 4 times) if MASTER_POS_WAIT() returns NULL
in sync_with_master and sync_slave_with_master.
The problem showed up only today, in MySQL 5.0 in rpl_server_id2.test,
but may affect 4.x as well, so fixing it here. Note that I am also fixing
5.0 too, with the same exact patch, because I don't want to leave 5.0 broken
until the next 4.0->4.1->5.0 merge.
client/mysqltest.c:
in sync_with_master (and sync_slave_with_master), if MASTER_POS_WAIT() returns NULL,
it may be that the slave SQL thread did not have time to start yes, so we sleep
1 sec and retry, 4 times at most.
mysql-test/r/rpl_server_id2.result:
result update
mysql-test/t/rpl_server_id2.test:
master_slave.inc already drops the table
2004-05-14 23:08:03 +02:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
It may be that the slave SQL thread has not started yet, though START
|
|
|
|
SLAVE has been issued ?
|
|
|
|
*/
|
|
|
|
if (tries++ == 3)
|
|
|
|
{
|
|
|
|
die("line %u: could not sync with master ('%s' returned NULL)",
|
|
|
|
start_lineno, query_buf);
|
|
|
|
}
|
|
|
|
sleep(1); /* So at most we will wait 3 seconds and make 4 tries */
|
|
|
|
mysql_free_result(res);
|
|
|
|
goto wait_for_position;
|
|
|
|
}
|
2002-09-11 05:40:08 +02:00
|
|
|
mysql_free_result(res);
|
|
|
|
last_result=0;
|
2002-08-08 02:12:02 +02:00
|
|
|
if (rpl_parse)
|
2001-06-09 03:24:41 +02:00
|
|
|
mysql_enable_rpl_parse(mysql);
|
2001-12-06 13:10:51 +01:00
|
|
|
|
2001-01-17 13:47:33 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2002-08-29 20:08:54 +02:00
|
|
|
int do_sync_with_master(struct st_query* q)
|
|
|
|
{
|
|
|
|
return do_sync_with_master2(q->first_argument);
|
|
|
|
}
|
2002-08-08 02:12:02 +02:00
|
|
|
|
2001-01-17 13:47:33 +01:00
|
|
|
int do_save_master_pos()
|
|
|
|
{
|
|
|
|
MYSQL_RES* res;
|
|
|
|
MYSQL_ROW row;
|
|
|
|
MYSQL* mysql = &cur_con->mysql;
|
2003-02-14 10:47:41 +01:00
|
|
|
const char *query;
|
2001-06-09 03:24:41 +02:00
|
|
|
int rpl_parse;
|
|
|
|
|
|
|
|
rpl_parse = mysql_rpl_parse_enabled(mysql);
|
|
|
|
mysql_disable_rpl_parse(mysql);
|
2001-12-06 13:10:51 +01:00
|
|
|
|
2003-02-14 10:47:41 +01:00
|
|
|
if (mysql_query(mysql, query= "show master status"))
|
2001-01-17 13:47:33 +01:00
|
|
|
die("At line %u: failed in show master status: %d: %s", start_lineno,
|
|
|
|
mysql_errno(mysql), mysql_error(mysql));
|
|
|
|
|
2002-08-08 02:12:02 +02:00
|
|
|
if (!(last_result =res = mysql_store_result(mysql)))
|
2003-02-14 10:47:41 +01:00
|
|
|
die("line %u: mysql_store_result() retuned NULL for '%s'", start_lineno,
|
|
|
|
query);
|
2002-08-08 02:12:02 +02:00
|
|
|
if (!(row = mysql_fetch_row(res)))
|
2001-01-17 13:47:33 +01:00
|
|
|
die("line %u: empty result in show master status", start_lineno);
|
2002-08-08 02:12:02 +02:00
|
|
|
strnmov(master_pos.file, row[0], sizeof(master_pos.file)-1);
|
|
|
|
master_pos.pos = strtoul(row[1], (char**) 0, 10);
|
2001-10-08 03:58:07 +02:00
|
|
|
mysql_free_result(res); last_result=0;
|
2001-12-06 13:10:51 +01:00
|
|
|
|
2002-08-08 02:12:02 +02:00
|
|
|
if (rpl_parse)
|
2001-06-09 03:24:41 +02:00
|
|
|
mysql_enable_rpl_parse(mysql);
|
2001-12-06 13:10:51 +01:00
|
|
|
|
2001-01-17 13:47:33 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-01-07 23:04:30 +01:00
|
|
|
int do_let(struct st_query* q)
|
2000-12-02 18:11:50 +01:00
|
|
|
{
|
2001-01-07 23:04:30 +01:00
|
|
|
char* p=q->first_argument;
|
|
|
|
char *var_name, *var_name_end, *var_val_start;
|
2000-12-27 02:31:06 +01:00
|
|
|
if (!*p)
|
2000-12-02 18:11:50 +01:00
|
|
|
die("Missing variable name in let\n");
|
|
|
|
var_name = p;
|
2003-03-16 08:20:45 +01:00
|
|
|
while (*p && (*p != '=' || my_isspace(charset_info,*p)))
|
2000-12-02 18:11:50 +01:00
|
|
|
p++;
|
|
|
|
var_name_end = p;
|
2000-12-27 02:31:06 +01:00
|
|
|
if (*p == '=') p++;
|
2003-03-16 08:20:45 +01:00
|
|
|
while (*p && my_isspace(charset_info,*p))
|
2000-12-02 18:11:50 +01:00
|
|
|
p++;
|
|
|
|
var_val_start = p;
|
2001-06-30 21:08:35 +02:00
|
|
|
return var_set(var_name, var_name_end, var_val_start, q->end);
|
2000-12-02 18:11:50 +01:00
|
|
|
}
|
|
|
|
|
2002-05-22 18:11:06 +02:00
|
|
|
int do_rpl_probe(struct st_query* q __attribute__((unused)))
|
2001-06-09 03:24:41 +02:00
|
|
|
{
|
2002-08-24 04:44:16 +02:00
|
|
|
DBUG_ENTER("do_rpl_probe");
|
2002-08-08 02:12:02 +02:00
|
|
|
if (mysql_rpl_probe(&cur_con->mysql))
|
2002-08-24 04:44:16 +02:00
|
|
|
die("Failed in mysql_rpl_probe(): '%s'", mysql_error(&cur_con->mysql));
|
|
|
|
DBUG_RETURN(0);
|
2001-06-09 03:24:41 +02:00
|
|
|
}
|
|
|
|
|
2002-05-22 18:11:06 +02:00
|
|
|
int do_enable_rpl_parse(struct st_query* q __attribute__((unused)))
|
2001-06-09 03:24:41 +02:00
|
|
|
{
|
|
|
|
mysql_enable_rpl_parse(&cur_con->mysql);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2002-05-22 18:11:06 +02:00
|
|
|
int do_disable_rpl_parse(struct st_query* q __attribute__((unused)))
|
2001-06-09 03:24:41 +02:00
|
|
|
{
|
|
|
|
mysql_disable_rpl_parse(&cur_con->mysql);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-08-18 15:04:26 +02:00
|
|
|
int do_sleep(struct st_query* q, my_bool real_sleep)
|
2000-10-10 10:42:07 +02:00
|
|
|
{
|
2002-08-30 11:40:40 +02:00
|
|
|
char *p=q->first_argument;
|
2003-03-16 08:20:45 +01:00
|
|
|
while (*p && my_isspace(charset_info,*p))
|
2002-09-11 05:40:08 +02:00
|
|
|
p++;
|
2000-12-27 02:31:06 +01:00
|
|
|
if (!*p)
|
|
|
|
die("Missing argument in sleep\n");
|
2002-08-18 15:04:26 +02:00
|
|
|
if (opt_sleep && !real_sleep)
|
2002-09-11 05:40:08 +02:00
|
|
|
my_sleep(opt_sleep * 1000000L);
|
2001-08-22 00:45:07 +02:00
|
|
|
else
|
2002-09-11 05:40:08 +02:00
|
|
|
my_sleep((ulong) (atof(p) * 1000000L));
|
2001-08-22 00:45:07 +02:00
|
|
|
return 0;
|
2000-10-10 10:42:07 +02:00
|
|
|
}
|
|
|
|
|
2001-01-07 23:04:30 +01:00
|
|
|
static void get_file_name(char *filename, struct st_query* q)
|
2000-12-27 02:31:06 +01:00
|
|
|
{
|
2001-01-07 23:04:30 +01:00
|
|
|
char* p=q->first_argument;
|
2000-12-27 02:31:06 +01:00
|
|
|
strnmov(filename, p, FN_REFLEN);
|
|
|
|
/* Remove end space */
|
2003-03-16 08:20:45 +01:00
|
|
|
while (p > filename && my_isspace(charset_info,p[-1]))
|
2000-12-27 02:31:06 +01:00
|
|
|
p--;
|
|
|
|
p[0]=0;
|
|
|
|
}
|
|
|
|
|
2000-10-10 10:42:07 +02:00
|
|
|
|
2001-11-04 14:43:41 +01:00
|
|
|
static uint get_ints(uint *to,struct st_query* q)
|
2001-01-15 21:56:44 +01:00
|
|
|
{
|
|
|
|
char* p=q->first_argument;
|
2001-01-16 05:02:05 +01:00
|
|
|
long val;
|
2001-11-04 14:43:41 +01:00
|
|
|
uint count=0;
|
2001-01-16 05:02:05 +01:00
|
|
|
DBUG_ENTER("get_ints");
|
|
|
|
|
2001-01-15 21:56:44 +01:00
|
|
|
if (!*p)
|
|
|
|
die("Missing argument in %s\n", q->query);
|
2001-01-16 05:02:05 +01:00
|
|
|
|
|
|
|
for (; (p=str2int(p,10,(long) INT_MIN, (long) INT_MAX, &val)) ; p++)
|
|
|
|
{
|
2001-11-04 14:43:41 +01:00
|
|
|
count++;
|
2001-01-16 05:02:05 +01:00
|
|
|
*to++= (uint) val;
|
|
|
|
if (*p != ',')
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
*to++=0; /* End of data */
|
2001-11-04 14:43:41 +01:00
|
|
|
DBUG_RETURN(count);
|
2001-01-15 21:56:44 +01:00
|
|
|
}
|
|
|
|
|
2001-02-13 11:39:50 +01:00
|
|
|
/*
|
|
|
|
Get a string; Return ptr to end of string
|
|
|
|
Strings may be surrounded by " or '
|
2001-12-15 03:41:20 +01:00
|
|
|
|
|
|
|
If string is a '$variable', return the value of the variable.
|
2001-02-13 11:39:50 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
2001-12-15 03:41:20 +01:00
|
|
|
static char *get_string(char **to_ptr, char **from_ptr,
|
|
|
|
struct st_query* q)
|
2001-02-13 11:39:50 +01:00
|
|
|
{
|
|
|
|
reg1 char c,sep;
|
2001-12-15 03:41:20 +01:00
|
|
|
char *to= *to_ptr, *from= *from_ptr, *start=to;
|
2001-02-13 11:39:50 +01:00
|
|
|
DBUG_ENTER("get_string");
|
|
|
|
|
|
|
|
/* Find separator */
|
|
|
|
if (*from == '"' || *from == '\'')
|
|
|
|
sep= *from++;
|
|
|
|
else
|
|
|
|
sep=' '; /* Separated with space */
|
|
|
|
|
|
|
|
for ( ; (c=*from) ; from++)
|
|
|
|
{
|
|
|
|
if (c == '\\' && from[1])
|
|
|
|
{ /* Escaped character */
|
|
|
|
/* We can't translate \0 -> ASCII 0 as replace can't handle ASCII 0 */
|
|
|
|
switch (*++from) {
|
|
|
|
case 'n':
|
|
|
|
*to++= '\n';
|
|
|
|
break;
|
|
|
|
case 't':
|
|
|
|
*to++= '\t';
|
|
|
|
break;
|
|
|
|
case 'r':
|
|
|
|
*to++ = '\r';
|
|
|
|
break;
|
|
|
|
case 'b':
|
|
|
|
*to++ = '\b';
|
|
|
|
break;
|
|
|
|
case 'Z': /* ^Z must be escaped on Win32 */
|
|
|
|
*to++='\032';
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
*to++ = *from;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (c == sep)
|
|
|
|
{
|
|
|
|
if (c == ' ' || c != *++from)
|
|
|
|
break; /* Found end of string */
|
|
|
|
*to++=c; /* Copy duplicated separator */
|
|
|
|
}
|
|
|
|
else
|
|
|
|
*to++=c;
|
|
|
|
}
|
|
|
|
if (*from != ' ' && *from)
|
|
|
|
die("Wrong string argument in %s\n", q->query);
|
|
|
|
|
2003-03-16 08:20:45 +01:00
|
|
|
while (my_isspace(charset_info,*from)) /* Point to next string */
|
2001-02-13 11:39:50 +01:00
|
|
|
from++;
|
|
|
|
|
2001-12-15 03:41:20 +01:00
|
|
|
*to =0; /* End of string marker */
|
|
|
|
*to_ptr= to+1; /* Store pointer to end */
|
2001-02-13 11:39:50 +01:00
|
|
|
*from_ptr= from;
|
2001-12-15 03:41:20 +01:00
|
|
|
|
|
|
|
/* Check if this was a variable */
|
|
|
|
if (*start == '$')
|
|
|
|
{
|
|
|
|
const char *end= to;
|
|
|
|
VAR *var=var_get(start, &end, 0, 1);
|
|
|
|
if (var && to == (char*) end+1)
|
|
|
|
{
|
2003-08-22 03:07:40 +02:00
|
|
|
DBUG_PRINT("info",("var: '%s' -> '%s'", start, var->str_val));
|
2001-12-15 03:41:20 +01:00
|
|
|
DBUG_RETURN(var->str_val); /* return found variable value */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DBUG_RETURN(start);
|
2001-02-13 11:39:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
Get arguments for replace. The syntax is:
|
|
|
|
replace from to [from to ...]
|
|
|
|
Where each argument may be quoted with ' or "
|
2001-12-15 03:41:20 +01:00
|
|
|
A argument may also be a variable, in which case the value of the
|
|
|
|
variable is replaced.
|
2001-02-13 11:39:50 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
static void get_replace(struct st_query *q)
|
|
|
|
{
|
|
|
|
uint i;
|
|
|
|
char *from=q->first_argument;
|
2001-02-15 02:43:14 +01:00
|
|
|
char *buff,*start;
|
2001-02-13 11:39:50 +01:00
|
|
|
char word_end_chars[256],*pos;
|
|
|
|
POINTER_ARRAY to_array,from_array;
|
|
|
|
DBUG_ENTER("get_replace");
|
|
|
|
|
2002-09-11 05:40:08 +02:00
|
|
|
free_replace();
|
2001-12-15 03:41:20 +01:00
|
|
|
|
2001-02-13 11:39:50 +01:00
|
|
|
bzero((char*) &to_array,sizeof(to_array));
|
|
|
|
bzero((char*) &from_array,sizeof(from_array));
|
|
|
|
if (!*from)
|
|
|
|
die("Missing argument in %s\n", q->query);
|
2001-02-15 02:43:14 +01:00
|
|
|
start=buff=my_malloc(strlen(from)+1,MYF(MY_WME | MY_FAE));
|
2001-02-13 11:39:50 +01:00
|
|
|
while (*from)
|
|
|
|
{
|
|
|
|
char *to=buff;
|
2001-12-15 03:41:20 +01:00
|
|
|
to=get_string(&buff, &from, q);
|
2001-02-13 11:39:50 +01:00
|
|
|
if (!*from)
|
2001-02-15 02:43:14 +01:00
|
|
|
die("Wrong number of arguments to replace in %s\n", q->query);
|
2001-02-13 11:39:50 +01:00
|
|
|
insert_pointer_name(&from_array,to);
|
2001-12-15 03:41:20 +01:00
|
|
|
to=get_string(&buff, &from, q);
|
2001-02-13 11:39:50 +01:00
|
|
|
insert_pointer_name(&to_array,to);
|
|
|
|
}
|
|
|
|
for (i=1,pos=word_end_chars ; i < 256 ; i++)
|
2003-03-16 08:20:45 +01:00
|
|
|
if (my_isspace(charset_info,i))
|
2002-08-30 11:40:40 +02:00
|
|
|
*pos++= i;
|
2001-02-15 02:43:14 +01:00
|
|
|
*pos=0; /* End pointer */
|
2001-02-13 11:39:50 +01:00
|
|
|
if (!(glob_replace=init_replace((char**) from_array.typelib.type_names,
|
|
|
|
(char**) to_array.typelib.type_names,
|
|
|
|
(uint) from_array.typelib.count,
|
|
|
|
word_end_chars)) ||
|
|
|
|
initialize_replace_buffer())
|
|
|
|
die("Can't initialize replace from %s\n", q->query);
|
|
|
|
free_pointer_array(&from_array);
|
|
|
|
free_pointer_array(&to_array);
|
2001-02-15 02:43:14 +01:00
|
|
|
my_free(start, MYF(0));
|
2001-12-15 03:41:20 +01:00
|
|
|
DBUG_VOID_RETURN;
|
2001-02-13 11:39:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void free_replace()
|
|
|
|
{
|
2001-02-15 02:43:14 +01:00
|
|
|
DBUG_ENTER("free_replace");
|
2002-09-11 05:40:08 +02:00
|
|
|
if (glob_replace)
|
|
|
|
{
|
|
|
|
my_free((char*) glob_replace,MYF(0));
|
|
|
|
glob_replace=0;
|
|
|
|
free_replace_buffer();
|
|
|
|
}
|
2001-02-15 02:43:14 +01:00
|
|
|
DBUG_VOID_RETURN;
|
2001-02-13 11:39:50 +01:00
|
|
|
}
|
|
|
|
|
2002-08-29 20:08:54 +02:00
|
|
|
int select_connection(char *p)
|
2000-10-10 10:42:07 +02:00
|
|
|
{
|
2002-08-29 20:08:54 +02:00
|
|
|
char* name;
|
2000-10-10 10:42:07 +02:00
|
|
|
struct connection *con;
|
2001-01-07 23:04:30 +01:00
|
|
|
DBUG_ENTER("select_connection");
|
|
|
|
DBUG_PRINT("enter",("name: '%s'",p));
|
|
|
|
|
2000-12-27 02:31:06 +01:00
|
|
|
if (!*p)
|
2000-10-10 10:42:07 +02:00
|
|
|
die("Missing connection name in connect\n");
|
|
|
|
name = p;
|
2003-03-16 08:20:45 +01:00
|
|
|
while (*p && !my_isspace(charset_info,*p))
|
2000-10-10 10:42:07 +02:00
|
|
|
p++;
|
|
|
|
*p = 0;
|
2000-12-22 20:10:35 +01:00
|
|
|
|
2001-01-07 23:04:30 +01:00
|
|
|
for (con = cons; con < next_con; con++)
|
|
|
|
{
|
2000-12-27 02:31:06 +01:00
|
|
|
if (!strcmp(con->name, name))
|
2001-01-07 23:04:30 +01:00
|
|
|
{
|
|
|
|
cur_con = con;
|
|
|
|
DBUG_RETURN(0);
|
|
|
|
}
|
|
|
|
}
|
2000-10-10 10:42:07 +02:00
|
|
|
die("connection '%s' not found in connection pool", name);
|
2001-01-07 23:04:30 +01:00
|
|
|
DBUG_RETURN(1); /* Never reached */
|
2000-10-10 10:42:07 +02:00
|
|
|
}
|
|
|
|
|
2001-01-07 23:04:30 +01:00
|
|
|
int close_connection(struct st_query* q)
|
2000-11-26 07:29:01 +01:00
|
|
|
{
|
2001-01-07 23:04:30 +01:00
|
|
|
char* p=q->first_argument, *name;
|
2000-11-26 07:29:01 +01:00
|
|
|
struct connection *con;
|
2001-01-07 23:04:30 +01:00
|
|
|
DBUG_ENTER("close_connection");
|
|
|
|
DBUG_PRINT("enter",("name: '%s'",p));
|
|
|
|
|
2000-12-27 02:31:06 +01:00
|
|
|
if (!*p)
|
2000-11-26 07:29:01 +01:00
|
|
|
die("Missing connection name in connect\n");
|
|
|
|
name = p;
|
2003-03-16 08:20:45 +01:00
|
|
|
while (*p && !my_isspace(charset_info,*p))
|
2000-11-26 07:29:01 +01:00
|
|
|
p++;
|
|
|
|
*p = 0;
|
2000-12-22 20:10:35 +01:00
|
|
|
|
2001-09-22 16:40:59 +02:00
|
|
|
for (con = cons; con < next_con; con++)
|
2001-01-07 23:04:30 +01:00
|
|
|
{
|
2000-12-27 02:31:06 +01:00
|
|
|
if (!strcmp(con->name, name))
|
2001-01-07 23:04:30 +01:00
|
|
|
{
|
2002-12-16 14:33:29 +01:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2001-09-22 16:40:59 +02:00
|
|
|
if (q->type == Q_DIRTY_CLOSE)
|
|
|
|
{
|
|
|
|
if (con->mysql.net.vio)
|
2001-01-20 06:40:16 +01:00
|
|
|
{
|
2001-09-22 16:40:59 +02:00
|
|
|
vio_delete(con->mysql.net.vio);
|
|
|
|
con->mysql.net.vio = 0;
|
2001-01-20 06:40:16 +01:00
|
|
|
}
|
2001-09-22 16:40:59 +02:00
|
|
|
}
|
2002-12-16 14:33:29 +01:00
|
|
|
#endif
|
2001-01-07 23:04:30 +01:00
|
|
|
mysql_close(&con->mysql);
|
|
|
|
DBUG_RETURN(0);
|
|
|
|
}
|
|
|
|
}
|
2000-11-26 07:29:01 +01:00
|
|
|
die("connection '%s' not found in connection pool", name);
|
2001-01-07 23:04:30 +01:00
|
|
|
DBUG_RETURN(1); /* Never reached */
|
2000-11-26 07:29:01 +01:00
|
|
|
}
|
|
|
|
|
2000-10-10 10:42:07 +02:00
|
|
|
|
2001-12-22 14:13:31 +01:00
|
|
|
/*
|
|
|
|
This one now is a hack - we may want to improve in in the
|
2000-10-10 10:42:07 +02:00
|
|
|
future to handle quotes. For now we assume that anything that is not
|
|
|
|
a comma, a space or ) belongs to the argument. space is a chopper, comma or
|
|
|
|
) are delimiters/terminators
|
2000-12-22 20:10:35 +01:00
|
|
|
*/
|
2001-01-07 23:04:30 +01:00
|
|
|
|
2000-10-10 10:42:07 +02:00
|
|
|
char* safe_get_param(char* str, char** arg, const char* msg)
|
|
|
|
{
|
2001-01-07 23:04:30 +01:00
|
|
|
DBUG_ENTER("safe_get_param");
|
2003-03-16 08:20:45 +01:00
|
|
|
while (*str && my_isspace(charset_info,*str))
|
2002-08-30 11:40:40 +02:00
|
|
|
str++;
|
2000-10-10 10:42:07 +02:00
|
|
|
*arg = str;
|
2001-03-25 00:02:26 +01:00
|
|
|
for (; *str && *str != ',' && *str != ')' ; str++)
|
|
|
|
{
|
2003-03-16 08:20:45 +01:00
|
|
|
if (my_isspace(charset_info,*str))
|
2002-08-30 11:40:40 +02:00
|
|
|
*str = 0;
|
2001-03-25 00:02:26 +01:00
|
|
|
}
|
2000-12-27 02:31:06 +01:00
|
|
|
if (!*str)
|
2000-10-10 10:42:07 +02:00
|
|
|
die(msg);
|
2000-12-22 20:10:35 +01:00
|
|
|
|
2000-10-10 10:42:07 +02:00
|
|
|
*str++ = 0;
|
2001-01-07 23:04:30 +01:00
|
|
|
DBUG_RETURN(str);
|
2000-10-10 10:42:07 +02:00
|
|
|
}
|
|
|
|
|
2001-10-04 23:42:25 +02:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2001-10-02 00:13:55 +02:00
|
|
|
void init_manager()
|
|
|
|
{
|
|
|
|
if (!(manager=mysql_manager_init(0)))
|
|
|
|
die("Failed in mysql_manager_init()");
|
|
|
|
if (!mysql_manager_connect(manager,manager_host,manager_user,
|
|
|
|
manager_pass,manager_port))
|
|
|
|
die("Could not connect to MySQL manager: %s(%d)",manager->last_error,
|
|
|
|
manager->last_errno);
|
|
|
|
|
|
|
|
}
|
2001-10-04 23:42:25 +02:00
|
|
|
#endif
|
2001-10-02 00:13:55 +02:00
|
|
|
|
2001-08-20 19:20:47 +02:00
|
|
|
int safe_connect(MYSQL* con, const char* host, const char* user,
|
|
|
|
const char* pass,
|
|
|
|
const char* db, int port, const char* sock)
|
|
|
|
{
|
|
|
|
int con_error = 1;
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < MAX_CON_TRIES; ++i)
|
|
|
|
{
|
2003-12-16 05:09:44 +01:00
|
|
|
if (mysql_real_connect(con, host,user, pass, db, port, sock,
|
|
|
|
CLIENT_MULTI_STATEMENTS))
|
2001-08-20 19:20:47 +02:00
|
|
|
{
|
|
|
|
con_error = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
sleep(CON_RETRY_SLEEP);
|
|
|
|
}
|
|
|
|
return con_error;
|
|
|
|
}
|
|
|
|
|
2001-01-07 23:04:30 +01:00
|
|
|
|
|
|
|
int do_connect(struct st_query* q)
|
2000-10-10 10:42:07 +02:00
|
|
|
{
|
|
|
|
char* con_name, *con_user,*con_pass, *con_host, *con_port_str,
|
|
|
|
*con_db, *con_sock;
|
2001-01-07 23:04:30 +01:00
|
|
|
char* p=q->first_argument;
|
2001-01-04 01:56:38 +01:00
|
|
|
char buff[FN_REFLEN];
|
2001-03-25 00:02:26 +01:00
|
|
|
int con_port;
|
2001-11-18 01:23:46 +01:00
|
|
|
int free_con_sock = 0;
|
2001-12-06 13:10:51 +01:00
|
|
|
|
2001-01-07 23:04:30 +01:00
|
|
|
DBUG_ENTER("do_connect");
|
|
|
|
DBUG_PRINT("enter",("connect: %s",p));
|
2000-10-10 10:42:07 +02:00
|
|
|
|
2000-12-27 02:31:06 +01:00
|
|
|
if (*p != '(')
|
2001-01-07 23:04:30 +01:00
|
|
|
die("Syntax error in connect - expected '(' found '%c'", *p);
|
2000-10-10 10:42:07 +02:00
|
|
|
p++;
|
|
|
|
p = safe_get_param(p, &con_name, "missing connection name");
|
|
|
|
p = safe_get_param(p, &con_host, "missing connection host");
|
|
|
|
p = safe_get_param(p, &con_user, "missing connection user");
|
|
|
|
p = safe_get_param(p, &con_pass, "missing connection password");
|
|
|
|
p = safe_get_param(p, &con_db, "missing connection db");
|
2001-03-25 00:02:26 +01:00
|
|
|
if (!*p || *p == ';') /* Default port and sock */
|
|
|
|
{
|
|
|
|
con_port=port;
|
|
|
|
con_sock=(char*) unix_sock;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2001-11-18 01:23:46 +01:00
|
|
|
VAR* var_port, *var_sock;
|
2001-03-25 00:02:26 +01:00
|
|
|
p = safe_get_param(p, &con_port_str, "missing connection port");
|
2001-11-18 01:23:46 +01:00
|
|
|
if (*con_port_str == '$')
|
|
|
|
{
|
2001-12-15 03:41:20 +01:00
|
|
|
if (!(var_port = var_get(con_port_str, 0, 0, 0)))
|
2001-11-18 01:23:46 +01:00
|
|
|
die("Unknown variable '%s'", con_port_str+1);
|
|
|
|
con_port = var_port->int_val;
|
|
|
|
}
|
|
|
|
else
|
2002-06-11 10:20:31 +02:00
|
|
|
con_port=atoi(con_port_str);
|
2001-03-25 00:02:26 +01:00
|
|
|
p = safe_get_param(p, &con_sock, "missing connection socket");
|
2001-11-18 01:23:46 +01:00
|
|
|
if (*con_sock == '$')
|
|
|
|
{
|
2001-12-15 03:41:20 +01:00
|
|
|
if (!(var_sock = var_get(con_sock, 0, 0, 0)))
|
2001-11-18 01:23:46 +01:00
|
|
|
die("Unknown variable '%s'", con_sock+1);
|
|
|
|
if (!(con_sock = (char*)my_malloc(var_sock->str_val_len+1, MYF(0))))
|
|
|
|
die("Out of memory");
|
|
|
|
free_con_sock = 1;
|
|
|
|
memcpy(con_sock, var_sock->str_val, var_sock->str_val_len);
|
|
|
|
con_sock[var_sock->str_val_len] = 0;
|
|
|
|
}
|
2001-03-25 00:02:26 +01:00
|
|
|
}
|
2001-12-06 13:10:51 +01:00
|
|
|
|
2000-12-27 02:31:06 +01:00
|
|
|
if (next_con == cons_end)
|
2001-02-21 03:39:48 +01:00
|
|
|
die("Connection limit exhausted - increase MAX_CONS in mysqltest.c");
|
2000-10-10 10:42:07 +02:00
|
|
|
|
2000-12-27 02:31:06 +01:00
|
|
|
if (!mysql_init(&next_con->mysql))
|
2000-10-10 10:42:07 +02:00
|
|
|
die("Failed on mysql_init()");
|
2001-10-04 01:44:18 +02:00
|
|
|
if (opt_compress)
|
|
|
|
mysql_options(&next_con->mysql,MYSQL_OPT_COMPRESS,NullS);
|
2002-06-03 23:40:27 +02:00
|
|
|
mysql_options(&next_con->mysql, MYSQL_OPT_LOCAL_INFILE, 0);
|
|
|
|
|
2003-03-11 10:41:53 +01:00
|
|
|
#ifdef HAVE_OPENSSL
|
|
|
|
if (opt_use_ssl)
|
|
|
|
mysql_ssl_set(&next_con->mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca,
|
|
|
|
opt_ssl_capath, opt_ssl_cipher);
|
|
|
|
#endif
|
2001-11-18 01:23:46 +01:00
|
|
|
if (con_sock && !free_con_sock && *con_sock && *con_sock != FN_LIBCHAR)
|
2001-06-05 00:34:04 +02:00
|
|
|
con_sock=fn_format(buff, con_sock, TMPDIR, "",0);
|
2001-03-25 00:02:26 +01:00
|
|
|
if (!con_db[0])
|
|
|
|
con_db=db;
|
2001-12-22 14:13:31 +01:00
|
|
|
/* Special database to allow one to connect without a database name */
|
2002-05-26 15:53:57 +02:00
|
|
|
if (con_db && !strcmp(con_db,"*NO-ONE*"))
|
2001-12-22 14:13:31 +01:00
|
|
|
con_db=0;
|
2003-11-21 00:53:01 +01:00
|
|
|
if ((safe_connect(&next_con->mysql, con_host,
|
|
|
|
con_user, con_pass,
|
|
|
|
con_db, con_port, con_sock ? con_sock: 0)))
|
2000-10-10 10:42:07 +02:00
|
|
|
die("Could not open connection '%s': %s", con_name,
|
|
|
|
mysql_error(&next_con->mysql));
|
|
|
|
|
2000-12-27 02:31:06 +01:00
|
|
|
if (!(next_con->name = my_strdup(con_name, MYF(MY_WME))))
|
2001-01-07 23:04:30 +01:00
|
|
|
die(NullS);
|
2000-10-10 10:42:07 +02:00
|
|
|
cur_con = next_con++;
|
2001-11-18 01:23:46 +01:00
|
|
|
if (free_con_sock)
|
|
|
|
my_free(con_sock, MYF(MY_WME));
|
2001-01-07 23:04:30 +01:00
|
|
|
DBUG_RETURN(0);
|
2000-10-10 10:42:07 +02:00
|
|
|
}
|
|
|
|
|
2002-06-11 10:20:31 +02:00
|
|
|
|
2001-01-07 23:04:30 +01:00
|
|
|
int do_done(struct st_query* q)
|
2000-12-02 18:11:50 +01:00
|
|
|
{
|
|
|
|
q->type = Q_END_BLOCK;
|
2000-12-27 02:31:06 +01:00
|
|
|
if (cur_block == block_stack)
|
2000-12-02 18:11:50 +01:00
|
|
|
die("Stray '}' - end of block before beginning");
|
2001-06-12 01:28:26 +02:00
|
|
|
if (*block_ok--)
|
|
|
|
{
|
2000-12-02 18:11:50 +01:00
|
|
|
parser.current_line = *--cur_block;
|
2001-06-12 01:28:26 +02:00
|
|
|
}
|
2000-12-02 18:11:50 +01:00
|
|
|
else
|
2001-09-28 07:05:54 +02:00
|
|
|
{
|
2002-06-11 10:20:31 +02:00
|
|
|
++parser.current_line;
|
|
|
|
--cur_block;
|
2001-09-28 07:05:54 +02:00
|
|
|
}
|
2000-12-02 18:11:50 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2001-01-07 23:04:30 +01:00
|
|
|
int do_while(struct st_query* q)
|
2000-12-02 18:11:50 +01:00
|
|
|
{
|
2001-01-07 23:04:30 +01:00
|
|
|
char* p=q->first_argument;
|
2001-03-27 19:23:04 +02:00
|
|
|
const char* expr_start, *expr_end;
|
2000-12-02 18:11:50 +01:00
|
|
|
VAR v;
|
2000-12-27 02:31:06 +01:00
|
|
|
if (cur_block == block_stack_end)
|
2002-06-11 10:20:31 +02:00
|
|
|
die("Nesting too deeply");
|
2001-06-12 01:28:26 +02:00
|
|
|
if (!*block_ok)
|
2002-06-11 10:20:31 +02:00
|
|
|
{
|
|
|
|
++false_block_depth;
|
|
|
|
*++block_ok = 0;
|
|
|
|
*cur_block++ = parser.current_line++;
|
|
|
|
return 0;
|
|
|
|
}
|
2001-12-06 13:10:51 +01:00
|
|
|
|
2000-12-02 18:11:50 +01:00
|
|
|
expr_start = strchr(p, '(');
|
2000-12-27 02:31:06 +01:00
|
|
|
if (!expr_start)
|
2000-12-02 18:11:50 +01:00
|
|
|
die("missing '(' in while");
|
|
|
|
expr_end = strrchr(expr_start, ')');
|
2000-12-27 02:31:06 +01:00
|
|
|
if (!expr_end)
|
2000-12-02 18:11:50 +01:00
|
|
|
die("missing ')' in while");
|
2001-09-28 07:05:54 +02:00
|
|
|
var_init(&v,0,0,0,0);
|
2001-03-27 19:23:04 +02:00
|
|
|
eval_expr(&v, ++expr_start, &expr_end);
|
2000-12-22 20:10:35 +01:00
|
|
|
*cur_block++ = parser.current_line++;
|
2000-12-27 02:31:06 +01:00
|
|
|
if (!v.int_val)
|
2002-06-11 10:20:31 +02:00
|
|
|
{
|
|
|
|
*++block_ok = 0;
|
|
|
|
false_block_depth++;
|
|
|
|
}
|
2001-06-12 01:28:26 +02:00
|
|
|
else
|
|
|
|
*++block_ok = 1;
|
2001-09-28 07:05:54 +02:00
|
|
|
var_free(&v);
|
2000-12-22 20:10:35 +01:00
|
|
|
return 0;
|
2000-12-02 18:11:50 +01:00
|
|
|
}
|
|
|
|
|
2000-10-10 10:42:07 +02:00
|
|
|
|
2004-03-19 17:33:38 +01:00
|
|
|
/*
|
|
|
|
Read characters from line buffer or file. This is needed to allow
|
|
|
|
my_ungetc() to buffer MAX_DELIMITER characters for a file
|
|
|
|
|
|
|
|
NOTE:
|
|
|
|
This works as long as one doesn't change files (with 'source file_name')
|
|
|
|
when there is things pushed into the buffer. This should however not
|
|
|
|
happen for any tests in the test suite.
|
|
|
|
*/
|
|
|
|
|
2004-03-20 11:48:14 +01:00
|
|
|
int my_getc(FILE *file)
|
2004-03-19 17:33:38 +01:00
|
|
|
{
|
|
|
|
if (line_buffer_pos == line_buffer)
|
|
|
|
return fgetc(file);
|
2004-03-20 11:48:14 +01:00
|
|
|
return *--line_buffer_pos;
|
2004-03-19 17:33:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void my_ungetc(int c)
|
|
|
|
{
|
2004-03-20 11:48:14 +01:00
|
|
|
*line_buffer_pos++= (char) c;
|
2004-03-19 17:33:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
my_bool end_of_query(int c)
|
2003-12-16 05:09:44 +01:00
|
|
|
{
|
2004-03-30 18:24:28 +02:00
|
|
|
uint i;
|
2004-03-19 17:33:38 +01:00
|
|
|
char tmp[MAX_DELIMITER];
|
2003-12-16 05:09:44 +01:00
|
|
|
|
2004-03-19 17:33:38 +01:00
|
|
|
if (c != *delimiter)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for (i= 1; i < delimiter_length &&
|
|
|
|
(c= my_getc(*cur_file)) == *(delimiter + i);
|
|
|
|
i++)
|
2003-12-16 05:09:44 +01:00
|
|
|
tmp[i]= c;
|
|
|
|
|
|
|
|
if (i == delimiter_length)
|
2004-03-19 17:33:38 +01:00
|
|
|
return 1; /* Found delimiter */
|
|
|
|
|
|
|
|
/* didn't find delimiter, push back things that we read */
|
2004-03-20 11:48:14 +01:00
|
|
|
my_ungetc(c);
|
|
|
|
while (i > 1)
|
|
|
|
my_ungetc(tmp[--i]);
|
2003-12-16 05:09:44 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-10-10 10:42:07 +02:00
|
|
|
int read_line(char* buf, int size)
|
|
|
|
{
|
|
|
|
int c;
|
2003-12-16 05:09:44 +01:00
|
|
|
char* p= buf, *buf_end= buf + size - 1;
|
|
|
|
int no_save= 0;
|
2000-10-10 10:42:07 +02:00
|
|
|
enum {R_NORMAL, R_Q1, R_ESC_Q_Q1, R_ESC_Q_Q2,
|
|
|
|
R_ESC_SLASH_Q1, R_ESC_SLASH_Q2,
|
2003-12-16 05:09:44 +01:00
|
|
|
R_Q2, R_COMMENT, R_LINE_START} state= R_LINE_START;
|
2004-06-21 09:21:20 +02:00
|
|
|
DBUG_ENTER("read_line");
|
2000-12-22 20:10:35 +01:00
|
|
|
|
2000-12-28 02:56:38 +01:00
|
|
|
start_lineno= *lineno;
|
2000-12-27 02:31:06 +01:00
|
|
|
for (; p < buf_end ;)
|
|
|
|
{
|
2003-12-16 05:09:44 +01:00
|
|
|
no_save= 0;
|
2004-03-19 17:33:38 +01:00
|
|
|
c= my_getc(*cur_file);
|
2000-12-27 02:31:06 +01:00
|
|
|
if (feof(*cur_file))
|
2000-10-10 10:42:07 +02:00
|
|
|
{
|
2001-01-07 23:04:30 +01:00
|
|
|
if ((*cur_file) != stdin)
|
2003-12-16 05:09:44 +01:00
|
|
|
my_fclose(*cur_file, MYF(0));
|
2001-10-02 04:53:00 +02:00
|
|
|
cur_file--;
|
|
|
|
lineno--;
|
2000-12-27 02:31:06 +01:00
|
|
|
if (cur_file == file_stack)
|
2004-06-21 09:21:20 +02:00
|
|
|
DBUG_RETURN(1);
|
2001-10-02 04:53:00 +02:00
|
|
|
continue;
|
2000-12-27 02:31:06 +01:00
|
|
|
}
|
2000-12-22 20:10:35 +01:00
|
|
|
|
2000-12-27 02:31:06 +01:00
|
|
|
switch(state) {
|
|
|
|
case R_NORMAL:
|
2000-12-28 02:56:38 +01:00
|
|
|
/* Only accept '{' in the beginning of a line */
|
2004-03-19 17:33:38 +01:00
|
|
|
if (end_of_query(c))
|
|
|
|
{
|
|
|
|
*p= 0;
|
2004-06-21 09:21:20 +02:00
|
|
|
DBUG_RETURN(0);
|
2004-03-19 17:33:38 +01:00
|
|
|
}
|
2000-12-27 02:31:06 +01:00
|
|
|
else if (c == '\'')
|
|
|
|
state = R_Q1;
|
|
|
|
else if (c == '"')
|
|
|
|
state = R_Q2;
|
|
|
|
else if (c == '\n')
|
2000-12-28 02:56:38 +01:00
|
|
|
{
|
2000-12-27 02:31:06 +01:00
|
|
|
state = R_LINE_START;
|
2000-12-28 02:56:38 +01:00
|
|
|
(*lineno)++;
|
|
|
|
}
|
2000-12-27 02:31:06 +01:00
|
|
|
break;
|
|
|
|
case R_COMMENT:
|
|
|
|
if (c == '\n')
|
|
|
|
{
|
2003-12-16 05:09:44 +01:00
|
|
|
*p= 0;
|
2000-12-28 02:56:38 +01:00
|
|
|
(*lineno)++;
|
2004-06-21 09:21:20 +02:00
|
|
|
DBUG_RETURN(0);
|
2000-12-27 02:31:06 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case R_LINE_START:
|
|
|
|
if (c == '#' || c == '-')
|
|
|
|
{
|
|
|
|
state = R_COMMENT;
|
|
|
|
}
|
2003-12-16 05:09:44 +01:00
|
|
|
else if (my_isspace(charset_info, c))
|
2000-12-28 02:56:38 +01:00
|
|
|
{
|
|
|
|
if (c == '\n')
|
|
|
|
start_lineno= ++*lineno; /* Query hasn't started yet */
|
2003-12-16 05:09:44 +01:00
|
|
|
no_save= 1;
|
2000-12-28 02:56:38 +01:00
|
|
|
}
|
2000-12-27 02:31:06 +01:00
|
|
|
else if (c == '}')
|
|
|
|
{
|
2003-12-16 05:09:44 +01:00
|
|
|
*buf++= '}';
|
|
|
|
*buf= 0;
|
2004-06-21 09:21:20 +02:00
|
|
|
DBUG_RETURN(0);
|
2000-12-27 02:31:06 +01:00
|
|
|
}
|
2004-03-19 17:33:38 +01:00
|
|
|
else if (end_of_query(c) || c == '{')
|
2000-12-27 02:31:06 +01:00
|
|
|
{
|
2003-12-16 05:09:44 +01:00
|
|
|
*p= 0;
|
2004-06-21 09:21:20 +02:00
|
|
|
DBUG_RETURN(0);
|
2000-12-27 02:31:06 +01:00
|
|
|
}
|
2001-09-16 06:09:12 +02:00
|
|
|
else if (c == '\'')
|
2003-12-16 05:09:44 +01:00
|
|
|
state= R_Q1;
|
2001-09-16 06:09:12 +02:00
|
|
|
else if (c == '"')
|
2003-12-16 05:09:44 +01:00
|
|
|
state= R_Q2;
|
2000-12-27 02:31:06 +01:00
|
|
|
else
|
2003-12-16 05:09:44 +01:00
|
|
|
state= R_NORMAL;
|
2000-12-27 02:31:06 +01:00
|
|
|
break;
|
2000-12-22 20:10:35 +01:00
|
|
|
|
2000-12-27 02:31:06 +01:00
|
|
|
case R_Q1:
|
|
|
|
if (c == '\'')
|
2003-12-16 05:09:44 +01:00
|
|
|
state= R_ESC_Q_Q1;
|
2000-12-27 02:31:06 +01:00
|
|
|
else if (c == '\\')
|
2003-12-16 05:09:44 +01:00
|
|
|
state= R_ESC_SLASH_Q1;
|
2000-12-27 02:31:06 +01:00
|
|
|
break;
|
|
|
|
case R_ESC_Q_Q1:
|
2004-03-19 17:33:38 +01:00
|
|
|
if (end_of_query(c))
|
|
|
|
{
|
|
|
|
*p= 0;
|
2004-06-21 09:21:20 +02:00
|
|
|
DBUG_RETURN(0);
|
2004-03-19 17:33:38 +01:00
|
|
|
}
|
2000-12-27 02:31:06 +01:00
|
|
|
if (c != '\'')
|
2003-12-16 05:09:44 +01:00
|
|
|
state= R_NORMAL;
|
2000-12-28 02:56:38 +01:00
|
|
|
else
|
2003-12-16 05:09:44 +01:00
|
|
|
state= R_Q1;
|
2000-12-27 02:31:06 +01:00
|
|
|
break;
|
|
|
|
case R_ESC_SLASH_Q1:
|
2003-12-16 05:09:44 +01:00
|
|
|
state= R_Q1;
|
2000-12-27 02:31:06 +01:00
|
|
|
break;
|
2000-10-10 10:42:07 +02:00
|
|
|
|
2000-12-27 02:31:06 +01:00
|
|
|
case R_Q2:
|
|
|
|
if (c == '"')
|
2003-12-16 05:09:44 +01:00
|
|
|
state= R_ESC_Q_Q2;
|
2000-12-27 02:31:06 +01:00
|
|
|
else if (c == '\\')
|
2003-12-16 05:09:44 +01:00
|
|
|
state= R_ESC_SLASH_Q2;
|
2000-12-27 02:31:06 +01:00
|
|
|
break;
|
|
|
|
case R_ESC_Q_Q2:
|
2004-03-19 17:33:38 +01:00
|
|
|
if (end_of_query(c))
|
|
|
|
{
|
|
|
|
*p= 0;
|
2004-06-21 09:21:20 +02:00
|
|
|
DBUG_RETURN(0);
|
2004-03-19 17:33:38 +01:00
|
|
|
}
|
2000-12-27 02:31:06 +01:00
|
|
|
if (c != '"')
|
2003-12-16 05:09:44 +01:00
|
|
|
state= R_NORMAL;
|
2000-12-28 02:56:38 +01:00
|
|
|
else
|
2003-12-16 05:09:44 +01:00
|
|
|
state= R_Q2;
|
2000-12-27 02:31:06 +01:00
|
|
|
break;
|
|
|
|
case R_ESC_SLASH_Q2:
|
2003-12-16 05:09:44 +01:00
|
|
|
state= R_Q2;
|
2000-12-27 02:31:06 +01:00
|
|
|
break;
|
2000-10-10 10:42:07 +02:00
|
|
|
}
|
2000-12-27 02:31:06 +01:00
|
|
|
|
|
|
|
if (!no_save)
|
2003-12-16 05:09:44 +01:00
|
|
|
*p++= c;
|
2000-12-27 02:31:06 +01:00
|
|
|
}
|
2003-12-16 05:09:44 +01:00
|
|
|
*p= 0; /* Always end with \0 */
|
2004-06-21 09:21:20 +02:00
|
|
|
DBUG_RETURN(feof(*cur_file));
|
2000-10-10 10:42:07 +02:00
|
|
|
}
|
|
|
|
|
2004-03-31 02:32:38 +02:00
|
|
|
|
2000-12-28 02:56:38 +01:00
|
|
|
static char read_query_buf[MAX_QUERY];
|
|
|
|
|
2001-01-07 23:04:30 +01:00
|
|
|
int read_query(struct st_query** q_ptr)
|
2000-10-10 10:42:07 +02:00
|
|
|
{
|
2001-01-07 23:04:30 +01:00
|
|
|
char *p = read_query_buf, * p1 ;
|
2001-04-18 03:23:14 +02:00
|
|
|
int expected_errno;
|
2001-01-07 23:04:30 +01:00
|
|
|
struct st_query* q;
|
2004-03-31 02:32:38 +02:00
|
|
|
DBUG_ENTER("read_query");
|
2000-12-28 02:56:38 +01:00
|
|
|
|
2000-12-27 02:31:06 +01:00
|
|
|
if (parser.current_line < parser.read_lines)
|
|
|
|
{
|
2001-01-07 23:04:30 +01:00
|
|
|
get_dynamic(&q_lines, (gptr) q_ptr, parser.current_line) ;
|
2004-03-19 13:52:48 +01:00
|
|
|
DBUG_RETURN(0);
|
2000-12-27 02:31:06 +01:00
|
|
|
}
|
2003-12-16 05:09:44 +01:00
|
|
|
if (!(*q_ptr= q= (struct st_query*) my_malloc(sizeof(*q), MYF(MY_WME))) ||
|
2001-01-15 21:56:44 +01:00
|
|
|
insert_dynamic(&q_lines, (gptr) &q))
|
|
|
|
die(NullS);
|
2000-12-22 20:10:35 +01:00
|
|
|
|
2003-12-16 05:09:44 +01:00
|
|
|
q->record_file[0]= 0;
|
|
|
|
q->require_file= 0;
|
|
|
|
q->first_word_len= 0;
|
2001-01-16 05:02:05 +01:00
|
|
|
memcpy((gptr) q->expected_errno, (gptr) global_expected_errno,
|
|
|
|
sizeof(global_expected_errno));
|
2003-12-16 05:09:44 +01:00
|
|
|
q->expected_errors= global_expected_errors;
|
2004-03-31 02:32:38 +02:00
|
|
|
q->abort_on_error= global_expected_errors == 0;
|
2003-12-16 05:09:44 +01:00
|
|
|
bzero((gptr) global_expected_errno, sizeof(global_expected_errno));
|
2001-11-04 14:43:41 +01:00
|
|
|
global_expected_errors=0;
|
|
|
|
|
2000-12-02 18:11:50 +01:00
|
|
|
q->type = Q_UNKNOWN;
|
2003-12-16 05:09:44 +01:00
|
|
|
q->query_buf= q->query= 0;
|
2000-12-28 02:56:38 +01:00
|
|
|
if (read_line(read_query_buf, sizeof(read_query_buf)))
|
2004-06-21 09:21:20 +02:00
|
|
|
{
|
|
|
|
DBUG_PRINT("warning",("too long query"));
|
2004-03-19 13:52:48 +01:00
|
|
|
DBUG_RETURN(1);
|
2004-06-21 09:21:20 +02:00
|
|
|
}
|
|
|
|
DBUG_PRINT("info", ("query: %s", read_query_buf));
|
2000-12-27 02:31:06 +01:00
|
|
|
if (*p == '#')
|
|
|
|
{
|
|
|
|
q->type = Q_COMMENT;
|
|
|
|
}
|
|
|
|
else if (p[0] == '-' && p[1] == '-')
|
|
|
|
{
|
2003-12-16 05:09:44 +01:00
|
|
|
q->type= Q_COMMENT_WITH_COMMAND;
|
|
|
|
p+= 2; /* To calculate first word */
|
2000-12-27 02:31:06 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (*p == '!')
|
2000-10-10 10:42:07 +02:00
|
|
|
{
|
2003-12-16 05:09:44 +01:00
|
|
|
q->abort_on_error= 0;
|
2000-12-27 02:31:06 +01:00
|
|
|
p++;
|
|
|
|
if (*p == '$')
|
|
|
|
{
|
2003-12-16 05:09:44 +01:00
|
|
|
expected_errno= 0;
|
2000-12-27 02:31:06 +01:00
|
|
|
p++;
|
2003-12-16 05:09:44 +01:00
|
|
|
for (; my_isdigit(charset_info, *p); p++)
|
2000-12-27 02:31:06 +01:00
|
|
|
expected_errno = expected_errno * 10 + *p - '0';
|
2001-01-16 05:02:05 +01:00
|
|
|
q->expected_errno[0] = expected_errno;
|
|
|
|
q->expected_errno[1] = 0;
|
2001-11-04 14:43:41 +01:00
|
|
|
q->expected_errors=1;
|
2000-12-27 02:31:06 +01:00
|
|
|
}
|
2000-10-10 10:42:07 +02:00
|
|
|
}
|
|
|
|
|
2003-12-16 05:09:44 +01:00
|
|
|
while (*p && my_isspace(charset_info, *p))
|
2002-08-30 11:40:40 +02:00
|
|
|
p++ ;
|
2000-12-27 02:31:06 +01:00
|
|
|
if (*p == '@')
|
2000-10-10 10:42:07 +02:00
|
|
|
{
|
|
|
|
p++;
|
|
|
|
p1 = q->record_file;
|
2003-12-16 05:09:44 +01:00
|
|
|
while (!my_isspace(charset_info, *p) &&
|
2001-04-18 03:23:14 +02:00
|
|
|
p1 < q->record_file + sizeof(q->record_file) - 1)
|
2000-10-10 10:42:07 +02:00
|
|
|
*p1++ = *p++;
|
2000-12-22 20:10:35 +01:00
|
|
|
*p1 = 0;
|
2000-10-10 10:42:07 +02:00
|
|
|
}
|
2000-12-27 02:31:06 +01:00
|
|
|
}
|
2003-12-16 05:09:44 +01:00
|
|
|
while (*p && my_isspace(charset_info, *p))
|
2002-08-30 11:40:40 +02:00
|
|
|
p++;
|
2003-12-16 05:09:44 +01:00
|
|
|
if (!(q->query_buf= q->query= my_strdup(p, MYF(MY_WME))))
|
2001-01-07 23:04:30 +01:00
|
|
|
die(NullS);
|
|
|
|
|
|
|
|
/* Calculate first word and first argument */
|
2003-12-16 05:09:44 +01:00
|
|
|
for (p= q->query; *p && !my_isspace(charset_info, *p) ; p++) ;
|
|
|
|
q->first_word_len= (uint) (p - q->query);
|
|
|
|
while (*p && my_isspace(charset_info, *p))
|
2002-08-30 11:40:40 +02:00
|
|
|
p++;
|
2003-12-16 05:09:44 +01:00
|
|
|
q->first_argument= p;
|
|
|
|
q->end= strend(q->query);
|
2000-12-02 18:11:50 +01:00
|
|
|
parser.read_lines++;
|
2004-03-19 13:52:48 +01:00
|
|
|
DBUG_RETURN(0);
|
2000-10-10 10:42:07 +02:00
|
|
|
}
|
|
|
|
|
2002-05-21 23:05:05 +02:00
|
|
|
|
|
|
|
static struct my_option my_long_options[] =
|
|
|
|
{
|
2003-06-13 10:59:02 +02:00
|
|
|
{"debug", '#', "Output debug log. Often this is 'd:t:o,filename'.",
|
2002-05-21 23:05:05 +02:00
|
|
|
0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
|
|
|
|
{"database", 'D', "Database to use.", (gptr*) &db, (gptr*) &db, 0,
|
|
|
|
GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
|
2003-06-13 10:59:02 +02:00
|
|
|
{"basedir", 'b', "Basedir for tests.", (gptr*) &opt_basedir,
|
2002-05-21 23:05:05 +02:00
|
|
|
(gptr*) &opt_basedir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
|
2003-06-13 10:59:02 +02:00
|
|
|
{"big-test", 'B', "Define BIG_TEST to 1.", (gptr*) &opt_big_test,
|
2002-05-21 23:05:05 +02:00
|
|
|
(gptr*) &opt_big_test, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
|
2003-06-13 10:59:02 +02:00
|
|
|
{"compress", 'C', "Use the compressed server/client protocol.",
|
2002-05-21 23:05:05 +02:00
|
|
|
(gptr*) &opt_compress, (gptr*) &opt_compress, 0, GET_BOOL, NO_ARG, 0, 0, 0,
|
|
|
|
0, 0, 0},
|
|
|
|
{"help", '?', "Display this help and exit.", 0, 0, 0, GET_NO_ARG, NO_ARG,
|
|
|
|
0, 0, 0, 0, 0, 0},
|
|
|
|
{"host", 'h', "Connect to host.", (gptr*) &host, (gptr*) &host, 0,
|
|
|
|
GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
|
2003-06-13 10:59:02 +02:00
|
|
|
{"manager-user", OPT_MANAGER_USER, "Undocumented: Used for debugging.",
|
2002-05-21 23:05:05 +02:00
|
|
|
(gptr*) &manager_user, (gptr*) &manager_user, 0, GET_STR, REQUIRED_ARG, 0,
|
|
|
|
0, 0, 0, 0, 0},
|
2003-06-13 10:59:02 +02:00
|
|
|
{"manager-host", OPT_MANAGER_HOST, "Undocumented: Used for debugging.",
|
2002-05-21 23:05:05 +02:00
|
|
|
(gptr*) &manager_host, (gptr*) &manager_host, 0, GET_STR, REQUIRED_ARG,
|
|
|
|
0, 0, 0, 0, 0, 0},
|
2003-06-13 10:59:02 +02:00
|
|
|
{"manager-password", OPT_MANAGER_PASSWD, "Undocumented: Used for debugging.",
|
2002-05-21 23:05:05 +02:00
|
|
|
0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
|
2003-06-13 10:59:02 +02:00
|
|
|
{"manager-port", OPT_MANAGER_PORT, "Undocumented: Used for debugging.",
|
2002-05-21 23:05:05 +02:00
|
|
|
(gptr*) &manager_port, (gptr*) &manager_port, 0, GET_INT, REQUIRED_ARG,
|
|
|
|
MYSQL_MANAGER_PORT, 0, 0, 0, 0, 0},
|
|
|
|
{"manager-wait-timeout", OPT_MANAGER_WAIT_TIMEOUT,
|
2003-06-13 10:59:02 +02:00
|
|
|
"Undocumented: Used for debugging.", (gptr*) &manager_wait_timeout,
|
2002-05-21 23:05:05 +02:00
|
|
|
(gptr*) &manager_wait_timeout, 0, GET_INT, REQUIRED_ARG, 3, 0, 0, 0, 0, 0},
|
|
|
|
{"password", 'p', "Password to use when connecting to server.",
|
|
|
|
0, 0, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
|
|
|
|
{"port", 'P', "Port number to use for connection.", (gptr*) &port,
|
2004-02-26 19:09:49 +01:00
|
|
|
(gptr*) &port, 0, GET_INT, REQUIRED_ARG, MYSQL_PORT, 0, 0, 0, 0, 0},
|
2002-05-21 23:05:05 +02:00
|
|
|
{"quiet", 's', "Suppress all normal output.", (gptr*) &silent,
|
|
|
|
(gptr*) &silent, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
|
|
|
|
{"record", 'r', "Record output of test_file into result file.",
|
|
|
|
0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
|
|
|
|
{"result-file", 'R', "Read/Store result from/in this file.",
|
|
|
|
(gptr*) &result_file, (gptr*) &result_file, 0, GET_STR, REQUIRED_ARG,
|
|
|
|
0, 0, 0, 0, 0, 0},
|
2003-06-13 10:59:02 +02:00
|
|
|
{"server-arg", 'A', "Send enbedded server this as a paramenter.",
|
2002-05-21 23:05:05 +02:00
|
|
|
0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
|
2003-06-13 10:59:02 +02:00
|
|
|
{"server-file", 'F', "Read embedded server arguments from file.",
|
2002-05-21 23:05:05 +02:00
|
|
|
0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
|
|
|
|
{"silent", 's', "Suppress all normal output. Synonym for --quiet.",
|
|
|
|
(gptr*) &silent, (gptr*) &silent, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
|
2002-10-29 21:56:30 +01:00
|
|
|
{"skip-safemalloc", OPT_SKIP_SAFEMALLOC,
|
2003-06-13 10:59:02 +02:00
|
|
|
"Don't use the memory allocation checking.", 0, 0, 0, GET_NO_ARG, NO_ARG,
|
2002-10-29 21:56:30 +01:00
|
|
|
0, 0, 0, 0, 0, 0},
|
2003-06-13 10:59:02 +02:00
|
|
|
{"sleep", 'T', "Sleep always this many seconds on sleep commands.",
|
2002-05-21 23:05:05 +02:00
|
|
|
(gptr*) &opt_sleep, (gptr*) &opt_sleep, 0, GET_INT, REQUIRED_ARG, 0, 0, 0,
|
|
|
|
0, 0, 0},
|
|
|
|
{"socket", 'S', "Socket file to use for connection.",
|
|
|
|
(gptr*) &unix_sock, (gptr*) &unix_sock, 0, GET_STR, REQUIRED_ARG, 0, 0, 0,
|
|
|
|
0, 0, 0},
|
2003-03-11 10:41:53 +01:00
|
|
|
#include "sslopt-longopts.h"
|
2002-05-21 23:05:05 +02:00
|
|
|
{"test-file", 'x', "Read test from/in this file (default stdin).",
|
|
|
|
0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
|
2003-06-13 10:59:02 +02:00
|
|
|
{"tmpdir", 't', "Temporary directory where sockets are put.",
|
2002-05-21 23:05:05 +02:00
|
|
|
0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
|
|
|
|
{"user", 'u', "User for login.", (gptr*) &user, (gptr*) &user, 0, GET_STR,
|
|
|
|
REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
|
|
|
|
{"verbose", 'v', "Write more.", (gptr*) &verbose, (gptr*) &verbose, 0,
|
|
|
|
GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
|
|
|
|
{"version", 'V', "Output version information and exit.",
|
|
|
|
0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
|
|
|
|
{ 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
|
2000-10-10 10:42:07 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
static void print_version(void)
|
|
|
|
{
|
|
|
|
printf("%s Ver %s Distrib %s, for %s (%s)\n",my_progname,MTEST_VERSION,
|
|
|
|
MYSQL_SERVER_VERSION,SYSTEM_TYPE,MACHINE_TYPE);
|
|
|
|
}
|
|
|
|
|
|
|
|
void usage()
|
|
|
|
{
|
|
|
|
print_version();
|
2002-05-21 23:05:05 +02:00
|
|
|
printf("MySQL AB, by Sasha, Matt, Monty & Jani\n");
|
2000-10-10 10:42:07 +02:00
|
|
|
printf("This software comes with ABSOLUTELY NO WARRANTY\n\n");
|
|
|
|
printf("Runs a test against the mysql server and compares output with a results file.\n\n");
|
|
|
|
printf("Usage: %s [OPTIONS] [database] < test_file\n", my_progname);
|
2002-05-21 23:05:05 +02:00
|
|
|
my_print_help(my_long_options);
|
2002-05-22 19:45:19 +02:00
|
|
|
printf(" --no-defaults Don't read default options from any options file.\n");
|
2002-05-21 23:05:05 +02:00
|
|
|
my_print_variables(my_long_options);
|
2000-10-10 10:42:07 +02:00
|
|
|
}
|
|
|
|
|
2002-05-21 23:05:05 +02:00
|
|
|
|
|
|
|
static my_bool
|
|
|
|
get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
|
|
|
|
char *argument)
|
|
|
|
{
|
2002-08-08 02:12:02 +02:00
|
|
|
switch(optid) {
|
2002-05-21 23:05:05 +02:00
|
|
|
case '#':
|
2003-01-07 15:53:10 +01:00
|
|
|
#ifndef DBUG_OFF
|
2002-05-21 23:05:05 +02:00
|
|
|
DBUG_PUSH(argument ? argument : "d:t:S:i:O,/tmp/mysqltest.trace");
|
2003-01-07 15:53:10 +01:00
|
|
|
#endif
|
2002-05-21 23:05:05 +02:00
|
|
|
break;
|
|
|
|
case 'r':
|
|
|
|
record = 1;
|
|
|
|
break;
|
|
|
|
case (int)OPT_MANAGER_PASSWD:
|
|
|
|
my_free(manager_pass,MYF(MY_ALLOW_ZERO_PTR));
|
|
|
|
manager_pass=my_strdup(argument, MYF(MY_FAE));
|
|
|
|
while (*argument) *argument++= 'x'; /* Destroy argument */
|
|
|
|
break;
|
|
|
|
case 'x':
|
|
|
|
{
|
|
|
|
char buff[FN_REFLEN];
|
|
|
|
if (!test_if_hard_path(argument))
|
|
|
|
{
|
|
|
|
strxmov(buff, opt_basedir, argument, NullS);
|
|
|
|
argument= buff;
|
|
|
|
}
|
|
|
|
fn_format(buff, argument, "", "", 4);
|
2003-01-28 07:38:28 +01:00
|
|
|
if (!(*++cur_file = my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(MY_WME))))
|
2002-05-21 23:05:05 +02:00
|
|
|
die("Could not open %s: errno = %d", argument, errno);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'p':
|
|
|
|
if (argument)
|
|
|
|
{
|
|
|
|
my_free(pass, MYF(MY_ALLOW_ZERO_PTR));
|
|
|
|
pass= my_strdup(argument, MYF(MY_FAE));
|
|
|
|
while (*argument) *argument++= 'x'; /* Destroy argument */
|
|
|
|
}
|
|
|
|
else
|
|
|
|
tty_password= 1;
|
|
|
|
break;
|
2003-03-11 10:41:53 +01:00
|
|
|
#include <sslopt-case.h>
|
2002-05-21 23:05:05 +02:00
|
|
|
case 't':
|
|
|
|
strnmov(TMPDIR, argument, sizeof(TMPDIR));
|
|
|
|
break;
|
|
|
|
case 'A':
|
|
|
|
if (!embedded_server_arg_count)
|
|
|
|
{
|
|
|
|
embedded_server_arg_count=1;
|
|
|
|
embedded_server_args[0]= (char*) "";
|
|
|
|
}
|
|
|
|
embedded_server_args[embedded_server_arg_count++]=
|
|
|
|
my_strdup(argument, MYF(MY_FAE));
|
|
|
|
if (embedded_server_arg_count == MAX_SERVER_ARGS ||
|
|
|
|
!embedded_server_args[embedded_server_arg_count-1])
|
|
|
|
{
|
|
|
|
die("Can't use server argument");
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'F':
|
|
|
|
if (read_server_arguments(argument))
|
|
|
|
die(NullS);
|
|
|
|
break;
|
2002-10-29 21:56:30 +01:00
|
|
|
case OPT_SKIP_SAFEMALLOC:
|
|
|
|
#ifdef SAFEMALLOC
|
|
|
|
sf_malloc_quick=1;
|
|
|
|
#endif
|
|
|
|
break;
|
2002-05-21 23:05:05 +02:00
|
|
|
case 'V':
|
|
|
|
print_version();
|
|
|
|
exit(0);
|
|
|
|
case '?':
|
|
|
|
usage();
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-10-10 10:42:07 +02:00
|
|
|
int parse_args(int argc, char **argv)
|
|
|
|
{
|
|
|
|
load_defaults("my",load_default_groups,&argc,&argv);
|
2001-01-07 23:04:30 +01:00
|
|
|
default_argv= argv;
|
2000-12-29 05:58:16 +01:00
|
|
|
|
2003-11-21 00:53:01 +01:00
|
|
|
if ((handle_options(&argc, &argv, my_long_options, get_one_option)))
|
2003-01-07 15:53:10 +01:00
|
|
|
exit(1);
|
2000-10-10 10:42:07 +02:00
|
|
|
|
|
|
|
if (argc > 1)
|
|
|
|
{
|
|
|
|
usage();
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
if (argc == 1)
|
2000-12-27 02:31:06 +01:00
|
|
|
db= *argv;
|
2000-10-10 10:42:07 +02:00
|
|
|
if (tty_password)
|
|
|
|
pass=get_tty_password(NullS);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2000-12-12 20:12:41 +01:00
|
|
|
char* safe_str_append(char* buf, const char* str, int size)
|
2000-10-10 10:42:07 +02:00
|
|
|
{
|
|
|
|
int i,c ;
|
2002-08-08 02:12:02 +02:00
|
|
|
for (i = 0; (c = *str++) && i < size - 1; i++)
|
2000-10-10 10:42:07 +02:00
|
|
|
*buf++ = c;
|
|
|
|
*buf = 0;
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
2000-12-12 20:12:41 +01:00
|
|
|
void str_to_file(const char* fname, char* str, int size)
|
|
|
|
{
|
|
|
|
int fd;
|
2001-10-08 03:58:07 +02:00
|
|
|
char buff[FN_REFLEN];
|
|
|
|
if (!test_if_hard_path(fname))
|
|
|
|
{
|
|
|
|
strxmov(buff, opt_basedir, fname, NullS);
|
|
|
|
fname=buff;
|
|
|
|
}
|
|
|
|
fn_format(buff,fname,"","",4);
|
2001-12-06 13:10:51 +01:00
|
|
|
|
2001-10-08 03:58:07 +02:00
|
|
|
if ((fd = my_open(buff, O_WRONLY | O_CREAT | O_TRUNC,
|
2000-12-28 02:56:38 +01:00
|
|
|
MYF(MY_WME | MY_FFNF))) < 0)
|
2001-10-08 03:58:07 +02:00
|
|
|
die("Could not open %s: errno = %d", buff, errno);
|
2000-12-27 02:31:06 +01:00
|
|
|
if (my_write(fd, (byte*)str, size, MYF(MY_WME|MY_FNABP)))
|
2000-12-12 20:12:41 +01:00
|
|
|
die("write failed");
|
|
|
|
my_close(fd, MYF(0));
|
|
|
|
}
|
|
|
|
|
|
|
|
void reject_dump(const char* record_file, char* buf, int size)
|
2000-10-10 10:42:07 +02:00
|
|
|
{
|
2000-12-15 15:12:31 +01:00
|
|
|
char reject_file[FN_REFLEN];
|
2000-12-28 18:25:41 +01:00
|
|
|
str_to_file(fn_format(reject_file, record_file,"",".reject",2), buf, size);
|
2000-10-10 10:42:07 +02:00
|
|
|
}
|
|
|
|
|
2001-11-05 02:43:00 +01:00
|
|
|
|
|
|
|
/* Append the string to ds, with optional replace */
|
|
|
|
|
2002-04-02 16:54:57 +02:00
|
|
|
static void replace_dynstr_append_mem(DYNAMIC_STRING *ds, const char *val,
|
|
|
|
int len)
|
2001-11-05 02:43:00 +01:00
|
|
|
{
|
|
|
|
if (glob_replace)
|
|
|
|
{
|
|
|
|
len=(int) replace_strings(glob_replace, &out_buff, &out_length, val);
|
|
|
|
if (len == -1)
|
|
|
|
die("Out of memory in replace\n");
|
|
|
|
val=out_buff;
|
|
|
|
}
|
|
|
|
dynstr_append_mem(ds, val, len);
|
|
|
|
}
|
|
|
|
|
2003-06-27 15:29:10 +02:00
|
|
|
|
2002-10-02 12:33:08 +02:00
|
|
|
/*
|
|
|
|
Append all results to the dynamic string separated with '\t'
|
2003-06-27 15:29:10 +02:00
|
|
|
Values may be converted with 'replace_column'
|
2002-10-02 12:33:08 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
static void append_result(DYNAMIC_STRING *ds, MYSQL_RES *res)
|
|
|
|
{
|
|
|
|
MYSQL_ROW row;
|
2003-06-27 15:29:10 +02:00
|
|
|
uint num_fields= mysql_num_fields(res);
|
2004-03-04 18:08:14 +01:00
|
|
|
MYSQL_FIELD *fields= !display_result_vertically ? 0 : mysql_fetch_fields(res);
|
2002-10-02 12:33:08 +02:00
|
|
|
unsigned long *lengths;
|
|
|
|
while ((row = mysql_fetch_row(res)))
|
|
|
|
{
|
2003-06-27 15:29:10 +02:00
|
|
|
uint i;
|
2002-10-02 12:33:08 +02:00
|
|
|
lengths = mysql_fetch_lengths(res);
|
|
|
|
for (i = 0; i < num_fields; i++)
|
|
|
|
{
|
|
|
|
const char *val= row[i];
|
|
|
|
ulonglong len= lengths[i];
|
2003-06-27 15:29:10 +02:00
|
|
|
|
|
|
|
if (i < max_replace_column && replace_column[i])
|
|
|
|
{
|
|
|
|
val= replace_column[i];
|
|
|
|
len= strlen(val);
|
|
|
|
}
|
2002-10-02 12:33:08 +02:00
|
|
|
if (!val)
|
|
|
|
{
|
2003-06-27 15:29:10 +02:00
|
|
|
val= "NULL";
|
|
|
|
len= 4;
|
2002-10-02 12:33:08 +02:00
|
|
|
}
|
2004-03-04 18:08:14 +01:00
|
|
|
if (!display_result_vertically)
|
|
|
|
{
|
|
|
|
if (i)
|
|
|
|
dynstr_append_mem(ds, "\t", 1);
|
|
|
|
replace_dynstr_append_mem(ds, val, len);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dynstr_append(ds, fields[i].name);
|
2002-10-02 12:33:08 +02:00
|
|
|
dynstr_append_mem(ds, "\t", 1);
|
2004-03-04 18:08:14 +01:00
|
|
|
replace_dynstr_append_mem(ds, val, len);
|
|
|
|
dynstr_append_mem(ds, "\n", 1);
|
|
|
|
}
|
2002-10-02 12:33:08 +02:00
|
|
|
}
|
2004-03-04 18:08:14 +01:00
|
|
|
if (!display_result_vertically)
|
|
|
|
dynstr_append_mem(ds, "\n", 1);
|
2002-10-02 12:33:08 +02:00
|
|
|
}
|
2003-06-27 15:29:10 +02:00
|
|
|
free_replace_column();
|
2002-10-02 12:33:08 +02:00
|
|
|
}
|
|
|
|
|
2001-11-05 02:43:00 +01:00
|
|
|
|
2001-06-05 02:38:10 +02:00
|
|
|
/*
|
2002-08-08 02:12:02 +02:00
|
|
|
* flags control the phased/stages of query execution to be performed
|
2001-03-24 03:59:30 +01:00
|
|
|
* if QUERY_SEND bit is on, the query will be sent. If QUERY_REAP is on
|
|
|
|
* the result will be read - for regular query, both bits must be on
|
|
|
|
*/
|
2001-06-05 02:38:10 +02:00
|
|
|
|
2001-01-20 06:40:16 +01:00
|
|
|
int run_query(MYSQL* mysql, struct st_query* q, int flags)
|
2000-10-10 10:42:07 +02:00
|
|
|
{
|
2003-12-16 05:09:44 +01:00
|
|
|
MYSQL_RES* res= 0;
|
2004-03-30 18:24:28 +02:00
|
|
|
uint i;
|
|
|
|
int error= 0, err= 0, counter= 0;
|
2001-01-07 23:04:30 +01:00
|
|
|
DYNAMIC_STRING *ds;
|
|
|
|
DYNAMIC_STRING ds_tmp;
|
2001-03-27 19:23:04 +02:00
|
|
|
DYNAMIC_STRING eval_query;
|
2003-12-18 12:56:13 +01:00
|
|
|
char* query;
|
2003-12-21 18:39:32 +01:00
|
|
|
int query_len, got_error_on_send= 0;
|
2001-01-07 23:04:30 +01:00
|
|
|
DBUG_ENTER("run_query");
|
2004-06-21 09:21:20 +02:00
|
|
|
DBUG_PRINT("enter",("flags: %d", flags));
|
2002-09-11 05:40:08 +02:00
|
|
|
|
2001-12-09 23:08:24 +01:00
|
|
|
if (q->type != Q_EVAL)
|
|
|
|
{
|
|
|
|
query = q->query;
|
|
|
|
query_len = strlen(query);
|
|
|
|
}
|
2001-03-27 19:23:04 +02:00
|
|
|
else
|
2001-12-09 23:08:24 +01:00
|
|
|
{
|
|
|
|
init_dynamic_string(&eval_query, "", 16384, 65536);
|
|
|
|
do_eval(&eval_query, q->query);
|
|
|
|
query = eval_query.str;
|
|
|
|
query_len = eval_query.length;
|
|
|
|
}
|
2002-09-11 05:40:08 +02:00
|
|
|
DBUG_PRINT("enter", ("query: '%-.60s'", query));
|
2001-12-06 13:10:51 +01:00
|
|
|
|
2001-12-09 23:08:24 +01:00
|
|
|
if (q->record_file[0])
|
2000-12-27 02:31:06 +01:00
|
|
|
{
|
2001-01-07 23:04:30 +01:00
|
|
|
init_dynamic_string(&ds_tmp, "", 16384, 65536);
|
2000-12-27 02:31:06 +01:00
|
|
|
ds = &ds_tmp;
|
|
|
|
}
|
2001-01-07 23:04:30 +01:00
|
|
|
else
|
|
|
|
ds= &ds_res;
|
2001-12-06 13:10:51 +01:00
|
|
|
|
2003-12-21 18:39:32 +01:00
|
|
|
if (flags & QUERY_SEND)
|
|
|
|
{
|
|
|
|
got_error_on_send= mysql_send_query(mysql, query, query_len);
|
|
|
|
if (got_error_on_send && !q->expected_errno[0])
|
|
|
|
die("At line %u: unable to send query '%s' (mysql_errno=%d , errno=%d)",
|
|
|
|
start_lineno, query, mysql_errno(mysql), errno);
|
|
|
|
}
|
2001-12-06 13:10:51 +01:00
|
|
|
|
2003-12-16 05:09:44 +01:00
|
|
|
do
|
2000-12-27 02:31:06 +01:00
|
|
|
{
|
2003-12-16 05:09:44 +01:00
|
|
|
if ((flags & QUERY_SEND) && !disable_query_log && !counter)
|
2001-10-08 03:58:07 +02:00
|
|
|
{
|
2003-12-16 05:09:44 +01:00
|
|
|
replace_dynstr_append_mem(ds,query, query_len);
|
2003-12-18 12:56:13 +01:00
|
|
|
dynstr_append_mem(ds, delimiter, delimiter_length);
|
|
|
|
dynstr_append_mem(ds, "\n", 1);
|
2001-10-08 03:58:07 +02:00
|
|
|
}
|
2003-12-16 05:09:44 +01:00
|
|
|
if (!(flags & QUERY_REAP))
|
|
|
|
DBUG_RETURN(0);
|
|
|
|
|
2003-12-21 18:39:32 +01:00
|
|
|
if (got_error_on_send ||
|
|
|
|
(!counter && (*mysql->methods->read_query_result)(mysql)) ||
|
|
|
|
(!(last_result= res= mysql_store_result(mysql)) &&
|
|
|
|
mysql_field_count(mysql)))
|
2000-10-10 10:42:07 +02:00
|
|
|
{
|
2003-12-16 05:09:44 +01:00
|
|
|
if (q->require_file)
|
2001-01-16 05:02:05 +01:00
|
|
|
{
|
2003-12-16 05:09:44 +01:00
|
|
|
abort_not_supported_test();
|
|
|
|
}
|
|
|
|
if (q->abort_on_error)
|
|
|
|
die("At line %u: query '%s' failed: %d: %s", start_lineno, query,
|
|
|
|
mysql_errno(mysql), mysql_error(mysql));
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for (i=0 ; (uint) i < q->expected_errors ; i++)
|
2001-09-28 07:05:54 +02:00
|
|
|
{
|
2003-12-16 05:09:44 +01:00
|
|
|
if ((q->expected_errno[i] == mysql_errno(mysql)))
|
2001-10-08 06:24:04 +02:00
|
|
|
{
|
2003-12-16 05:09:44 +01:00
|
|
|
if (i == 0 && q->expected_errors == 1)
|
|
|
|
{
|
|
|
|
/* Only log error if there is one possible error */
|
|
|
|
dynstr_append_mem(ds,"ERROR ",6);
|
|
|
|
replace_dynstr_append_mem(ds, mysql_sqlstate(mysql),
|
|
|
|
strlen(mysql_sqlstate(mysql)));
|
|
|
|
dynstr_append_mem(ds, ": ", 2);
|
|
|
|
replace_dynstr_append_mem(ds,mysql_error(mysql),
|
|
|
|
strlen(mysql_error(mysql)));
|
|
|
|
dynstr_append_mem(ds,"\n",1);
|
|
|
|
}
|
|
|
|
/* Don't log error if we may not get an error */
|
|
|
|
else if (q->expected_errno[0] != 0)
|
|
|
|
dynstr_append(ds,"Got one of the listed errors\n");
|
|
|
|
goto end; /* Ok */
|
2001-10-08 06:24:04 +02:00
|
|
|
}
|
2001-09-28 07:05:54 +02:00
|
|
|
}
|
2004-03-31 02:32:38 +02:00
|
|
|
DBUG_PRINT("info",("i: %d expected_errors: %d", i,
|
|
|
|
q->expected_errors));
|
2003-12-16 05:09:44 +01:00
|
|
|
dynstr_append_mem(ds, "ERROR ",6);
|
|
|
|
replace_dynstr_append_mem(ds, mysql_sqlstate(mysql),
|
|
|
|
strlen(mysql_sqlstate(mysql)));
|
|
|
|
dynstr_append_mem(ds,": ",2);
|
|
|
|
replace_dynstr_append_mem(ds, mysql_error(mysql),
|
|
|
|
strlen(mysql_error(mysql)));
|
|
|
|
dynstr_append_mem(ds,"\n",1);
|
|
|
|
if (i)
|
|
|
|
{
|
|
|
|
verbose_msg("query '%s' failed with wrong errno %d instead of %d...",
|
|
|
|
q->query, mysql_errno(mysql), q->expected_errno[0]);
|
|
|
|
error= 1;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
verbose_msg("query '%s' failed: %d: %s", q->query, mysql_errno(mysql),
|
|
|
|
mysql_error(mysql));
|
|
|
|
/*
|
|
|
|
if we do not abort on error, failure to run the query does
|
|
|
|
not fail the whole test case
|
|
|
|
*/
|
2000-12-27 02:31:06 +01:00
|
|
|
goto end;
|
|
|
|
}
|
2003-12-16 05:09:44 +01:00
|
|
|
/*{
|
|
|
|
verbose_msg("failed in mysql_store_result for query '%s' (%d)", query,
|
|
|
|
mysql_errno(mysql));
|
|
|
|
error = 1;
|
|
|
|
goto end;
|
|
|
|
}*/
|
2000-10-18 19:57:02 +02:00
|
|
|
}
|
2003-12-16 05:09:44 +01:00
|
|
|
|
|
|
|
if (q->expected_errno[0])
|
|
|
|
{
|
2001-04-15 20:14:40 +02:00
|
|
|
error = 1;
|
2003-12-16 05:09:44 +01:00
|
|
|
verbose_msg("query '%s' succeeded - should have failed with errno %d...",
|
|
|
|
q->query, q->expected_errno[0]);
|
2001-04-15 20:14:40 +02:00
|
|
|
goto end;
|
2003-12-16 05:09:44 +01:00
|
|
|
}
|
2000-12-22 20:10:35 +01:00
|
|
|
|
2003-12-16 05:09:44 +01:00
|
|
|
if (!disable_result_log)
|
2000-12-27 02:31:06 +01:00
|
|
|
{
|
2003-12-16 05:09:44 +01:00
|
|
|
if (res)
|
2003-01-06 00:48:59 +01:00
|
|
|
{
|
2004-03-30 18:24:28 +02:00
|
|
|
MYSQL_FIELD *field, *field_end;
|
|
|
|
uint num_fields= mysql_num_fields(res);
|
2003-12-16 05:09:44 +01:00
|
|
|
|
2004-03-30 18:24:28 +02:00
|
|
|
if (display_metadata)
|
|
|
|
{
|
2004-04-06 16:57:33 +02:00
|
|
|
dynstr_append(ds,"Catalog\tDatabase\tTable\tTable_alias\tColumn\tColumn_alias\tName\tType\tLength\tMax length\tIs_null\tFlags\tDecimals\tCharsetnr\n");
|
2004-03-30 18:24:28 +02:00
|
|
|
for (field= mysql_fetch_fields(res), field_end= field+num_fields ;
|
|
|
|
field < field_end ;
|
|
|
|
field++)
|
|
|
|
{
|
|
|
|
char buff[22];
|
|
|
|
dynstr_append_mem(ds, field->catalog, field->catalog_length);
|
|
|
|
dynstr_append_mem(ds, "\t", 1);
|
|
|
|
dynstr_append_mem(ds, field->db, field->db_length);
|
|
|
|
dynstr_append_mem(ds, "\t", 1);
|
|
|
|
dynstr_append_mem(ds, field->org_table, field->org_table_length);
|
|
|
|
dynstr_append_mem(ds, "\t", 1);
|
|
|
|
dynstr_append_mem(ds, field->table, field->table_length);
|
|
|
|
dynstr_append_mem(ds, "\t", 1);
|
|
|
|
dynstr_append_mem(ds, field->org_name, field->org_name_length);
|
|
|
|
dynstr_append_mem(ds, "\t", 1);
|
|
|
|
dynstr_append_mem(ds, field->name, field->name_length);
|
|
|
|
dynstr_append_mem(ds, "\t", 1);
|
|
|
|
int10_to_str((int) field->type, buff, 10);
|
|
|
|
dynstr_append(ds, buff);
|
|
|
|
dynstr_append_mem(ds, "\t", 1);
|
|
|
|
int10_to_str((int) field->length, buff, 10);
|
|
|
|
dynstr_append(ds, buff);
|
|
|
|
dynstr_append_mem(ds, "\t", 1);
|
|
|
|
int10_to_str((int) field->max_length, buff, 10);
|
|
|
|
dynstr_append(ds, buff);
|
|
|
|
dynstr_append_mem(ds, "\t", 1);
|
|
|
|
dynstr_append_mem(ds, (char*) (IS_NOT_NULL(field->flags) ?
|
|
|
|
"N" : "Y"), 1);
|
|
|
|
dynstr_append_mem(ds, "\t", 1);
|
|
|
|
|
|
|
|
int10_to_str((int) field->flags, buff, 10);
|
|
|
|
dynstr_append(ds, buff);
|
|
|
|
dynstr_append_mem(ds, "\t", 1);
|
|
|
|
int10_to_str((int) field->decimals, buff, 10);
|
|
|
|
dynstr_append(ds, buff);
|
2004-04-06 16:57:33 +02:00
|
|
|
dynstr_append_mem(ds, "\t", 1);
|
|
|
|
int10_to_str((int) field->charsetnr, buff, 10);
|
|
|
|
dynstr_append(ds, buff);
|
2004-03-30 18:24:28 +02:00
|
|
|
dynstr_append_mem(ds, "\n", 1);
|
|
|
|
}
|
|
|
|
}
|
2004-03-04 18:08:14 +01:00
|
|
|
if (!display_result_vertically)
|
2003-12-16 05:09:44 +01:00
|
|
|
{
|
2004-03-30 18:24:28 +02:00
|
|
|
field= mysql_fetch_fields(res);
|
2004-03-04 18:08:14 +01:00
|
|
|
for (i = 0; i < num_fields; i++)
|
|
|
|
{
|
|
|
|
if (i)
|
|
|
|
dynstr_append_mem(ds, "\t", 1);
|
2004-03-30 18:24:28 +02:00
|
|
|
replace_dynstr_append_mem(ds, field[i].name,
|
|
|
|
strlen(field[i].name));
|
2004-03-04 18:08:14 +01:00
|
|
|
}
|
|
|
|
dynstr_append_mem(ds, "\n", 1);
|
2003-12-16 05:09:44 +01:00
|
|
|
}
|
|
|
|
append_result(ds, res);
|
2003-01-06 00:48:59 +01:00
|
|
|
}
|
2000-10-10 10:42:07 +02:00
|
|
|
|
2003-12-16 05:09:44 +01:00
|
|
|
/* Add all warnings to the result */
|
|
|
|
if (!disable_warnings && mysql_warning_count(mysql))
|
2003-01-06 00:48:59 +01:00
|
|
|
{
|
2003-12-16 05:09:44 +01:00
|
|
|
MYSQL_RES *warn_res=0;
|
|
|
|
uint count= mysql_warning_count(mysql);
|
|
|
|
if (!mysql_real_query(mysql, "SHOW WARNINGS", 13))
|
|
|
|
{
|
|
|
|
warn_res= mysql_store_result(mysql);
|
|
|
|
}
|
|
|
|
if (!warn_res)
|
|
|
|
verbose_msg("Warning count is %u but didn't get any warnings\n",
|
|
|
|
count);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dynstr_append_mem(ds, "Warnings:\n", 10);
|
|
|
|
append_result(ds, warn_res);
|
|
|
|
mysql_free_result(warn_res);
|
|
|
|
}
|
2003-01-06 00:48:59 +01:00
|
|
|
}
|
2004-03-15 18:36:16 +01:00
|
|
|
if (!disable_info)
|
2003-01-06 00:48:59 +01:00
|
|
|
{
|
2004-03-15 18:36:16 +01:00
|
|
|
char buf[40];
|
2004-03-18 00:48:46 +01:00
|
|
|
sprintf(buf,"affected rows: %lu\n",(ulong) mysql_affected_rows(mysql));
|
2004-03-15 18:36:16 +01:00
|
|
|
dynstr_append(ds, buf);
|
|
|
|
if (mysql_info(mysql))
|
|
|
|
{
|
|
|
|
dynstr_append(ds, "info: ");
|
|
|
|
dynstr_append(ds, mysql_info(mysql));
|
|
|
|
dynstr_append_mem(ds, "\n", 1);
|
|
|
|
}
|
2003-01-06 00:48:59 +01:00
|
|
|
}
|
2003-01-04 14:37:20 +01:00
|
|
|
}
|
2003-01-06 00:48:59 +01:00
|
|
|
|
2003-12-16 05:09:44 +01:00
|
|
|
if (record)
|
|
|
|
{
|
|
|
|
if (!q->record_file[0] && !result_file)
|
|
|
|
die("At line %u: Missing result file", start_lineno);
|
|
|
|
if (!result_file)
|
|
|
|
str_to_file(q->record_file, ds->str, ds->length);
|
|
|
|
}
|
|
|
|
else if (q->record_file[0])
|
|
|
|
{
|
|
|
|
error = check_result(ds, q->record_file, q->require_file);
|
|
|
|
}
|
|
|
|
if (res)
|
|
|
|
mysql_free_result(res);
|
|
|
|
last_result= 0;
|
|
|
|
counter++;
|
|
|
|
} while (!(err= mysql_next_result(mysql)));
|
|
|
|
if (err >= 1)
|
|
|
|
mysql_error(mysql);
|
2000-12-22 20:10:35 +01:00
|
|
|
|
2000-12-27 02:31:06 +01:00
|
|
|
end:
|
2004-03-20 11:48:14 +01:00
|
|
|
free_replace();
|
2001-10-08 03:58:07 +02:00
|
|
|
last_result=0;
|
2001-01-07 23:04:30 +01:00
|
|
|
if (ds == &ds_tmp)
|
|
|
|
dynstr_free(&ds_tmp);
|
2002-08-08 02:12:02 +02:00
|
|
|
if (q->type == Q_EVAL)
|
2001-03-27 19:23:04 +02:00
|
|
|
dynstr_free(&eval_query);
|
2001-01-07 23:04:30 +01:00
|
|
|
DBUG_RETURN(error);
|
2000-10-10 10:42:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-01-07 23:04:30 +01:00
|
|
|
void get_query_type(struct st_query* q)
|
2000-12-02 18:11:50 +01:00
|
|
|
{
|
2000-12-27 02:31:06 +01:00
|
|
|
char save;
|
|
|
|
uint type;
|
2004-03-19 13:52:48 +01:00
|
|
|
DBUG_ENTER("get_query_type");
|
|
|
|
|
2001-01-07 23:04:30 +01:00
|
|
|
if (*q->query == '}')
|
2000-12-27 02:31:06 +01:00
|
|
|
{
|
|
|
|
q->type = Q_END_BLOCK;
|
2004-03-19 13:52:48 +01:00
|
|
|
DBUG_VOID_RETURN;
|
2000-12-27 02:31:06 +01:00
|
|
|
}
|
|
|
|
if (q->type != Q_COMMENT_WITH_COMMAND)
|
|
|
|
q->type = Q_QUERY;
|
|
|
|
|
2001-01-07 23:04:30 +01:00
|
|
|
save=q->query[q->first_word_len];
|
|
|
|
q->query[q->first_word_len]=0;
|
2001-02-15 02:43:14 +01:00
|
|
|
type=find_type(q->query, &command_typelib, 1+2);
|
2001-01-07 23:04:30 +01:00
|
|
|
q->query[q->first_word_len]=save;
|
2000-12-27 02:31:06 +01:00
|
|
|
if (type > 0)
|
2001-04-18 03:23:14 +02:00
|
|
|
q->type=(enum enum_commands) type; /* Found command */
|
2004-03-19 13:52:48 +01:00
|
|
|
DBUG_VOID_RETURN;
|
2000-12-02 18:11:50 +01:00
|
|
|
}
|
2000-10-10 10:42:07 +02:00
|
|
|
|
2004-03-19 13:52:48 +01:00
|
|
|
|
2003-05-21 20:39:58 +02:00
|
|
|
static byte *get_var_key(const byte* var, uint* len,
|
2001-04-18 07:42:24 +02:00
|
|
|
my_bool __attribute__((unused)) t)
|
2001-04-18 07:06:00 +02:00
|
|
|
{
|
|
|
|
register char* key;
|
|
|
|
key = ((VAR*)var)->name;
|
|
|
|
*len = ((VAR*)var)->name_len;
|
|
|
|
return (byte*)key;
|
|
|
|
}
|
|
|
|
|
2003-05-21 20:39:58 +02:00
|
|
|
static VAR *var_init(VAR *v, const char *name, int name_len, const char *val,
|
2001-04-18 07:06:00 +02:00
|
|
|
int val_len)
|
|
|
|
{
|
|
|
|
int val_alloc_len;
|
2003-05-21 20:39:58 +02:00
|
|
|
VAR *tmp_var;
|
2001-12-15 03:41:20 +01:00
|
|
|
if (!name_len && name)
|
2001-04-18 07:06:00 +02:00
|
|
|
name_len = strlen(name);
|
2001-12-15 03:41:20 +01:00
|
|
|
if (!val_len && val)
|
2001-04-18 07:06:00 +02:00
|
|
|
val_len = strlen(val) ;
|
|
|
|
val_alloc_len = val_len + 16; /* room to grow */
|
2002-08-08 02:12:02 +02:00
|
|
|
if (!(tmp_var=v) && !(tmp_var = (VAR*)my_malloc(sizeof(*tmp_var)
|
2001-12-15 03:41:20 +01:00
|
|
|
+ name_len, MYF(MY_WME))))
|
2001-04-18 07:06:00 +02:00
|
|
|
die("Out of memory");
|
2001-12-06 13:10:51 +01:00
|
|
|
|
2001-12-15 03:41:20 +01:00
|
|
|
tmp_var->name = (name) ? (char*) tmp_var + sizeof(*tmp_var) : 0;
|
2002-08-08 02:12:02 +02:00
|
|
|
tmp_var->alloced = (v == 0);
|
2001-07-12 04:59:17 +02:00
|
|
|
|
2001-12-15 03:41:20 +01:00
|
|
|
if (!(tmp_var->str_val = my_malloc(val_alloc_len+1, MYF(MY_WME))))
|
2001-06-30 21:08:35 +02:00
|
|
|
die("Out of memory");
|
2001-12-06 13:10:51 +01:00
|
|
|
|
2001-04-18 07:06:00 +02:00
|
|
|
memcpy(tmp_var->name, name, name_len);
|
2001-12-15 03:41:20 +01:00
|
|
|
if (val)
|
|
|
|
{
|
|
|
|
memcpy(tmp_var->str_val, val, val_len);
|
|
|
|
tmp_var->str_val[val_len]=0;
|
|
|
|
}
|
2001-04-18 07:06:00 +02:00
|
|
|
tmp_var->name_len = name_len;
|
|
|
|
tmp_var->str_val_len = val_len;
|
|
|
|
tmp_var->alloced_len = val_alloc_len;
|
2001-07-12 04:59:17 +02:00
|
|
|
tmp_var->int_val = (val) ? atoi(val) : 0;
|
2001-04-18 07:06:00 +02:00
|
|
|
tmp_var->int_dirty = 0;
|
|
|
|
return tmp_var;
|
|
|
|
}
|
|
|
|
|
2003-05-21 20:39:58 +02:00
|
|
|
static void var_free(void *v)
|
2001-04-18 07:06:00 +02:00
|
|
|
{
|
2001-08-22 00:45:07 +02:00
|
|
|
my_free(((VAR*) v)->str_val, MYF(MY_WME));
|
2001-09-28 07:05:54 +02:00
|
|
|
if (((VAR*)v)->alloced)
|
|
|
|
my_free((char*) v, MYF(MY_WME));
|
2001-04-18 07:06:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-06-11 18:26:13 +02:00
|
|
|
static VAR* var_from_env(const char *name, const char *def_val)
|
2001-04-18 07:06:00 +02:00
|
|
|
{
|
2003-05-21 20:39:58 +02:00
|
|
|
const char *tmp;
|
|
|
|
VAR *v;
|
2001-12-15 03:41:20 +01:00
|
|
|
if (!(tmp = getenv(name)))
|
2001-04-18 07:06:00 +02:00
|
|
|
tmp = def_val;
|
2001-12-06 13:10:51 +01:00
|
|
|
|
2002-08-08 02:12:02 +02:00
|
|
|
v = var_init(0, name, 0, tmp, 0);
|
2003-09-19 11:44:31 +02:00
|
|
|
my_hash_insert(&var_hash, (byte*)v);
|
2004-06-11 18:26:13 +02:00
|
|
|
return v;
|
2000-12-02 18:11:50 +01:00
|
|
|
}
|
2000-10-10 10:42:07 +02:00
|
|
|
|
2001-06-24 21:11:00 +02:00
|
|
|
|
2003-05-21 20:39:58 +02:00
|
|
|
static void init_var_hash(MYSQL *mysql)
|
2001-04-18 07:06:00 +02:00
|
|
|
{
|
2003-05-21 20:39:58 +02:00
|
|
|
VAR *v;
|
2002-10-29 21:56:30 +01:00
|
|
|
DBUG_ENTER("init_var_hash");
|
2003-03-16 08:20:45 +01:00
|
|
|
if (hash_init(&var_hash, charset_info,
|
2002-03-14 18:44:42 +01:00
|
|
|
1024, 0, 0, get_var_key, var_free, MYF(0)))
|
2001-04-18 07:06:00 +02:00
|
|
|
die("Variable hash initialization failed");
|
2004-06-11 18:26:13 +02:00
|
|
|
if (opt_big_test)
|
|
|
|
my_hash_insert(&var_hash, (byte*) var_init(0,"BIG_TEST", 0, "1",0));
|
2003-05-21 20:39:58 +02:00
|
|
|
v= var_init(0,"MAX_TABLES", 0, (sizeof(ulong) == 4) ? "31" : "62",0);
|
2003-09-19 11:44:31 +02:00
|
|
|
my_hash_insert(&var_hash, (byte*) v);
|
2003-05-21 20:39:58 +02:00
|
|
|
v= var_init(0,"SERVER_VERSION", 0, mysql_get_server_info(mysql), 0);
|
2003-09-19 11:44:31 +02:00
|
|
|
my_hash_insert(&var_hash, (byte*) v);
|
2003-05-21 20:39:58 +02:00
|
|
|
|
2002-10-29 21:56:30 +01:00
|
|
|
DBUG_VOID_RETURN;
|
2001-04-18 07:06:00 +02:00
|
|
|
}
|
2000-12-27 02:31:06 +01:00
|
|
|
|
2001-09-17 03:41:22 +02:00
|
|
|
|
2003-05-21 20:39:58 +02:00
|
|
|
int main(int argc, char **argv)
|
2000-10-10 10:42:07 +02:00
|
|
|
{
|
|
|
|
int error = 0;
|
2003-05-21 20:39:58 +02:00
|
|
|
struct st_query *q;
|
2001-03-25 00:02:26 +01:00
|
|
|
my_bool require_file=0, q_send_flag=0;
|
2000-12-27 02:31:06 +01:00
|
|
|
char save_file[FN_REFLEN];
|
2001-01-07 23:04:30 +01:00
|
|
|
MY_INIT(argv[0]);
|
2003-12-16 15:03:39 +01:00
|
|
|
{
|
2001-10-02 04:53:00 +02:00
|
|
|
DBUG_ENTER("main");
|
|
|
|
DBUG_PROCESS(argv[0]);
|
2001-01-07 23:04:30 +01:00
|
|
|
|
2000-12-27 02:31:06 +01:00
|
|
|
save_file[0]=0;
|
2001-01-04 01:56:38 +01:00
|
|
|
TMPDIR[0]=0;
|
2000-10-10 10:42:07 +02:00
|
|
|
memset(cons, 0, sizeof(cons));
|
|
|
|
cons_end = cons + MAX_CONS;
|
|
|
|
next_con = cons + 1;
|
|
|
|
cur_con = cons;
|
2001-12-06 13:10:51 +01:00
|
|
|
|
2000-10-10 10:42:07 +02:00
|
|
|
memset(file_stack, 0, sizeof(file_stack));
|
2001-01-17 13:47:33 +01:00
|
|
|
memset(&master_pos, 0, sizeof(master_pos));
|
2000-10-10 10:42:07 +02:00
|
|
|
file_stack_end = file_stack + MAX_INCLUDE_DEPTH;
|
|
|
|
cur_file = file_stack;
|
2000-12-28 02:56:38 +01:00
|
|
|
lineno = lineno_stack;
|
2002-04-28 23:22:37 +02:00
|
|
|
my_init_dynamic_array(&q_lines, sizeof(struct st_query*), INIT_Q_LINES,
|
2000-12-02 18:11:50 +01:00
|
|
|
INIT_Q_LINES);
|
|
|
|
memset(block_stack, 0, sizeof(block_stack));
|
|
|
|
block_stack_end = block_stack + BLOCK_STACK_DEPTH;
|
2001-06-12 01:28:26 +02:00
|
|
|
memset(block_ok_stack, 0, sizeof(block_stack));
|
2000-12-02 18:11:50 +01:00
|
|
|
cur_block = block_stack;
|
2001-06-12 01:28:26 +02:00
|
|
|
block_ok = block_ok_stack;
|
|
|
|
*block_ok = 1;
|
2001-01-07 23:04:30 +01:00
|
|
|
init_dynamic_string(&ds_res, "", 0, 65536);
|
2000-10-10 10:42:07 +02:00
|
|
|
parse_args(argc, argv);
|
2001-10-08 03:58:07 +02:00
|
|
|
if (mysql_server_init(embedded_server_arg_count,
|
|
|
|
embedded_server_args,
|
|
|
|
(char**) embedded_server_groups))
|
2001-10-02 04:53:00 +02:00
|
|
|
die("Can't initialize MySQL server");
|
|
|
|
if (cur_file == file_stack)
|
|
|
|
*++cur_file = stdin;
|
2000-12-28 02:56:38 +01:00
|
|
|
*lineno=1;
|
2001-10-10 23:36:35 +02:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
|
|
|
if (manager_host)
|
|
|
|
init_manager();
|
2001-10-04 23:42:25 +02:00
|
|
|
#endif
|
2000-12-27 02:31:06 +01:00
|
|
|
if (!( mysql_init(&cur_con->mysql)))
|
2000-10-10 10:42:07 +02:00
|
|
|
die("Failed in mysql_init()");
|
2001-10-04 01:44:18 +02:00
|
|
|
if (opt_compress)
|
|
|
|
mysql_options(&cur_con->mysql,MYSQL_OPT_COMPRESS,NullS);
|
2002-06-03 23:40:27 +02:00
|
|
|
mysql_options(&cur_con->mysql, MYSQL_OPT_LOCAL_INFILE, 0);
|
2003-03-11 10:41:53 +01:00
|
|
|
#ifdef HAVE_OPENSSL
|
|
|
|
if (opt_use_ssl)
|
|
|
|
mysql_ssl_set(&cur_con->mysql, opt_ssl_key, opt_ssl_cert, opt_ssl_ca,
|
|
|
|
opt_ssl_capath, opt_ssl_cipher);
|
|
|
|
#endif
|
2002-06-03 23:40:27 +02:00
|
|
|
|
2003-05-21 20:39:58 +02:00
|
|
|
if (!(cur_con->name = my_strdup("default", MYF(MY_WME))))
|
2000-12-12 20:12:41 +01:00
|
|
|
die("Out of memory");
|
2001-12-06 13:10:51 +01:00
|
|
|
|
2003-05-21 20:39:58 +02:00
|
|
|
if (safe_connect(&cur_con->mysql, host, user, pass, db, port, unix_sock))
|
2000-10-10 10:42:07 +02:00
|
|
|
die("Failed in mysql_real_connect(): %s", mysql_error(&cur_con->mysql));
|
|
|
|
|
2003-05-21 20:39:58 +02:00
|
|
|
init_var_hash(&cur_con->mysql);
|
|
|
|
|
2001-02-15 02:43:14 +01:00
|
|
|
while (!read_query(&q))
|
2000-12-27 02:31:06 +01:00
|
|
|
{
|
|
|
|
int current_line_inc = 1, processed = 0;
|
|
|
|
if (q->type == Q_UNKNOWN || q->type == Q_COMMENT_WITH_COMMAND)
|
|
|
|
get_query_type(q);
|
2001-06-12 01:28:26 +02:00
|
|
|
if (*block_ok)
|
2000-10-10 10:42:07 +02:00
|
|
|
{
|
2000-12-27 02:31:06 +01:00
|
|
|
processed = 1;
|
|
|
|
switch (q->type) {
|
|
|
|
case Q_CONNECT: do_connect(q); break;
|
2002-08-29 20:08:54 +02:00
|
|
|
case Q_CONNECTION: select_connection(q->first_argument); break;
|
2001-01-20 06:40:16 +01:00
|
|
|
case Q_DISCONNECT:
|
2002-08-08 02:12:02 +02:00
|
|
|
case Q_DIRTY_CLOSE:
|
2001-01-20 06:40:16 +01:00
|
|
|
close_connection(q); break;
|
2001-06-09 03:24:41 +02:00
|
|
|
case Q_RPL_PROBE: do_rpl_probe(q); break;
|
2002-08-08 02:12:02 +02:00
|
|
|
case Q_ENABLE_RPL_PARSE: do_enable_rpl_parse(q); break;
|
2001-12-09 23:08:24 +01:00
|
|
|
case Q_DISABLE_RPL_PARSE: do_disable_rpl_parse(q); break;
|
2002-08-08 02:12:02 +02:00
|
|
|
case Q_ENABLE_QUERY_LOG: disable_query_log=0; break;
|
2001-12-09 23:08:24 +01:00
|
|
|
case Q_DISABLE_QUERY_LOG: disable_query_log=1; break;
|
|
|
|
case Q_ENABLE_RESULT_LOG: disable_result_log=0; break;
|
|
|
|
case Q_DISABLE_RESULT_LOG: disable_result_log=1; break;
|
2003-01-06 00:48:59 +01:00
|
|
|
case Q_ENABLE_WARNINGS: disable_warnings=0; break;
|
|
|
|
case Q_DISABLE_WARNINGS: disable_warnings=1; break;
|
|
|
|
case Q_ENABLE_INFO: disable_info=0; break;
|
|
|
|
case Q_DISABLE_INFO: disable_info=1; break;
|
2004-03-30 18:24:28 +02:00
|
|
|
case Q_ENABLE_METADATA: display_metadata=1; break;
|
|
|
|
case Q_DISABLE_METADATA: display_metadata=0; break;
|
2000-12-27 02:31:06 +01:00
|
|
|
case Q_SOURCE: do_source(q); break;
|
2002-08-18 15:04:26 +02:00
|
|
|
case Q_SLEEP: do_sleep(q, 0); break;
|
|
|
|
case Q_REAL_SLEEP: do_sleep(q, 1); break;
|
2002-02-09 21:58:53 +01:00
|
|
|
case Q_REQUIRE_VERSION: do_require_version(q); break;
|
Robustness feature.
Won't be pushed as is - separate email sent for internal review.
WL#1717 "binlog-innodb consistency".
Now when mysqld starts, if InnoDB does a crash recovery, we use the binlog name
and position retrieved from InnoDB (corresponding to the last transaction
successfully committed by InnoDB) to cut any rolled back transaction from
the binary log. This is triggered by the --innodb-safe-binlog option.
Provided you configure mysqld to fsync() InnoDB at every commit (using
flush_log_at_trx_commit) and to fsync() the binlog at every write
(using --sync-binlog=1), this behaviour guarantees that a master always has
consistency between binlog and InnoDB, whenever the crash happens.
6 tests to verify that it works.
client/mysqltest.c:
New command require_os (only "unix" accepted for now).
innobase/include/trx0sys.h:
when InnoDB does crash recovery, we now save the binlog coords it prints, into variables for later use.
innobase/trx/trx0sys.c:
when InnoDB does crash recovery, we now save the binlog coords it prints, into variables for later use.
mysql-test/mysql-test-run.sh:
The tests which check that the binlog is cut at restart, need to not delete those binlogs, of course.
And not delete replication info, so that we can test that the slave does not receive anything
wrong from the cut binlog.
sql/ha_innodb.cc:
methods to read from InnoDB the binlog coords stored into it
sql/ha_innodb.h:
ethods to read from InnoDB the binlog coords stored into it
sql/log.cc:
Added my_sync() when we create a binlog (my_sync of the binlog and of the index file);
this is always done, whether --sync-binlog or not (binlog creation is rare, so no speed
problem, and I like to have the existence of the binlog always reliably recorded, even if
later content is not).
If --crash-binlog-innodb, crash between the binlog write and the InnoDB commit.
New methods:
- report_pos_in_innodb() to store the binlog name and position into InnoDB (used only when
we create a new binlog: at startup and at FLUSH LOGS)
- cut_spurious_tail() to possibly cut the tail of a binlog based on the info we read
from InnoDB (does something only if InnoDB has just done a crash recovery).
sql/mysql_priv.h:
new option, to crash (use for testing only)
sql/mysqld.cc:
New option --innodb-safe-binlog and --crash-binlog-innodb (the latter is for testing, it makes mysqld crash).
Just after opening the logs and opening the storage engines, cut any wrong statement from the binlog, based
on info read from InnoDB.
sql/sql_class.h:
new methods for MYSQL_LOG.
2004-06-20 19:11:02 +02:00
|
|
|
case Q_REQUIRE_OS: do_require_os(q); break;
|
2002-01-25 06:49:47 +01:00
|
|
|
case Q_WAIT_FOR_SLAVE_TO_STOP: do_wait_for_slave_to_stop(q); break;
|
2001-10-11 21:54:06 +02:00
|
|
|
case Q_REQUIRE_MANAGER: do_require_manager(q); break;
|
2002-08-08 02:12:02 +02:00
|
|
|
#ifndef EMBEDDED_LIBRARY
|
2001-10-02 00:13:55 +02:00
|
|
|
case Q_SERVER_START: do_server_start(q); break;
|
|
|
|
case Q_SERVER_STOP: do_server_stop(q); break;
|
2002-08-08 02:12:02 +02:00
|
|
|
#endif
|
2000-12-27 02:31:06 +01:00
|
|
|
case Q_INC: do_inc(q); break;
|
|
|
|
case Q_DEC: do_dec(q); break;
|
|
|
|
case Q_ECHO: do_echo(q); break;
|
|
|
|
case Q_SYSTEM: do_system(q); break;
|
2003-12-16 05:09:44 +01:00
|
|
|
case Q_DELIMITER:
|
|
|
|
strmake(delimiter, q->first_argument, sizeof(delimiter) - 1);
|
|
|
|
delimiter_length= strlen(delimiter);
|
|
|
|
break;
|
2004-03-04 18:08:14 +01:00
|
|
|
case Q_DISPLAY_VERTICAL_RESULTS: display_result_vertically= TRUE; break;
|
2004-03-06 17:00:54 +01:00
|
|
|
case Q_DISPLAY_HORIZONTAL_RESULTS:
|
2004-03-04 18:08:14 +01:00
|
|
|
display_result_vertically= FALSE; break;
|
2000-12-27 02:31:06 +01:00
|
|
|
case Q_LET: do_let(q); break;
|
2002-08-08 02:12:02 +02:00
|
|
|
case Q_EVAL_RESULT: eval_result = 1; break;
|
2001-03-27 19:23:04 +02:00
|
|
|
case Q_EVAL:
|
2002-08-08 02:12:02 +02:00
|
|
|
if (q->query == q->query_buf)
|
2004-06-21 09:21:20 +02:00
|
|
|
{
|
2001-11-04 14:43:41 +01:00
|
|
|
q->query= q->first_argument;
|
2004-06-21 09:21:20 +02:00
|
|
|
q->first_word_len= 0;
|
|
|
|
}
|
2002-08-08 02:12:02 +02:00
|
|
|
/* fall through */
|
2004-03-06 00:00:21 +01:00
|
|
|
case Q_QUERY_VERTICAL:
|
2004-03-06 17:00:54 +01:00
|
|
|
case Q_QUERY_HORIZONTAL:
|
2004-03-06 00:00:21 +01:00
|
|
|
{
|
|
|
|
my_bool old_display_result_vertically= display_result_vertically;
|
|
|
|
if (!q->query[q->first_word_len])
|
|
|
|
{
|
|
|
|
/* This happens when we use 'query_..' on it's own line */
|
|
|
|
q_send_flag=1;
|
2004-06-21 09:21:20 +02:00
|
|
|
DBUG_PRINT("info",
|
|
|
|
("query: '%s' first_word_len: %d send_flag=1",
|
|
|
|
q->query, q->first_word_len));
|
2004-03-06 00:00:21 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* fix up query pointer if this is * first iteration for this line */
|
|
|
|
if (q->query == q->query_buf)
|
|
|
|
q->query += q->first_word_len + 1;
|
2004-03-16 15:26:00 +01:00
|
|
|
display_result_vertically= (q->type==Q_QUERY_VERTICAL);
|
2004-06-21 09:21:20 +02:00
|
|
|
error|= run_query(&cur_con->mysql, q, QUERY_REAP|QUERY_SEND);
|
2004-03-06 00:00:21 +01:00
|
|
|
display_result_vertically= old_display_result_vertically;
|
|
|
|
break;
|
|
|
|
}
|
2000-12-27 02:31:06 +01:00
|
|
|
case Q_QUERY:
|
2002-08-08 02:12:02 +02:00
|
|
|
case Q_REAP:
|
2000-12-27 02:31:06 +01:00
|
|
|
{
|
2002-06-02 16:04:16 +02:00
|
|
|
/*
|
|
|
|
We read the result always regardless of the mode for both full
|
|
|
|
query and read-result only (reap)
|
|
|
|
*/
|
|
|
|
int flags = QUERY_REAP;
|
2001-03-27 19:23:04 +02:00
|
|
|
if (q->type != Q_REAP) /* for a full query, enable the send stage */
|
2001-01-20 06:40:16 +01:00
|
|
|
flags |= QUERY_SEND;
|
2001-03-25 00:02:26 +01:00
|
|
|
if (q_send_flag)
|
|
|
|
{
|
|
|
|
flags= QUERY_SEND;
|
|
|
|
q_send_flag=0;
|
|
|
|
}
|
2000-12-27 02:31:06 +01:00
|
|
|
if (save_file[0])
|
2000-12-02 18:11:50 +01:00
|
|
|
{
|
2000-12-27 02:31:06 +01:00
|
|
|
strmov(q->record_file,save_file);
|
|
|
|
q->require_file=require_file;
|
|
|
|
save_file[0]=0;
|
2000-12-02 18:11:50 +01:00
|
|
|
}
|
2001-01-23 16:20:51 +01:00
|
|
|
error |= run_query(&cur_con->mysql, q, flags);
|
2001-01-20 06:40:16 +01:00
|
|
|
break;
|
2000-12-27 02:31:06 +01:00
|
|
|
}
|
2001-01-20 06:40:16 +01:00
|
|
|
case Q_SEND:
|
2001-03-25 00:02:26 +01:00
|
|
|
if (!q->query[q->first_word_len])
|
|
|
|
{
|
|
|
|
/* This happens when we use 'send' on it's own line */
|
|
|
|
q_send_flag=1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* fix up query pointer if this is * first iteration for this line */
|
|
|
|
if (q->query == q->query_buf)
|
2001-03-24 03:59:30 +01:00
|
|
|
q->query += q->first_word_len;
|
2002-06-02 16:04:16 +02:00
|
|
|
/*
|
|
|
|
run_query() can execute a query partially, depending on the flags
|
|
|
|
QUERY_SEND flag without QUERY_REAP tells it to just send the
|
|
|
|
query and read the result some time later when reap instruction
|
|
|
|
is given on this connection.
|
2001-03-24 03:59:30 +01:00
|
|
|
*/
|
2002-06-02 16:04:16 +02:00
|
|
|
error |= run_query(&cur_con->mysql, q, QUERY_SEND);
|
2001-01-20 06:40:16 +01:00
|
|
|
break;
|
2000-12-27 02:31:06 +01:00
|
|
|
case Q_RESULT:
|
|
|
|
get_file_name(save_file,q);
|
|
|
|
require_file=0;
|
|
|
|
break;
|
2001-01-15 21:56:44 +01:00
|
|
|
case Q_ERROR:
|
2001-11-04 14:43:41 +01:00
|
|
|
global_expected_errors=get_ints(global_expected_errno,q);
|
2001-01-15 21:56:44 +01:00
|
|
|
break;
|
2000-12-27 02:31:06 +01:00
|
|
|
case Q_REQUIRE:
|
|
|
|
get_file_name(save_file,q);
|
|
|
|
require_file=1;
|
|
|
|
break;
|
2001-02-13 11:39:50 +01:00
|
|
|
case Q_REPLACE:
|
|
|
|
get_replace(q);
|
|
|
|
break;
|
2003-06-27 15:29:10 +02:00
|
|
|
case Q_REPLACE_COLUMN:
|
|
|
|
get_replace_column(q);
|
|
|
|
break;
|
2002-08-08 02:12:02 +02:00
|
|
|
case Q_SAVE_MASTER_POS: do_save_master_pos(); break;
|
|
|
|
case Q_SYNC_WITH_MASTER: do_sync_with_master(q); break;
|
2002-08-29 20:08:54 +02:00
|
|
|
case Q_SYNC_SLAVE_WITH_MASTER:
|
|
|
|
{
|
|
|
|
do_save_master_pos();
|
|
|
|
if (*q->first_argument)
|
|
|
|
select_connection(q->first_argument);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
char buf[] = "slave";
|
|
|
|
select_connection(buf);
|
|
|
|
}
|
|
|
|
do_sync_with_master2("");
|
|
|
|
break;
|
|
|
|
}
|
2000-12-27 02:31:06 +01:00
|
|
|
case Q_COMMENT: /* Ignore row */
|
2002-08-08 02:12:02 +02:00
|
|
|
case Q_COMMENT_WITH_COMMAND:
|
2003-03-04 11:22:35 +01:00
|
|
|
break;
|
2001-03-15 02:55:48 +01:00
|
|
|
case Q_PING:
|
|
|
|
(void) mysql_ping(&cur_con->mysql);
|
|
|
|
break;
|
2003-09-08 07:53:51 +02:00
|
|
|
case Q_EXEC:
|
|
|
|
(void) do_exec(q);
|
|
|
|
break;
|
2000-12-27 02:31:06 +01:00
|
|
|
default: processed = 0; break;
|
|
|
|
}
|
|
|
|
}
|
2000-12-02 18:11:50 +01:00
|
|
|
|
2000-12-27 02:31:06 +01:00
|
|
|
if (!processed)
|
|
|
|
{
|
|
|
|
current_line_inc = 0;
|
2002-08-24 04:44:16 +02:00
|
|
|
switch (q->type) {
|
2000-12-27 02:31:06 +01:00
|
|
|
case Q_WHILE: do_while(q); break;
|
|
|
|
case Q_END_BLOCK: do_done(q); break;
|
|
|
|
default: current_line_inc = 1; break;
|
|
|
|
}
|
2000-10-10 10:42:07 +02:00
|
|
|
}
|
|
|
|
|
2000-12-27 02:31:06 +01:00
|
|
|
parser.current_line += current_line_inc;
|
|
|
|
}
|
|
|
|
|
2001-01-07 23:04:30 +01:00
|
|
|
if (result_file && ds_res.length)
|
2000-12-27 02:31:06 +01:00
|
|
|
{
|
2001-01-07 23:04:30 +01:00
|
|
|
if (!record)
|
2000-12-27 02:31:06 +01:00
|
|
|
error |= check_result(&ds_res, result_file, q->require_file);
|
|
|
|
else
|
2001-01-07 23:04:30 +01:00
|
|
|
str_to_file(result_file, ds_res.str, ds_res.length);
|
2000-12-27 02:31:06 +01:00
|
|
|
}
|
2001-01-07 23:04:30 +01:00
|
|
|
dynstr_free(&ds_res);
|
2000-12-22 20:10:35 +01:00
|
|
|
|
2003-08-18 23:08:08 +02:00
|
|
|
if (!silent)
|
|
|
|
{
|
2001-12-15 03:41:20 +01:00
|
|
|
if (error)
|
2000-10-10 10:42:07 +02:00
|
|
|
printf("not ok\n");
|
|
|
|
else
|
|
|
|
printf("ok\n");
|
|
|
|
}
|
2000-12-22 20:10:35 +01:00
|
|
|
|
2001-01-07 23:04:30 +01:00
|
|
|
free_used_memory();
|
2001-02-15 02:43:14 +01:00
|
|
|
exit(error ? 1 : 0);
|
|
|
|
return error ? 1 : 0; /* Keep compiler happy */
|
2003-12-16 15:03:39 +01:00
|
|
|
}
|
2000-10-10 10:42:07 +02:00
|
|
|
}
|
2001-02-13 11:39:50 +01:00
|
|
|
|
2003-12-16 05:09:44 +01:00
|
|
|
|
2001-10-08 03:58:07 +02:00
|
|
|
/*
|
|
|
|
Read arguments for embedded server and put them into
|
|
|
|
embedded_server_args_count and embedded_server_args[]
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
2003-05-21 20:39:58 +02:00
|
|
|
static int read_server_arguments(const char *name)
|
2001-10-08 03:58:07 +02:00
|
|
|
{
|
|
|
|
char argument[1024],buff[FN_REFLEN], *str=0;
|
|
|
|
FILE *file;
|
|
|
|
|
|
|
|
if (!test_if_hard_path(name))
|
|
|
|
{
|
|
|
|
strxmov(buff, opt_basedir, name, NullS);
|
|
|
|
name=buff;
|
|
|
|
}
|
|
|
|
fn_format(buff,name,"","",4);
|
|
|
|
|
|
|
|
if (!embedded_server_arg_count)
|
|
|
|
{
|
|
|
|
embedded_server_arg_count=1;
|
|
|
|
embedded_server_args[0]= (char*) ""; /* Progname */
|
|
|
|
}
|
2003-01-28 07:38:28 +01:00
|
|
|
if (!(file=my_fopen(buff, O_RDONLY | FILE_BINARY, MYF(MY_WME))))
|
2001-10-08 03:58:07 +02:00
|
|
|
return 1;
|
|
|
|
while (embedded_server_arg_count < MAX_SERVER_ARGS &&
|
|
|
|
(str=fgets(argument,sizeof(argument), file)))
|
|
|
|
{
|
|
|
|
*(strend(str)-1)=0; /* Remove end newline */
|
|
|
|
if (!(embedded_server_args[embedded_server_arg_count]=
|
|
|
|
(char*) my_strdup(str,MYF(MY_WME))))
|
|
|
|
{
|
|
|
|
my_fclose(file,MYF(0));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
embedded_server_arg_count++;
|
|
|
|
}
|
|
|
|
my_fclose(file,MYF(0));
|
|
|
|
if (str)
|
|
|
|
{
|
|
|
|
fprintf(stderr,"Too many arguments in option file: %s\n",name);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2001-02-13 11:39:50 +01:00
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
* Handle replacement of strings
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#define PC_MALLOC 256 /* Bytes for pointers */
|
|
|
|
#define PS_MALLOC 512 /* Bytes for data */
|
|
|
|
|
|
|
|
#define SPACE_CHAR 256
|
|
|
|
#define START_OF_LINE 257
|
|
|
|
#define END_OF_LINE 258
|
|
|
|
#define LAST_CHAR_CODE 259
|
|
|
|
|
|
|
|
typedef struct st_replace {
|
2002-08-08 02:12:02 +02:00
|
|
|
bool found;
|
2001-02-13 11:39:50 +01:00
|
|
|
struct st_replace *next[256];
|
|
|
|
} REPLACE;
|
|
|
|
|
|
|
|
typedef struct st_replace_found {
|
|
|
|
bool found;
|
|
|
|
char *replace_string;
|
|
|
|
uint to_offset;
|
|
|
|
int from_offset;
|
|
|
|
} REPLACE_STRING;
|
|
|
|
|
|
|
|
#ifndef WORD_BIT
|
|
|
|
#define WORD_BIT (8*sizeof(uint))
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
static int insert_pointer_name(reg1 POINTER_ARRAY *pa,my_string name)
|
|
|
|
{
|
|
|
|
uint i,length,old_count;
|
|
|
|
byte *new_pos;
|
|
|
|
const char **new_array;
|
|
|
|
DBUG_ENTER("insert_pointer_name");
|
|
|
|
|
|
|
|
if (! pa->typelib.count)
|
|
|
|
{
|
|
|
|
if (!(pa->typelib.type_names=(const char **)
|
|
|
|
my_malloc(((PC_MALLOC-MALLOC_OVERHEAD)/
|
|
|
|
(sizeof(my_string)+sizeof(*pa->flag))*
|
|
|
|
(sizeof(my_string)+sizeof(*pa->flag))),MYF(MY_WME))))
|
|
|
|
DBUG_RETURN(-1);
|
|
|
|
if (!(pa->str= (byte*) my_malloc((uint) (PS_MALLOC-MALLOC_OVERHEAD),
|
|
|
|
MYF(MY_WME))))
|
|
|
|
{
|
|
|
|
my_free((gptr) pa->typelib.type_names,MYF(0));
|
|
|
|
DBUG_RETURN (-1);
|
|
|
|
}
|
|
|
|
pa->max_count=(PC_MALLOC-MALLOC_OVERHEAD)/(sizeof(byte*)+
|
|
|
|
sizeof(*pa->flag));
|
|
|
|
pa->flag= (int7*) (pa->typelib.type_names+pa->max_count);
|
|
|
|
pa->length=0;
|
|
|
|
pa->max_length=PS_MALLOC-MALLOC_OVERHEAD;
|
|
|
|
pa->array_allocs=1;
|
|
|
|
}
|
|
|
|
length=(uint) strlen(name)+1;
|
|
|
|
if (pa->length+length >= pa->max_length)
|
|
|
|
{
|
|
|
|
if (!(new_pos= (byte*) my_realloc((gptr) pa->str,
|
|
|
|
(uint) (pa->max_length+PS_MALLOC),
|
|
|
|
MYF(MY_WME))))
|
|
|
|
DBUG_RETURN(1);
|
|
|
|
if (new_pos != pa->str)
|
|
|
|
{
|
|
|
|
my_ptrdiff_t diff=PTR_BYTE_DIFF(new_pos,pa->str);
|
|
|
|
for (i=0 ; i < pa->typelib.count ; i++)
|
|
|
|
pa->typelib.type_names[i]= ADD_TO_PTR(pa->typelib.type_names[i],diff,
|
|
|
|
char*);
|
|
|
|
pa->str=new_pos;
|
|
|
|
}
|
|
|
|
pa->max_length+=PS_MALLOC;
|
|
|
|
}
|
|
|
|
if (pa->typelib.count >= pa->max_count-1)
|
|
|
|
{
|
|
|
|
int len;
|
|
|
|
pa->array_allocs++;
|
|
|
|
len=(PC_MALLOC*pa->array_allocs - MALLOC_OVERHEAD);
|
|
|
|
if (!(new_array=(const char **) my_realloc((gptr) pa->typelib.type_names,
|
|
|
|
(uint) len/
|
|
|
|
(sizeof(byte*)+sizeof(*pa->flag))*
|
|
|
|
(sizeof(byte*)+sizeof(*pa->flag)),
|
|
|
|
MYF(MY_WME))))
|
|
|
|
DBUG_RETURN(1);
|
|
|
|
pa->typelib.type_names=new_array;
|
|
|
|
old_count=pa->max_count;
|
|
|
|
pa->max_count=len/(sizeof(byte*) + sizeof(*pa->flag));
|
|
|
|
pa->flag= (int7*) (pa->typelib.type_names+pa->max_count);
|
|
|
|
memcpy((byte*) pa->flag,(my_string) (pa->typelib.type_names+old_count),
|
|
|
|
old_count*sizeof(*pa->flag));
|
|
|
|
}
|
|
|
|
pa->flag[pa->typelib.count]=0; /* Reset flag */
|
|
|
|
pa->typelib.type_names[pa->typelib.count++]= pa->str+pa->length;
|
|
|
|
pa->typelib.type_names[pa->typelib.count]= NullS; /* Put end-mark */
|
|
|
|
VOID(strmov(pa->str+pa->length,name));
|
|
|
|
pa->length+=length;
|
|
|
|
DBUG_RETURN(0);
|
|
|
|
} /* insert_pointer_name */
|
|
|
|
|
|
|
|
|
|
|
|
/* free pointer array */
|
|
|
|
|
|
|
|
void free_pointer_array(POINTER_ARRAY *pa)
|
|
|
|
{
|
|
|
|
if (pa->typelib.count)
|
|
|
|
{
|
|
|
|
pa->typelib.count=0;
|
|
|
|
my_free((gptr) pa->typelib.type_names,MYF(0));
|
|
|
|
pa->typelib.type_names=0;
|
|
|
|
my_free((gptr) pa->str,MYF(0));
|
|
|
|
}
|
|
|
|
} /* free_pointer_array */
|
|
|
|
|
|
|
|
|
|
|
|
/* Code for replace rutines */
|
|
|
|
|
|
|
|
#define SET_MALLOC_HUNC 64
|
|
|
|
|
|
|
|
typedef struct st_rep_set {
|
2002-08-08 02:12:02 +02:00
|
|
|
uint *bits; /* Pointer to used sets */
|
|
|
|
short next[LAST_CHAR_CODE]; /* Pointer to next sets */
|
2001-02-13 11:39:50 +01:00
|
|
|
uint found_len; /* Best match to date */
|
|
|
|
int found_offset;
|
2002-08-08 02:12:02 +02:00
|
|
|
uint table_offset;
|
|
|
|
uint size_of_bits; /* For convinience */
|
2001-02-13 11:39:50 +01:00
|
|
|
} REP_SET;
|
|
|
|
|
|
|
|
typedef struct st_rep_sets {
|
|
|
|
uint count; /* Number of sets */
|
|
|
|
uint extra; /* Extra sets in buffer */
|
|
|
|
uint invisible; /* Sets not chown */
|
|
|
|
uint size_of_bits;
|
|
|
|
REP_SET *set,*set_buffer;
|
|
|
|
uint *bit_buffer;
|
|
|
|
} REP_SETS;
|
|
|
|
|
|
|
|
typedef struct st_found_set {
|
|
|
|
uint table_offset;
|
|
|
|
int found_offset;
|
|
|
|
} FOUND_SET;
|
|
|
|
|
|
|
|
typedef struct st_follow {
|
|
|
|
int chr;
|
|
|
|
uint table_offset;
|
|
|
|
uint len;
|
|
|
|
} FOLLOWS;
|
|
|
|
|
|
|
|
|
|
|
|
static int init_sets(REP_SETS *sets,uint states);
|
|
|
|
static REP_SET *make_new_set(REP_SETS *sets);
|
|
|
|
static void make_sets_invisible(REP_SETS *sets);
|
|
|
|
static void free_last_set(REP_SETS *sets);
|
|
|
|
static void free_sets(REP_SETS *sets);
|
|
|
|
static void set_bit(REP_SET *set, uint bit);
|
|
|
|
static void clear_bit(REP_SET *set, uint bit);
|
|
|
|
static void or_bits(REP_SET *to,REP_SET *from);
|
|
|
|
static void copy_bits(REP_SET *to,REP_SET *from);
|
|
|
|
static int cmp_bits(REP_SET *set1,REP_SET *set2);
|
|
|
|
static int get_next_bit(REP_SET *set,uint lastpos);
|
|
|
|
static int find_set(REP_SETS *sets,REP_SET *find);
|
|
|
|
static int find_found(FOUND_SET *found_set,uint table_offset,
|
|
|
|
int found_offset);
|
|
|
|
static uint start_at_word(my_string pos);
|
|
|
|
static uint end_of_word(my_string pos);
|
|
|
|
static uint replace_len(my_string pos);
|
|
|
|
|
|
|
|
static uint found_sets=0;
|
|
|
|
|
|
|
|
|
|
|
|
/* Init a replace structure for further calls */
|
|
|
|
|
|
|
|
REPLACE *init_replace(my_string *from, my_string *to,uint count,
|
|
|
|
my_string word_end_chars)
|
|
|
|
{
|
|
|
|
uint i,j,states,set_nr,len,result_len,max_length,found_end,bits_set,bit_nr;
|
|
|
|
int used_sets,chr,default_state;
|
|
|
|
char used_chars[LAST_CHAR_CODE],is_word_end[256];
|
|
|
|
my_string pos,to_pos,*to_array;
|
|
|
|
REP_SETS sets;
|
|
|
|
REP_SET *set,*start_states,*word_states,*new_set;
|
|
|
|
FOLLOWS *follow,*follow_ptr;
|
|
|
|
REPLACE *replace;
|
|
|
|
FOUND_SET *found_set;
|
|
|
|
REPLACE_STRING *rep_str;
|
|
|
|
DBUG_ENTER("init_replace");
|
|
|
|
|
|
|
|
/* Count number of states */
|
|
|
|
for (i=result_len=max_length=0 , states=2 ; i < count ; i++)
|
|
|
|
{
|
|
|
|
len=replace_len(from[i]);
|
|
|
|
if (!len)
|
|
|
|
{
|
|
|
|
errno=EINVAL;
|
|
|
|
my_message(0,"No to-string for last from-string",MYF(ME_BELL));
|
|
|
|
DBUG_RETURN(0);
|
|
|
|
}
|
|
|
|
states+=len+1;
|
|
|
|
result_len+=(uint) strlen(to[i])+1;
|
|
|
|
if (len > max_length)
|
|
|
|
max_length=len;
|
|
|
|
}
|
|
|
|
bzero((char*) is_word_end,sizeof(is_word_end));
|
|
|
|
for (i=0 ; word_end_chars[i] ; i++)
|
|
|
|
is_word_end[(uchar) word_end_chars[i]]=1;
|
|
|
|
|
|
|
|
if (init_sets(&sets,states))
|
|
|
|
DBUG_RETURN(0);
|
|
|
|
found_sets=0;
|
|
|
|
if (!(found_set= (FOUND_SET*) my_malloc(sizeof(FOUND_SET)*max_length*count,
|
|
|
|
MYF(MY_WME))))
|
|
|
|
{
|
|
|
|
free_sets(&sets);
|
|
|
|
DBUG_RETURN(0);
|
|
|
|
}
|
|
|
|
VOID(make_new_set(&sets)); /* Set starting set */
|
|
|
|
make_sets_invisible(&sets); /* Hide previus sets */
|
|
|
|
used_sets=-1;
|
|
|
|
word_states=make_new_set(&sets); /* Start of new word */
|
|
|
|
start_states=make_new_set(&sets); /* This is first state */
|
|
|
|
if (!(follow=(FOLLOWS*) my_malloc((states+2)*sizeof(FOLLOWS),MYF(MY_WME))))
|
|
|
|
{
|
|
|
|
free_sets(&sets);
|
|
|
|
my_free((gptr) found_set,MYF(0));
|
|
|
|
DBUG_RETURN(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Init follow_ptr[] */
|
|
|
|
for (i=0, states=1, follow_ptr=follow+1 ; i < count ; i++)
|
|
|
|
{
|
|
|
|
if (from[i][0] == '\\' && from[i][1] == '^')
|
|
|
|
{
|
|
|
|
set_bit(start_states,states+1);
|
|
|
|
if (!from[i][2])
|
|
|
|
{
|
|
|
|
start_states->table_offset=i;
|
|
|
|
start_states->found_offset=1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (from[i][0] == '\\' && from[i][1] == '$')
|
|
|
|
{
|
|
|
|
set_bit(start_states,states);
|
|
|
|
set_bit(word_states,states);
|
|
|
|
if (!from[i][2] && start_states->table_offset == (uint) ~0)
|
|
|
|
{
|
|
|
|
start_states->table_offset=i;
|
|
|
|
start_states->found_offset=0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
set_bit(word_states,states);
|
|
|
|
if (from[i][0] == '\\' && (from[i][1] == 'b' && from[i][2]))
|
|
|
|
set_bit(start_states,states+1);
|
|
|
|
else
|
|
|
|
set_bit(start_states,states);
|
|
|
|
}
|
|
|
|
for (pos=from[i], len=0; *pos ; pos++)
|
|
|
|
{
|
|
|
|
if (*pos == '\\' && *(pos+1))
|
|
|
|
{
|
|
|
|
pos++;
|
|
|
|
switch (*pos) {
|
|
|
|
case 'b':
|
|
|
|
follow_ptr->chr = SPACE_CHAR;
|
|
|
|
break;
|
|
|
|
case '^':
|
|
|
|
follow_ptr->chr = START_OF_LINE;
|
|
|
|
break;
|
|
|
|
case '$':
|
|
|
|
follow_ptr->chr = END_OF_LINE;
|
|
|
|
break;
|
|
|
|
case 'r':
|
|
|
|
follow_ptr->chr = '\r';
|
|
|
|
break;
|
|
|
|
case 't':
|
|
|
|
follow_ptr->chr = '\t';
|
|
|
|
break;
|
|
|
|
case 'v':
|
|
|
|
follow_ptr->chr = '\v';
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
follow_ptr->chr = (uchar) *pos;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
follow_ptr->chr= (uchar) *pos;
|
|
|
|
follow_ptr->table_offset=i;
|
|
|
|
follow_ptr->len= ++len;
|
|
|
|
follow_ptr++;
|
|
|
|
}
|
|
|
|
follow_ptr->chr=0;
|
|
|
|
follow_ptr->table_offset=i;
|
|
|
|
follow_ptr->len=len;
|
|
|
|
follow_ptr++;
|
|
|
|
states+=(uint) len+1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for (set_nr=0,pos=0 ; set_nr < sets.count ; set_nr++)
|
|
|
|
{
|
|
|
|
set=sets.set+set_nr;
|
|
|
|
default_state= 0; /* Start from beginning */
|
|
|
|
|
|
|
|
/* If end of found-string not found or start-set with current set */
|
|
|
|
|
|
|
|
for (i= (uint) ~0; (i=get_next_bit(set,i)) ;)
|
|
|
|
{
|
|
|
|
if (!follow[i].chr)
|
|
|
|
{
|
|
|
|
if (! default_state)
|
|
|
|
default_state= find_found(found_set,set->table_offset,
|
|
|
|
set->found_offset+1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
copy_bits(sets.set+used_sets,set); /* Save set for changes */
|
|
|
|
if (!default_state)
|
|
|
|
or_bits(sets.set+used_sets,sets.set); /* Can restart from start */
|
|
|
|
|
|
|
|
/* Find all chars that follows current sets */
|
|
|
|
bzero((char*) used_chars,sizeof(used_chars));
|
|
|
|
for (i= (uint) ~0; (i=get_next_bit(sets.set+used_sets,i)) ;)
|
|
|
|
{
|
|
|
|
used_chars[follow[i].chr]=1;
|
|
|
|
if ((follow[i].chr == SPACE_CHAR && !follow[i+1].chr &&
|
|
|
|
follow[i].len > 1) || follow[i].chr == END_OF_LINE)
|
|
|
|
used_chars[0]=1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Mark word_chars used if \b is in state */
|
|
|
|
if (used_chars[SPACE_CHAR])
|
|
|
|
for (pos= word_end_chars ; *pos ; pos++)
|
|
|
|
used_chars[(int) (uchar) *pos] = 1;
|
|
|
|
|
|
|
|
/* Handle other used characters */
|
|
|
|
for (chr= 0 ; chr < 256 ; chr++)
|
|
|
|
{
|
|
|
|
if (! used_chars[chr])
|
|
|
|
set->next[chr]= chr ? default_state : -1;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
new_set=make_new_set(&sets);
|
|
|
|
set=sets.set+set_nr; /* if realloc */
|
|
|
|
new_set->table_offset=set->table_offset;
|
|
|
|
new_set->found_len=set->found_len;
|
|
|
|
new_set->found_offset=set->found_offset+1;
|
|
|
|
found_end=0;
|
|
|
|
|
|
|
|
for (i= (uint) ~0 ; (i=get_next_bit(sets.set+used_sets,i)) ; )
|
|
|
|
{
|
|
|
|
if (!follow[i].chr || follow[i].chr == chr ||
|
|
|
|
(follow[i].chr == SPACE_CHAR &&
|
|
|
|
(is_word_end[chr] ||
|
|
|
|
(!chr && follow[i].len > 1 && ! follow[i+1].chr))) ||
|
|
|
|
(follow[i].chr == END_OF_LINE && ! chr))
|
|
|
|
{
|
|
|
|
if ((! chr || (follow[i].chr && !follow[i+1].chr)) &&
|
|
|
|
follow[i].len > found_end)
|
|
|
|
found_end=follow[i].len;
|
|
|
|
if (chr && follow[i].chr)
|
|
|
|
set_bit(new_set,i+1); /* To next set */
|
|
|
|
else
|
|
|
|
set_bit(new_set,i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (found_end)
|
|
|
|
{
|
|
|
|
new_set->found_len=0; /* Set for testing if first */
|
|
|
|
bits_set=0;
|
|
|
|
for (i= (uint) ~0; (i=get_next_bit(new_set,i)) ;)
|
|
|
|
{
|
|
|
|
if ((follow[i].chr == SPACE_CHAR ||
|
|
|
|
follow[i].chr == END_OF_LINE) && ! chr)
|
|
|
|
bit_nr=i+1;
|
|
|
|
else
|
|
|
|
bit_nr=i;
|
|
|
|
if (follow[bit_nr-1].len < found_end ||
|
|
|
|
(new_set->found_len &&
|
|
|
|
(chr == 0 || !follow[bit_nr].chr)))
|
|
|
|
clear_bit(new_set,i);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (chr == 0 || !follow[bit_nr].chr)
|
|
|
|
{ /* best match */
|
|
|
|
new_set->table_offset=follow[bit_nr].table_offset;
|
|
|
|
if (chr || (follow[i].chr == SPACE_CHAR ||
|
|
|
|
follow[i].chr == END_OF_LINE))
|
|
|
|
new_set->found_offset=found_end; /* New match */
|
|
|
|
new_set->found_len=found_end;
|
|
|
|
}
|
|
|
|
bits_set++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (bits_set == 1)
|
|
|
|
{
|
|
|
|
set->next[chr] = find_found(found_set,
|
|
|
|
new_set->table_offset,
|
|
|
|
new_set->found_offset);
|
|
|
|
free_last_set(&sets);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
set->next[chr] = find_set(&sets,new_set);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
set->next[chr] = find_set(&sets,new_set);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Alloc replace structure for the replace-state-machine */
|
|
|
|
|
|
|
|
if ((replace=(REPLACE*) my_malloc(sizeof(REPLACE)*(sets.count)+
|
|
|
|
sizeof(REPLACE_STRING)*(found_sets+1)+
|
|
|
|
sizeof(my_string)*count+result_len,
|
|
|
|
MYF(MY_WME | MY_ZEROFILL))))
|
|
|
|
{
|
|
|
|
rep_str=(REPLACE_STRING*) (replace+sets.count);
|
|
|
|
to_array=(my_string*) (rep_str+found_sets+1);
|
|
|
|
to_pos=(my_string) (to_array+count);
|
|
|
|
for (i=0 ; i < count ; i++)
|
|
|
|
{
|
|
|
|
to_array[i]=to_pos;
|
|
|
|
to_pos=strmov(to_pos,to[i])+1;
|
|
|
|
}
|
|
|
|
rep_str[0].found=1;
|
|
|
|
rep_str[0].replace_string=0;
|
|
|
|
for (i=1 ; i <= found_sets ; i++)
|
|
|
|
{
|
|
|
|
pos=from[found_set[i-1].table_offset];
|
|
|
|
rep_str[i].found= !bcmp(pos,"\\^",3) ? 2 : 1;
|
|
|
|
rep_str[i].replace_string=to_array[found_set[i-1].table_offset];
|
|
|
|
rep_str[i].to_offset=found_set[i-1].found_offset-start_at_word(pos);
|
|
|
|
rep_str[i].from_offset=found_set[i-1].found_offset-replace_len(pos)+
|
|
|
|
end_of_word(pos);
|
|
|
|
}
|
|
|
|
for (i=0 ; i < sets.count ; i++)
|
|
|
|
{
|
|
|
|
for (j=0 ; j < 256 ; j++)
|
|
|
|
if (sets.set[i].next[j] >= 0)
|
|
|
|
replace[i].next[j]=replace+sets.set[i].next[j];
|
|
|
|
else
|
|
|
|
replace[i].next[j]=(REPLACE*) (rep_str+(-sets.set[i].next[j]-1));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
my_free((gptr) follow,MYF(0));
|
|
|
|
free_sets(&sets);
|
|
|
|
my_free((gptr) found_set,MYF(0));
|
|
|
|
DBUG_PRINT("exit",("Replace table has %d states",sets.count));
|
|
|
|
DBUG_RETURN(replace);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int init_sets(REP_SETS *sets,uint states)
|
|
|
|
{
|
|
|
|
bzero((char*) sets,sizeof(*sets));
|
|
|
|
sets->size_of_bits=((states+7)/8);
|
|
|
|
if (!(sets->set_buffer=(REP_SET*) my_malloc(sizeof(REP_SET)*SET_MALLOC_HUNC,
|
|
|
|
MYF(MY_WME))))
|
|
|
|
return 1;
|
|
|
|
if (!(sets->bit_buffer=(uint*) my_malloc(sizeof(uint)*sets->size_of_bits*
|
|
|
|
SET_MALLOC_HUNC,MYF(MY_WME))))
|
|
|
|
{
|
|
|
|
my_free((gptr) sets->set,MYF(0));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Make help sets invisible for nicer codeing */
|
|
|
|
|
|
|
|
static void make_sets_invisible(REP_SETS *sets)
|
|
|
|
{
|
|
|
|
sets->invisible=sets->count;
|
|
|
|
sets->set+=sets->count;
|
|
|
|
sets->count=0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static REP_SET *make_new_set(REP_SETS *sets)
|
|
|
|
{
|
|
|
|
uint i,count,*bit_buffer;
|
|
|
|
REP_SET *set;
|
|
|
|
if (sets->extra)
|
|
|
|
{
|
|
|
|
sets->extra--;
|
|
|
|
set=sets->set+ sets->count++;
|
|
|
|
bzero((char*) set->bits,sizeof(uint)*sets->size_of_bits);
|
|
|
|
bzero((char*) &set->next[0],sizeof(set->next[0])*LAST_CHAR_CODE);
|
|
|
|
set->found_offset=0;
|
|
|
|
set->found_len=0;
|
|
|
|
set->table_offset= (uint) ~0;
|
|
|
|
set->size_of_bits=sets->size_of_bits;
|
|
|
|
return set;
|
|
|
|
}
|
|
|
|
count=sets->count+sets->invisible+SET_MALLOC_HUNC;
|
|
|
|
if (!(set=(REP_SET*) my_realloc((gptr) sets->set_buffer,
|
|
|
|
sizeof(REP_SET)*count,
|
|
|
|
MYF(MY_WME))))
|
|
|
|
return 0;
|
|
|
|
sets->set_buffer=set;
|
|
|
|
sets->set=set+sets->invisible;
|
|
|
|
if (!(bit_buffer=(uint*) my_realloc((gptr) sets->bit_buffer,
|
|
|
|
(sizeof(uint)*sets->size_of_bits)*count,
|
|
|
|
MYF(MY_WME))))
|
|
|
|
return 0;
|
|
|
|
sets->bit_buffer=bit_buffer;
|
|
|
|
for (i=0 ; i < count ; i++)
|
|
|
|
{
|
|
|
|
sets->set_buffer[i].bits=bit_buffer;
|
|
|
|
bit_buffer+=sets->size_of_bits;
|
|
|
|
}
|
|
|
|
sets->extra=SET_MALLOC_HUNC;
|
|
|
|
return make_new_set(sets);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void free_last_set(REP_SETS *sets)
|
|
|
|
{
|
|
|
|
sets->count--;
|
|
|
|
sets->extra++;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void free_sets(REP_SETS *sets)
|
|
|
|
{
|
|
|
|
my_free((gptr)sets->set_buffer,MYF(0));
|
|
|
|
my_free((gptr)sets->bit_buffer,MYF(0));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void set_bit(REP_SET *set, uint bit)
|
|
|
|
{
|
|
|
|
set->bits[bit / WORD_BIT] |= 1 << (bit % WORD_BIT);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void clear_bit(REP_SET *set, uint bit)
|
|
|
|
{
|
|
|
|
set->bits[bit / WORD_BIT] &= ~ (1 << (bit % WORD_BIT));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void or_bits(REP_SET *to,REP_SET *from)
|
|
|
|
{
|
|
|
|
reg1 uint i;
|
|
|
|
for (i=0 ; i < to->size_of_bits ; i++)
|
|
|
|
to->bits[i]|=from->bits[i];
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void copy_bits(REP_SET *to,REP_SET *from)
|
|
|
|
{
|
|
|
|
memcpy((byte*) to->bits,(byte*) from->bits,
|
|
|
|
(size_t) (sizeof(uint) * to->size_of_bits));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cmp_bits(REP_SET *set1,REP_SET *set2)
|
|
|
|
{
|
|
|
|
return bcmp((byte*) set1->bits,(byte*) set2->bits,
|
|
|
|
sizeof(uint) * set1->size_of_bits);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Get next set bit from set. */
|
|
|
|
|
|
|
|
static int get_next_bit(REP_SET *set,uint lastpos)
|
|
|
|
{
|
|
|
|
uint pos,*start,*end,bits;
|
|
|
|
|
|
|
|
start=set->bits+ ((lastpos+1) / WORD_BIT);
|
|
|
|
end=set->bits + set->size_of_bits;
|
|
|
|
bits=start[0] & ~((1 << ((lastpos+1) % WORD_BIT)) -1);
|
|
|
|
|
|
|
|
while (! bits && ++start < end)
|
|
|
|
bits=start[0];
|
|
|
|
if (!bits)
|
|
|
|
return 0;
|
|
|
|
pos=(uint) (start-set->bits)*WORD_BIT;
|
|
|
|
while (! (bits & 1))
|
|
|
|
{
|
|
|
|
bits>>=1;
|
|
|
|
pos++;
|
|
|
|
}
|
|
|
|
return pos;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* find if there is a same set in sets. If there is, use it and
|
|
|
|
free given set, else put in given set in sets and return it's
|
|
|
|
position */
|
|
|
|
|
|
|
|
static int find_set(REP_SETS *sets,REP_SET *find)
|
|
|
|
{
|
|
|
|
uint i;
|
|
|
|
for (i=0 ; i < sets->count-1 ; i++)
|
|
|
|
{
|
|
|
|
if (!cmp_bits(sets->set+i,find))
|
|
|
|
{
|
|
|
|
free_last_set(sets);
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return i; /* return new postion */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* find if there is a found_set with same table_offset & found_offset
|
|
|
|
If there is return offset to it, else add new offset and return pos.
|
|
|
|
Pos returned is -offset-2 in found_set_structure because it's is
|
|
|
|
saved in set->next and set->next[] >= 0 points to next set and
|
|
|
|
set->next[] == -1 is reserved for end without replaces.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int find_found(FOUND_SET *found_set,uint table_offset, int found_offset)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for (i=0 ; (uint) i < found_sets ; i++)
|
|
|
|
if (found_set[i].table_offset == table_offset &&
|
|
|
|
found_set[i].found_offset == found_offset)
|
|
|
|
return -i-2;
|
|
|
|
found_set[i].table_offset=table_offset;
|
|
|
|
found_set[i].found_offset=found_offset;
|
|
|
|
found_sets++;
|
|
|
|
return -i-2; /* return new postion */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return 1 if regexp starts with \b or ends with \b*/
|
|
|
|
|
|
|
|
static uint start_at_word(my_string pos)
|
|
|
|
{
|
|
|
|
return (((!bcmp(pos,"\\b",2) && pos[2]) || !bcmp(pos,"\\^",2)) ? 1 : 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint end_of_word(my_string pos)
|
|
|
|
{
|
|
|
|
my_string end=strend(pos);
|
|
|
|
return ((end > pos+2 && !bcmp(end-2,"\\b",2)) ||
|
|
|
|
(end >= pos+2 && !bcmp(end-2,"\\$",2))) ?
|
|
|
|
1 : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static uint replace_len(my_string str)
|
|
|
|
{
|
|
|
|
uint len=0;
|
|
|
|
while (*str)
|
|
|
|
{
|
|
|
|
if (str[0] == '\\' && str[1])
|
|
|
|
str++;
|
|
|
|
str++;
|
|
|
|
len++;
|
|
|
|
}
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Replace strings; Return length of result string */
|
|
|
|
|
2002-08-08 02:12:02 +02:00
|
|
|
uint replace_strings(REPLACE *rep, my_string *start,uint *max_length,
|
2002-04-02 16:54:57 +02:00
|
|
|
const char *from)
|
2001-02-13 11:39:50 +01:00
|
|
|
{
|
|
|
|
reg1 REPLACE *rep_pos;
|
|
|
|
reg2 REPLACE_STRING *rep_str;
|
2001-08-22 00:45:07 +02:00
|
|
|
my_string to,end,pos,new_str;
|
2001-02-13 11:39:50 +01:00
|
|
|
|
|
|
|
end=(to= *start) + *max_length-1;
|
|
|
|
rep_pos=rep+1;
|
2002-08-08 02:12:02 +02:00
|
|
|
for (;;)
|
2001-02-13 11:39:50 +01:00
|
|
|
{
|
|
|
|
while (!rep_pos->found)
|
|
|
|
{
|
|
|
|
rep_pos= rep_pos->next[(uchar) *from];
|
|
|
|
if (to == end)
|
|
|
|
{
|
|
|
|
(*max_length)+=8192;
|
2001-08-22 00:45:07 +02:00
|
|
|
if (!(new_str=my_realloc(*start,*max_length,MYF(MY_WME))))
|
2001-02-13 11:39:50 +01:00
|
|
|
return (uint) -1;
|
2001-08-22 00:45:07 +02:00
|
|
|
to=new_str+(to - *start);
|
|
|
|
end=(*start=new_str)+ *max_length-1;
|
2001-02-13 11:39:50 +01:00
|
|
|
}
|
|
|
|
*to++= *from++;
|
|
|
|
}
|
|
|
|
if (!(rep_str = ((REPLACE_STRING*) rep_pos))->replace_string)
|
|
|
|
return (uint) (to - *start)-1;
|
|
|
|
to-=rep_str->to_offset;
|
|
|
|
for (pos=rep_str->replace_string; *pos ; pos++)
|
|
|
|
{
|
|
|
|
if (to == end)
|
|
|
|
{
|
|
|
|
(*max_length)*=2;
|
2001-08-22 00:45:07 +02:00
|
|
|
if (!(new_str=my_realloc(*start,*max_length,MYF(MY_WME))))
|
2001-02-13 11:39:50 +01:00
|
|
|
return (uint) -1;
|
2001-08-22 00:45:07 +02:00
|
|
|
to=new_str+(to - *start);
|
|
|
|
end=(*start=new_str)+ *max_length-1;
|
2001-02-13 11:39:50 +01:00
|
|
|
}
|
|
|
|
*to++= *pos;
|
|
|
|
}
|
|
|
|
if (!*(from-=rep_str->from_offset) && rep_pos->found != 2)
|
|
|
|
return (uint) (to - *start);
|
|
|
|
rep_pos=rep;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int initialize_replace_buffer(void)
|
|
|
|
{
|
|
|
|
out_length=8192;
|
|
|
|
if (!(out_buff=my_malloc(out_length,MYF(MY_WME))))
|
|
|
|
return(1);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void free_replace_buffer(void)
|
|
|
|
{
|
|
|
|
my_free(out_buff,MYF(MY_WME));
|
|
|
|
}
|
2003-06-27 15:29:10 +02:00
|
|
|
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
Replace results for a column
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
static void free_replace_column()
|
|
|
|
{
|
|
|
|
uint i;
|
|
|
|
for (i=0 ; i < max_replace_column ; i++)
|
|
|
|
{
|
|
|
|
if (replace_column[i])
|
|
|
|
{
|
|
|
|
my_free(replace_column[i], 0);
|
|
|
|
replace_column[i]= 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
max_replace_column= 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Get arguments for replace_columns. The syntax is:
|
|
|
|
replace-column column_number to_string [column_number to_string ...]
|
|
|
|
Where each argument may be quoted with ' or "
|
|
|
|
A argument may also be a variable, in which case the value of the
|
|
|
|
variable is replaced.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void get_replace_column(struct st_query *q)
|
|
|
|
{
|
|
|
|
char *from=q->first_argument;
|
|
|
|
char *buff,*start;
|
|
|
|
DBUG_ENTER("get_replace_columns");
|
|
|
|
|
|
|
|
free_replace_column();
|
|
|
|
if (!*from)
|
|
|
|
die("Missing argument in %s\n", q->query);
|
|
|
|
|
|
|
|
/* Allocate a buffer for results */
|
|
|
|
start=buff=my_malloc(strlen(from)+1,MYF(MY_WME | MY_FAE));
|
|
|
|
while (*from)
|
|
|
|
{
|
|
|
|
char *to;
|
|
|
|
uint column_number;
|
|
|
|
|
|
|
|
to= get_string(&buff, &from, q);
|
|
|
|
if (!(column_number= atoi(to)) || column_number > MAX_COLUMNS)
|
|
|
|
die("Wrong column number to replace_columns in %s\n", q->query);
|
|
|
|
if (!*from)
|
|
|
|
die("Wrong number of arguments to replace in %s\n", q->query);
|
|
|
|
to= get_string(&buff, &from, q);
|
|
|
|
my_free(replace_column[column_number-1], MY_ALLOW_ZERO_PTR);
|
|
|
|
replace_column[column_number-1]= my_strdup(to, MYF(MY_WME | MY_FAE));
|
|
|
|
set_if_bigger(max_replace_column, column_number);
|
|
|
|
}
|
|
|
|
my_free(start, MYF(0));
|
|
|
|
}
|
2004-05-25 21:00:14 +02:00
|
|
|
|
|
|
|
#ifdef __NETWARE__
|
|
|
|
|
|
|
|
/*
|
|
|
|
Substitute environment variables with text.
|
|
|
|
|
|
|
|
SYNOPSIS
|
|
|
|
subst_env_var()
|
|
|
|
arg String that should be substitute
|
|
|
|
|
|
|
|
DESCRIPTION
|
|
|
|
This function takes a string as an input and replaces the
|
|
|
|
environment variables, that starts with '$' character, with it value.
|
|
|
|
|
|
|
|
NOTES
|
|
|
|
Return string must be freed with my_free()
|
|
|
|
|
|
|
|
RETURN
|
|
|
|
String with environment variables replaced.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static char *subst_env_var(const char *str)
|
|
|
|
{
|
|
|
|
char *result;
|
2004-06-01 22:35:09 +02:00
|
|
|
char *pos;
|
2004-05-25 21:00:14 +02:00
|
|
|
|
|
|
|
result= pos= my_malloc(MAX_QUERY, MYF(MY_FAE));
|
|
|
|
while (*str)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
need this only when we want to provide the functionality of
|
|
|
|
escaping through \ 'backslash'
|
|
|
|
if ((result == pos && *str=='$') ||
|
|
|
|
(result != pos && *str=='$' && str[-1] !='\\'))
|
|
|
|
*/
|
|
|
|
if (*str == '$')
|
|
|
|
{
|
|
|
|
char env_var[256], *env_pos= env_var, *subst;
|
|
|
|
|
|
|
|
/* Search for end of environment variable */
|
|
|
|
for (str++;
|
|
|
|
*str && !isspace(*str) && *str != '\\' && *str != '/' &&
|
|
|
|
*str != '$';
|
|
|
|
str++)
|
2004-06-01 22:35:09 +02:00
|
|
|
*env_pos++= *str;
|
2004-05-25 21:00:14 +02:00
|
|
|
*env_pos= 0;
|
|
|
|
|
|
|
|
if (!(subst= getenv(env_var)))
|
|
|
|
{
|
|
|
|
my_free(result, MYF(0));
|
|
|
|
die("MYSQLTEST.NLM: Environment variable %s is not defined\n",
|
|
|
|
env_var);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* get the string to be substitued for env_var */
|
|
|
|
pos= strmov(pos, subst);
|
|
|
|
/* Process delimiter in *str again */
|
|
|
|
}
|
|
|
|
else
|
|
|
|
*pos++= *str++;
|
|
|
|
}
|
|
|
|
*pos= 0;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
popen replacement for Netware
|
|
|
|
|
|
|
|
SYNPOSIS
|
|
|
|
my_popen()
|
|
|
|
name Command to execute (with possible env variables)
|
|
|
|
mode Mode for popen.
|
|
|
|
|
|
|
|
NOTES
|
|
|
|
Environment variable expansion does not take place for popen function
|
|
|
|
on NetWare, so we use this function to wrap around popen to do this.
|
|
|
|
|
|
|
|
For the moment we ignore 'mode' and always use 'r0'
|
|
|
|
|
|
|
|
RETURN
|
|
|
|
# >= 0 File handle
|
|
|
|
-1 Error
|
|
|
|
*/
|
|
|
|
|
|
|
|
#undef popen /* Remove wrapper */
|
|
|
|
|
2004-06-01 22:35:09 +02:00
|
|
|
FILE *my_popen(const char *cmd, const char *mode __attribute__((unused)))
|
2004-05-25 21:00:14 +02:00
|
|
|
{
|
|
|
|
char *subst_cmd;
|
2004-06-01 22:35:09 +02:00
|
|
|
FILE *res_file;
|
|
|
|
|
2004-05-25 21:00:14 +02:00
|
|
|
subst_cmd= subst_env_var(cmd);
|
|
|
|
res_file= popen(subst_cmd, "r0");
|
|
|
|
my_free(subst_cmd, MYF(0));
|
|
|
|
return res_file;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* __NETWARE__ */
|