diff options
Diffstat (limited to 'libmysqld')
-rw-r--r-- | libmysqld/Makefile.am | 135 | ||||
-rw-r--r-- | libmysqld/copyright | 14 | ||||
-rw-r--r-- | libmysqld/embedded_priv.h | 32 | ||||
-rw-r--r-- | libmysqld/examples/Makefile.am | 26 | ||||
-rwxr-xr-x | libmysqld/examples/test-run | 138 | ||||
-rw-r--r-- | libmysqld/lib_load.cc | 44 | ||||
-rw-r--r-- | libmysqld/lib_sql.cc | 603 | ||||
-rw-r--r-- | libmysqld/lib_vio.c | 222 | ||||
-rw-r--r-- | libmysqld/libmysqld.c | 2091 | ||||
-rw-r--r-- | libmysqld/libmysqld.def | 72 |
10 files changed, 3377 insertions, 0 deletions
diff --git a/libmysqld/Makefile.am b/libmysqld/Makefile.am new file mode 100644 index 00000000000..edee13c98bb --- /dev/null +++ b/libmysqld/Makefile.am @@ -0,0 +1,135 @@ +# Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library 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 +# Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public +# License along with this library; if not, write to the Free +# Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, +# MA 02111-1307, USA +# +# This file is public domain and comes with NO WARRANTY of any kind + +MYSQLDATAdir = $(localstatedir) +MYSQLSHAREdir = $(pkgdatadir) +MYSQLBASEdir= $(prefix) + +DEFS = -DEMBEDDED_LIBRARY -DMYSQL_SERVER \ + -DDEFAULT_MYSQL_HOME="\"$(MYSQLBASEdir)\"" \ + -DDATADIR="\"$(MYSQLDATAdir)\"" \ + -DSHAREDIR="\"$(MYSQLSHAREdir)\"" +INCLUDES= @MT_INCLUDES@ @bdb_includes@ -I$(srcdir)/../include \ + -I../include -I$(srcdir)/.. -I$(top_srcdir) -I.. \ + -I../sql -I../regex + +noinst_LIBRARIES = libmysqld_int.a +pkglib_LIBRARIES = libmysqld.a +SUBDIRS = . examples +libmysqld_sources= libmysqld.c lib_sql.cc lib_load.cc +libmysqlsources = errmsg.c get_password.c password.c + +noinst_HEADERS = embedded_priv.h + +sqlsources = convert.cc derror.cc field.cc field_conv.cc filesort.cc \ + ha_innodb.cc ha_berkeley.cc ha_heap.cc ha_isam.cc ha_isammrg.cc \ + ha_myisam.cc ha_myisammrg.cc handler.cc sql_handler.cc \ + hostname.cc init.cc \ + item.cc item_buff.cc item_cmpfunc.cc item_create.cc \ + item_func.cc item_strfunc.cc item_sum.cc item_timefunc.cc \ + item_uniq.cc key.cc lock.cc log.cc log_event.cc mf_iocache.cc\ + mini_client.cc net_pkg.cc net_serv.cc opt_ft.cc opt_range.cc \ + opt_sum.cc procedure.cc records.cc sql_acl.cc \ + repl_failsafe.cc slave.cc \ + sql_analyse.cc sql_base.cc sql_cache.cc sql_class.cc \ + sql_crypt.cc sql_db.cc sql_delete.cc sql_insert.cc sql_lex.cc \ + sql_list.cc sql_manager.cc sql_map.cc sql_parse.cc \ + sql_rename.cc sql_repl.cc sql_select.cc sql_do.cc sql_show.cc \ + sql_string.cc sql_table.cc sql_test.cc sql_udf.cc \ + sql_update.cc sql_yacc.cc table.cc thr_malloc.cc time.cc \ + unireg.cc uniques.cc stacktrace.c sql_union.cc hash_filo.cc + +EXTRA_DIST = lib_vio.c + +libmysqld_int_a_SOURCES= $(libmysqld_sources) $(libmysqlsources) $(sqlsources) +libmysqld_a_SOURCES= + +# Don't depend on things from mit-pthreads + +OMIT_DEPENDENCIES = pthread.h stdio.h __stdio.h stdlib.h __stdlib.h math.h\ + __math.h time.h __time.h unistd.h __unistd.h types.h \ + xtypes.h ac-types.h posix.h string.h __string.h \ + errno.h socket.h inet.h dirent.h netdb.h \ + cleanup.h cond.h debug_out.h fd.h kernel.h mutex.h \ + prio_queue.h pthread_attr.h pthread_once.h queue.h\ + sleep.h specific.h version.h pwd.h timers.h uio.h \ + cdefs.h machdep.h signal.h __signal.h util.h lex.h \ + wait.h + +# automake misses these +sql_yacc.cc sql_yacc.h: $(top_srcdir)/sql/sql_yacc.yy + +# The following libraries should be included in libmysqld.a +INC_LIB= $(top_builddir)/regex/libregex.a \ + $(top_builddir)/myisam/libmyisam.a \ + $(top_builddir)/myisammrg/libmyisammrg.a \ + $(top_builddir)/heap/libheap.a \ + @innodb_libs@ @bdb_libs_with_path@ \ + $(top_builddir)/mysys/libmysys.a \ + $(top_builddir)/strings/libmystrings.a \ + $(top_builddir)/dbug/libdbug.a \ + $(top_builddir)/regex/libregex.a + +# +# To make it easy for the end user to use the embedded library we +# generate a total libmysqld.a from all library files, + +libmysqld.a: libmysqld_int.a $(INC_LIB) + if test ! -d tmp ; then mkdir tmp ; fi + rm -f $@ libmysqld_int2.a tmp/*.o tmp/*.a + cp $(INC_LIB) tmp + cp libmysqld_int.a libmysqld_int2.a ; \ + cd tmp ; \ + for file in *.a ; do \ + bfile=`basename $$file .a` ; \ + ar x $$file; \ + for obj in *.o ; do mv $$obj $${bfile}_$$obj ; done ; \ + ar q ../libmysqld_int2.a *.o ; \ + rm *.o ; \ + done + mv libmysqld_int2.a libmysqld.a + rm tmp/* + $(RANLIB) libmysqld.a + +## XXX: any time the client interface changes, we'll need to bump +## the version info for libmysqld; however, it's possible for the +## libmysqld interface to change without affecting the standard +## libmysqlclient interface. Should we make a separate version +## string for the two? +#libmysqld_la_LDFLAGS = -version-info @SHARED_LIB_VERSION@ +#CLEANFILES = $(libmysqld_la_LIBADD) libmysqld.la + +# This is called from the toplevel makefile +link_sources: + set -x; \ + for f in $(sqlsources); do \ + rm -f $(srcdir)/$$f; \ + @LN_CP_F@ $(srcdir)/../sql/$$f $(srcdir)/$$f; \ + done; \ + for f in $(libmysqlsources); do \ + rm -f $(srcdir)/$$f; \ + @LN_CP_F@ $(srcdir)/../libmysql/$$f $(srcdir)/$$f; \ + done + +clean-local: + rm -f `echo $(sqlsources) $(libmysqlsources) | sed "s;\.lo;.c;g"` \ + $(top_srcdir)/linked_libmysqld_sources + +# Don't update the files from bitkeeper +%::SCCS/s.% diff --git a/libmysqld/copyright b/libmysqld/copyright new file mode 100644 index 00000000000..0b4dd1725a2 --- /dev/null +++ b/libmysqld/copyright @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2000 + * SWsoft company + * + * This material is provided "as is", with absolutely no warranty expressed + * or implied. Any use is at your own risk. + * + * Permission to use or copy this software for any purpose is hereby granted + * without fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + * + */ diff --git a/libmysqld/embedded_priv.h b/libmysqld/embedded_priv.h new file mode 100644 index 00000000000..96d2c96ed0b --- /dev/null +++ b/libmysqld/embedded_priv.h @@ -0,0 +1,32 @@ +/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB + + 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. + + 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. + + 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 */ + +/* Prototypes for the embedded version of MySQL */ + +#include <my_global.h> +#include <mysql_embed.h> +#include <mysql.h> +#include <mysql_version.h> +#include <mysqld_error.h> +#include <my_pthread.h> + +C_MODE_START +extern void start_embedded_connection(NET * net); +extern void end_embedded_connection(NET * net); +extern void lib_connection_phase(NET *net, int phase); +extern bool lib_dispatch_command(enum enum_server_command command, NET *net, + const char *arg, ulong length); +C_MODE_END diff --git a/libmysqld/examples/Makefile.am b/libmysqld/examples/Makefile.am new file mode 100644 index 00000000000..ecfcbccd322 --- /dev/null +++ b/libmysqld/examples/Makefile.am @@ -0,0 +1,26 @@ +noinst_PROGRAMS = mysqltest mysql +client_sources = $(mysqltest_SOURCES) $(mysql_SOURCES) + +link_sources: + for f in $(client_sources); do \ + rm -f $(srcdir)/$$f; \ + @LN_CP_F@ $(srcdir)/../../client/$$f $(srcdir)/$$f; \ + done; + +DEFS = -DEMBEDDED_LIBRARY +INCLUDES = -I$(top_srcdir)/include $(openssl_includes) \ + -I$(srcdir) -I$(top_srcdir) -I$(top_srcdir)/client +LIBS = @LIBS@ +LDADD = @CLIENT_EXTRA_LDFLAGS@ ../libmysqld.a @innodb_system_libs@ @LIBDL@ $(CXXLDFLAGS) + +mysqltest_SOURCES = mysqltest.c + +mysql_SOURCES = mysql.cc readline.cc completion_hash.cc \ + my_readline.h sql_string.h completion_hash.h +mysql_LDADD = @readline_link@ @TERMCAP_LIB@ $(LDADD) + +clean: + rm -f $(client_sources) + +# Don't update the files from bitkeeper +%::SCCS/s.% diff --git a/libmysqld/examples/test-run b/libmysqld/examples/test-run new file mode 100755 index 00000000000..c7434488259 --- /dev/null +++ b/libmysqld/examples/test-run @@ -0,0 +1,138 @@ +#! /bin/sh + +# This is slapped together as a quick way to run the tests and +# is not meant for prime time. Please hack at it and submit +# changes, though, so we can gradually turn it into something +# that will run on all platforms (or incorporate it into the +# standard mysql-test-run). + +# All paths below must be relative to $test_data_dir +top_builddir=../.. +mysql_test_dir=$top_builddir/mysql-test +examples=$top_builddir/libmysqld/examples +mysqltest=$examples/mysqltest +datadir=$mysql_test_dir/var/master-data +test_data_dir=test +gdb=0 +list=0 +run= +tests= +start= +clean=1 + +cr=" +" +er="\b\b\b\b\b\b\b\b" + +usage () { + cat <<EOF +usage: $0 [-g|-h|-r] [test-name ...] + + -C | --noclean Do not remove old innodb and bdb files at start. + -g | --gdb run $mysqltest in gdb + -h | --help show this help + -l | --list ) list all available tests + -r | --run automatically 'run' program in gdb + -s t | --start=t start with test t (skip all tests before t) +EOF +} + +init_args="--server-arg=--language=$top_builddir/sql/share/english" +while test $# -gt 0 +do + arg= + argset=0 + case "$1" in + --?*=* ) arg=`echo "$1" | sed -e 's,^[^=][^=]*=,,'`; argset=1 ;; + esac + + case "$1" in + -g | --gdb ) gdb=1; shift;; + -h | --help | -\? ) usage; exit 0;; + -l | --list ) list=1 ; shift ;; + -r | --run ) run="${cr}run"; shift;; + --debug) init_args="$init_args --debug" ; shift ;; + -C | --noclean) clean=0 ; shift ;; + -s | --start=* ) + test $argset -eq 0 && { shift; arg="$1"; } + start="$arg" + shift + ;; + -* ) usage; exit 1;; + * ) tests="$tests $1"; shift;; + esac +done + +if test ! -d "$datadir/$test_data_dir" +then + echo "bad setup (is '$datadir/$test_data_dir'', missing ?)" >&2 + exit 1 +fi + +test -n "$tests" || + tests=`/bin/ls -1 "$mysql_test_dir"/t/*.test | grep -v '^.*/rpl[^/]*$' | \ + sed -e 's,^.*/,,' -e 's,.test$,,'` + +echo "cleaning data directory '$datadir/$test_data_dir'" +if test $clean = 1 +then + rm -f $datadir/ib_* $datadir/ibdata* + rm -f $datadir/log.00* + rm -f $datadir/test/*.db +fi +rm -f $datadir/../tmp/* +rm -f test-gdbinit + +TZ=GMT-3; export TZ + +# At least one of the tests needs the following environment variable +MYSQL_TEST_DIR=`( cd $mysql_test_dir ; pwd )` ; export MYSQL_TEST_DIR + +skip=1 +test -z "$start" && skip=0 + +for b in $tests +do + test $list -eq 1 && { echo " $b"; continue; } + test $skip -eq 1 && test -n "$start" && test "$start" = "$b" && skip=0 + test $skip -eq 1 && { echo "skipping '$b'"; continue; } + + t="t/$b.test" + r="r/$b.result" + + # Only test if $t exists; there is no $r for some tests + test -f $mysql_test_dir/$t || { + echo "test '$mysql_test_dir/$t' doesn't exist" >&2 + continue + } + args="$init_args -v --basedir=$mysql_test_dir/ -R $r -x $t --server-arg=--datadir=$datadir" + if test -f "$mysql_test_dir/t/$b-master.opt" ; then + args="$args --server-file=t/$b-master.opt" + fi + + args="$args $test_data_dir" # Add database last + echo "set args $args$run" > test-gdbinit + #if false && test -n "$run" + if test -n "$run" -o $gdb -eq 1 + then + echo -e "$er>>> $b" + else + echo -e "$er>>> $b> \c" + read junk + fi + if test $gdb -eq 1 + then + if [ -x "$top_builddir/libtool" ]; then + $top_builddir/libtool gdb -x test-gdbinit -q $mysqltest + else + gdb -x test-gdbinit -q $mysqltest + fi + res=$? + rm -f test-gdbinit + else + $mysqltest $args + res=$? + fi + + test $res -eq 0 -o $res -eq 2 || echo "!!! error: $res" +done diff --git a/libmysqld/lib_load.cc b/libmysqld/lib_load.cc new file mode 100644 index 00000000000..3db5a2488d1 --- /dev/null +++ b/libmysqld/lib_load.cc @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2000 + * SWsoft company + * + * This material is provided "as is", with absolutely no warranty expressed + * or implied. Any use is at your own risk. + * + * Permission to use or copy this software for any purpose is hereby granted + * without fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + * + */ +/* Copy data from a textfile to table */ + +#include "mysql_priv.h" +#include <my_dir.h> +#include <m_ctype.h> + + +int +mysql_load_internal(THD * thd, sql_exchange * ex, TABLE_LIST * table_list, + List<Item> & fields, enum enum_duplicates handle_duplicates, + bool read_file_from_client, thr_lock_type lock_type); + +int +mysql_load(THD * thd, sql_exchange * ex, TABLE_LIST * table_list, + List<Item> & fields, enum enum_duplicates handle_duplicates, + bool read_file_from_client, thr_lock_type lock_type) +{ + read_file_from_client = 0; //server is always in the same process + return mysql_load_internal(thd, ex, table_list, fields, handle_duplicates, + read_file_from_client, lock_type); +} + +#define mysql_load mysql_load_internal + + +#if defined (__WIN__) +#include "../sql/sql_load.cpp" +#else +#include "../sql/sql_load.cc" +#endif diff --git a/libmysqld/lib_sql.cc b/libmysqld/lib_sql.cc new file mode 100644 index 00000000000..1ef84607155 --- /dev/null +++ b/libmysqld/lib_sql.cc @@ -0,0 +1,603 @@ +/* + * Copyright (c) 2000 + * SWsoft company + * + * This material is provided "as is", with absolutely no warranty expressed + * or implied. Any use is at your own risk. + * + * Permission to use or copy this software for any purpose is hereby granted + * without fee, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + * + */ +#include "embedded_priv.h" +#include "sys/types.h" +#include "../regex/regex.h" +#include "my_sys.h" + +/* + The following is needed to not cause conflicts when we include mysqld.cc +*/ + +#define main main1 +#define mysql_unix_port mysql_inix_port1 +#define mysql_port mysql_port1 +#if !defined(__WIN__) +#define net_read_timeout net_read_timeout1 +#define net_write_timeout net_write_timeout1 +#endif +#define changeable_vars changeable_vars1 + +extern "C" +{ +#include "mysql_com.h" +#include "lib_vio.c" +} + + +class THD; + +static int check_connections1(THD * thd); +static int check_connections2(THD * thd); +static bool check_user(THD *thd, enum_server_command command, + const char *user, const char *passwd, const char *db, + bool check_count); +void free_defaults_internal(char ** argv) {if (argv) free_defaults(argv);} +#define free_defaults free_defaults_internal + +#if defined (__WIN__) +#include "../sql/mysqld.cpp" +#else +#include "../sql/mysqld.cc" +#endif + +#define SCRAMBLE_LENGTH 8 +C_MODE_START + +char * get_mysql_home(){ return mysql_home;}; +char * get_mysql_real_data_home(){ return mysql_real_data_home;}; + + +bool lib_dispatch_command(enum enum_server_command command, NET *net, + const char *arg, ulong length) +{ + THD *thd=(THD *) net->vio->dest_thd; + thd->store_globals(); // Fix if more than one connect + thd->net.last_error[0]=0; // Clear error message + thd->net.last_errno=0; + + net_new_transaction(&thd->net); + return dispatch_command(command, thd, (char *) arg, length + 1); +} + + +void lib_connection_phase(NET * net, int phase) +{ + THD * thd; + thd = (THD *)(net->vio->dest_thd); + if (thd) + { + switch (phase) + { + case 2: + check_connections2(thd); + break; + } + } +} +C_MODE_END + + +void start_embedded_conn1(NET * net) +{ + THD * thd = new THD; + my_net_init(&thd->net,NULL); + /* if (protocol_version>9) */ + thd->net.return_errno=1; + thd->thread_id = thread_id++; + + Vio * v = net->vio; + if (!v) + { + v = vio_new(0,VIO_CLOSED,0); + net->vio = v; + } + if (v) + { + v -> dest_thd = thd; + } + thd->net.vio = v; + if (thd->store_globals()) + { + fprintf(stderr,"store_globals failed.\n"); + return; + } + + thd->mysys_var=my_thread_var; + thd->dbug_thread_id=my_thread_id(); + thd->thread_stack= (char*) &thd; + + if (thd->max_join_size == HA_POS_ERROR) + thd->options |= OPTION_BIG_SELECTS; + + thd->proc_info=0; // Remove 'login' + thd->command=COM_SLEEP; + thd->version=refresh_version; + thd->set_time(); + init_sql_alloc(&thd->mem_root,8192,8192); + + check_connections1(thd); +} + + + + +static int +check_connections1(THD *thd) +{ + uint connect_errors=0; + NET *net= &thd->net; + /* + ** store the connection details + */ + DBUG_PRINT("info", (("check_connections called by thread %d"), + thd->thread_id)); + DBUG_PRINT("general",("New connection received on %s", + vio_description(net->vio))); + if (!thd->host) // If TCP/IP connection + { + thd->host=(char*) localhost; + } + else /* Hostname given means that the connection was on a socket */ + { + DBUG_PRINT("general",("Host: %s",thd->host)); + thd->ip=0; + bzero((char*) &thd->remote,sizeof(struct sockaddr)); + } + //vio_keepalive(net->vio, TRUE); + + /* nasty, but any other way? */ + uint pkt_len = 0; + + char buff[80],*end; + int client_flags = CLIENT_LONG_FLAG | CLIENT_CONNECT_WITH_DB | + CLIENT_TRANSACTIONS; + LINT_INIT(pkt_len); + + end=strmov(buff,server_version)+1; + int4store((uchar*) end,thd->thread_id); + end+=4; + memcpy(end,thd->scramble,SCRAMBLE_LENGTH+1); + end+=SCRAMBLE_LENGTH +1; + int2store(end,client_flags); + end[2]=MY_CHARSET_CURRENT; + +#define MIN_HANDSHAKE_SIZE 6 + + int2store(end+3,thd->server_status); + bzero(end+5,13); + end+=18; + if (net_write_command(net,protocol_version, buff, + (uint) (end-buff))) + { + inc_host_errors(&thd->remote.sin_addr); + return(ER_HANDSHAKE_ERROR); + } + return 0; +} + +static int +check_connections2(THD * thd) +{ + uint connect_errors=0; + uint pkt_len = 0; + NET * net = &thd -> net; + if (protocol_version>9) net -> return_errno=1; + + if ( (pkt_len=my_net_read(net)) == packet_error || + pkt_len < MIN_HANDSHAKE_SIZE) + { + inc_host_errors(&thd->remote.sin_addr); + return(ER_HANDSHAKE_ERROR); + } + +#ifdef _CUSTOMCONFIG_ +#include "_cust_sql_parse.h" +#endif + if (connect_errors) + reset_host_errors(&thd->remote.sin_addr); + if (thd->packet.alloc(net_buffer_length)) + return(ER_OUT_OF_RESOURCES); + + thd->client_capabilities=uint2korr(net->read_pos); + + thd->max_packet_length=uint3korr(net->read_pos+2); + char *user= (char*) net->read_pos+5; + char *passwd= strend(user)+1; + char *db=0; + if (passwd[0] && strlen(passwd) != SCRAMBLE_LENGTH) + return ER_HANDSHAKE_ERROR; + if (thd->client_capabilities & CLIENT_CONNECT_WITH_DB) + db=strend(passwd)+1; + if (thd->client_capabilities & CLIENT_INTERACTIVE) + thd->inactive_timeout=net_interactive_timeout; + if (thd->client_capabilities & CLIENT_TRANSACTIONS) + thd->net.return_status= &thd->server_status; + net->timeout=net_read_timeout; + if (check_user(thd,COM_CONNECT, user, passwd, db, 1)) + return (-1); + thd->password=test(passwd[0]); + return 0; +} + +static bool check_user(THD *thd,enum_server_command command, const char *user, + const char *passwd, const char *db, bool check_count) +{ + NET *net= &thd->net; + USER_RESOURCES ur; + thd->db=0; + + if (!(thd->user = my_strdup(user, MYF(0)))) + { + send_error(net,ER_OUT_OF_RESOURCES); + return 1; + } + thd->master_access=acl_getroot(thd, thd->host, thd->ip, thd->user, + passwd, thd->scramble, &thd->priv_user, + protocol_version == 9 || + !(thd->client_capabilities & + CLIENT_LONG_PASSWORD),&ur); + DBUG_PRINT("info", + ("Capabilities: %d packet_length: %d Host: '%s' User: '%s' Using password: %s Access: %u db: '%s'", + thd->client_capabilities, thd->max_packet_length, + thd->host_or_ip, thd->priv_user, + passwd[0] ? "yes": "no", + thd->master_access, thd->db ? thd->db : "*none*")); + if (thd->master_access & NO_ACCESS) + { + net_printf(net, ER_ACCESS_DENIED_ERROR, + thd->user, + thd->host_or_ip, + passwd[0] ? ER(ER_YES) : ER(ER_NO)); + mysql_log.write(thd,COM_CONNECT,ER(ER_ACCESS_DENIED_ERROR), + thd->user, + thd->host_or_ip, + passwd[0] ? ER(ER_YES) : ER(ER_NO)); + return(1); // Error already given + } + if (check_count) + { + VOID(pthread_mutex_lock(&LOCK_thread_count)); + bool tmp=(thread_count - delayed_insert_threads >= max_connections && + !(thd->master_access & PROCESS_ACL)); + VOID(pthread_mutex_unlock(&LOCK_thread_count)); + if (tmp) + { // Too many connections + send_error(net, ER_CON_COUNT_ERROR); + return(1); + } + } + mysql_log.write(thd,command, + (thd->priv_user == thd->user ? + (char*) "%s@%s on %s" : + (char*) "%s@%s as anonymous on %s"), + user, + thd->host_or_ip, + db ? db : (char*) ""); + thd->db_access=0; + if (db && db[0]) + return test(mysql_change_db(thd,db)); + else + send_ok(net); // Ready to handle questions + return 0; // ok +} + + +extern "C" +{ + +static my_bool inited, org_my_init_done; + +int STDCALL mysql_server_init(int argc, char **argv, char **groups) +{ + char glob_hostname[FN_REFLEN]; + + /* This mess is to allow people to call the init function without + * having to mess with a fake argv */ + int *argcp; + char ***argvp; + int fake_argc = 1; + char *fake_argv[] = { (char *)"", 0 }; + const char *fake_groups[] = { "server", "embedded", 0 }; + if (argc) + { + argcp = &argc; + argvp = (char***) &argv; + } + else + { + argcp = &fake_argc; + argvp = (char ***) &fake_argv; + } + if (!groups) + groups = (char**) fake_groups; + + my_umask=0660; // Default umask for new files + my_umask_dir=0700; // Default umask for new directories + + /* Only call MY_INIT() if it hasn't been called before */ + if (!inited) + { + inited=1; + org_my_init_done=my_init_done; + } + if (!org_my_init_done) + { + MY_INIT((char *)"mysql_embedded"); // init my_sys library & pthreads + } + + tzset(); // Set tzname + + start_time=time((time_t*) 0); +#ifdef HAVE_TZNAME +#if defined(HAVE_LOCALTIME_R) && defined(_REENTRANT) + { + struct tm tm_tmp; + localtime_r(&start_time,&tm_tmp); + strmov(time_zone,tzname[tm_tmp.tm_isdst != 0 ? 1 : 0]); + } +#else + { + struct tm *start_tm; + start_tm=localtime(&start_time); + strmov(time_zone,tzname[start_tm->tm_isdst != 0 ? 1 : 0]); + } +#endif +#endif + + if (gethostname(glob_hostname,sizeof(glob_hostname)-4) < 0) + strmov(glob_hostname,"mysql"); +#ifndef DBUG_OFF + strcat(server_version,"-debug"); +#endif + strcat(server_version,"-embedded"); + load_defaults("my", (const char **) groups, argcp, argvp); + defaults_argv=*argvp; + mysql_tmpdir=getenv("TMPDIR"); /* Use this if possible */ +#if defined( __WIN__) || defined(OS2) + if (!mysql_tmpdir) + mysql_tmpdir=getenv("TEMP"); + if (!mysql_tmpdir) + mysql_tmpdir=getenv("TMP"); +#endif + if (!mysql_tmpdir || !mysql_tmpdir[0]) + mysql_tmpdir=(char*) P_tmpdir; /* purecov: inspected */ + set_options(); + get_options(*argcp, *argvp); + + if (opt_log || opt_update_log || opt_slow_log || opt_bin_log) + strcat(server_version,"-log"); + DBUG_PRINT("info",("%s Ver %s for %s on %s\n",my_progname, + server_version, SYSTEM_TYPE,MACHINE_TYPE)); + + /* These must be set early */ + + (void) pthread_mutex_init(&LOCK_mysql_create_db,MY_MUTEX_INIT_SLOW); + (void) pthread_mutex_init(&LOCK_Acl,MY_MUTEX_INIT_SLOW); + (void) pthread_mutex_init(&LOCK_grant,MY_MUTEX_INIT_FAST); + (void) pthread_mutex_init(&LOCK_open,MY_MUTEX_INIT_FAST); + (void) pthread_mutex_init(&LOCK_thread_count,MY_MUTEX_INIT_FAST); + (void) pthread_mutex_init(&LOCK_mapped_file,MY_MUTEX_INIT_SLOW); + (void) pthread_mutex_init(&LOCK_status,MY_MUTEX_INIT_FAST); + (void) pthread_mutex_init(&LOCK_error_log,MY_MUTEX_INIT_FAST); + (void) pthread_mutex_init(&LOCK_delayed_insert,MY_MUTEX_INIT_FAST); + (void) pthread_mutex_init(&LOCK_delayed_status,MY_MUTEX_INIT_FAST); + (void) pthread_mutex_init(&LOCK_delayed_create,MY_MUTEX_INIT_SLOW); + (void) pthread_mutex_init(&LOCK_manager,MY_MUTEX_INIT_FAST); + (void) pthread_mutex_init(&LOCK_crypt,MY_MUTEX_INIT_FAST); + (void) pthread_mutex_init(&LOCK_bytes_sent,MY_MUTEX_INIT_FAST); + (void) pthread_mutex_init(&LOCK_bytes_received,MY_MUTEX_INIT_FAST); + (void) pthread_mutex_init(&LOCK_timezone,MY_MUTEX_INIT_FAST); + (void) pthread_mutex_init(&LOCK_user_conn, MY_MUTEX_INIT_FAST); + (void) pthread_cond_init(&COND_thread_count,NULL); + (void) pthread_cond_init(&COND_refresh,NULL); + (void) pthread_cond_init(&COND_thread_cache,NULL); + (void) pthread_cond_init(&COND_flush_thread_cache,NULL); + (void) pthread_cond_init(&COND_manager,NULL); + (void) pthread_cond_init(&COND_binlog_update, NULL); + + if (set_default_charset_by_name(default_charset, MYF(MY_WME))) + { + mysql_server_end(); + return 1; + } + charsets_list = list_charsets(MYF(MY_COMPILED_SETS|MY_CONFIG_SETS)); + + /* Parameter for threads created for connections */ + (void) pthread_attr_init(&connection_attrib); + (void) pthread_attr_setdetachstate(&connection_attrib, + PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&connection_attrib,thread_stack); + pthread_attr_setscope(&connection_attrib, PTHREAD_SCOPE_SYSTEM); + +#if defined( SET_RLIMIT_NOFILE) || defined( OS2) + /* connections and databases neads lots of files */ + { + uint wanted_files=10+(uint) max(max_connections*5, + max_connections+table_cache_size*2); + uint files=set_maximum_open_files(wanted_files); + if (files && files < wanted_files) // Some systems return 0 + { + max_connections= (ulong) min((files-10),max_connections); + table_cache_size= (ulong) max((files-10-max_connections)/2,64); + DBUG_PRINT("warning", + ("Changed limits: max_connections: %ld table_cache: %ld", + max_connections,table_cache_size)); + sql_print_error("Warning: Changed limits: max_connections: %ld table_cache: %ld",max_connections,table_cache_size); + } + } +#endif + unireg_init(opt_specialflag); /* Set up extern variabels */ + init_errmessage(); /* Read error messages from file */ + lex_init(); + item_init(); + mysys_uses_curses=0; +#ifdef USE_REGEX + regex_init(); +#endif + if (use_temp_pool && bitmap_init(&temp_pool,1024,1)) + { + mysql_server_end(); + return 1; + } + + /* + ** We have enough space for fiddling with the argv, continue + */ + umask(((~my_umask) & 0666)); + table_cache_init(); + hostname_cache_init(); + /*sql_cache_init();*/ + randominit(&sql_rand,(ulong) start_time,(ulong) start_time/2); + reset_floating_point_exceptions(); + init_thr_lock(); + init_slave_list(); + + /* Setup log files */ + if (opt_log) + open_log(&mysql_log, glob_hostname, opt_logname, ".log", LOG_NORMAL); + if (opt_update_log) + { + open_log(&mysql_update_log, glob_hostname, opt_update_logname, "", + LOG_NEW); + using_update_log=1; + } + if (opt_bin_log) + { + if (!opt_bin_logname) + { + char tmp[FN_REFLEN]; + strmake(tmp,glob_hostname,FN_REFLEN-5); + strmov(strcend(tmp,'.'),"-bin"); + opt_bin_logname=my_strdup(tmp,MYF(MY_WME)); + } + mysql_bin_log.set_index_file_name(opt_binlog_index_name); + open_log(&mysql_bin_log, glob_hostname, opt_bin_logname, "-bin", + LOG_BIN); + using_update_log=1; + } + + if (opt_slow_log) + open_log(&mysql_slow_log, glob_hostname, opt_slow_logname, "-slow.log", + LOG_NORMAL); + if (ha_init()) + { + sql_print_error("Can't init databases"); + exit(1); + } + ha_key_cache(); +#ifdef HAVE_MLOCKALL + if (locked_in_memory && !geteuid()) + { + ha_key_cache(); + if (mlockall(MCL_CURRENT)) + { + sql_print_error("Warning: Failed to lock memory. Errno: %d\n",errno); + } + else + locked_in_memory=1; + } +#else + locked_in_memory=0; +#endif + + if (opt_myisam_log) + (void) mi_log( 1 ); + ft_init_stopwords(ft_precompiled_stopwords); /* SerG */ + + /* + init signals & alarm + After this we can't quit by a simple unireg_abort + */ + error_handler_hook = my_message_sql; + if (pthread_key_create(&THR_THD,NULL) || pthread_key_create(&THR_NET,NULL) || + pthread_key_create(&THR_MALLOC,NULL)) + { + sql_print_error("Can't create thread-keys"); + exit(1); + } + opt_noacl = 1; // No permissions + if (acl_init(opt_noacl)) + { + mysql_server_end(); + return 1; + } + +#ifdef HAVE_DLOPEN + if (!opt_noacl) + udf_init(); +#endif + + (void) thr_setconcurrency(concurrency); // 10 by default + + if ( +#ifdef HAVE_BERKELEY_DB + !berkeley_skip || +#endif + (flush_time && flush_time != ~(ulong) 0L)) + { + pthread_t hThread; + if (pthread_create(&hThread,&connection_attrib,handle_manager,0)) + { + sql_print_error("Warning: Can't create thread to manage maintenance"); + mysql_server_end(); + return 1; + } + } + return 0; +} + + +void STDCALL mysql_server_end() +{ + clean_up(0); +#ifdef THREAD + /* Don't call my_thread_end() if the application is using MY_INIT() */ + if (!org_my_init_done) + my_thread_end(); +#endif + /* If library called my_init(), free memory allocated by it */ + if (!org_my_init_done) + my_end(0); +} + +my_bool STDCALL mysql_thread_init() +{ +#ifdef THREAD + return my_thread_init(); +#else + return 0; +#endif +} + +void STDCALL mysql_thread_end() +{ +#ifdef THREAD + my_thread_end(); +#endif +} + +void start_embedded_connection(NET * net) +{ + start_embedded_conn1(net); +} + +void end_embedded_connection(NET * net) +{ + THD *thd = (THD *) net->vio->dest_thd; + delete thd; +} + +} /* extern "C" */ diff --git a/libmysqld/lib_vio.c b/libmysqld/lib_vio.c new file mode 100644 index 00000000000..5460c546faa --- /dev/null +++ b/libmysqld/lib_vio.c @@ -0,0 +1,222 @@ +/* Copyright (C) 2000 MySQL AB + + 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. + + 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. + + 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 */ + +/* + Note that we can't have assertion on file descriptors; The reason for + this is that during mysql shutdown, another thread can close a file + we are working on. In this case we should just return read errors from + the file descriptior. +*/ + +#include <my_global.h> +#include "mysql_embed.h" +#include "mysql.h" + +#ifndef HAVE_VIO /* is Vio enabled */ + +#include <errno.h> +#include <my_sys.h> +#include <violite.h> +#include <my_sys.h> +#include <my_net.h> +#include <m_string.h> +#include <dbug.h> +#include <assert.h> + +#ifndef __WIN__ +#define HANDLE void * +#endif + +struct st_vio +{ + my_socket sd; /* my_socket - real or imaginary */ + HANDLE hPipe; + my_bool localhost; /* Are we from localhost? */ + int fcntl_mode; /* Buffered fcntl(sd,F_GETFL) */ + struct sockaddr_in local; /* Local internet address */ + struct sockaddr_in remote; /* Remote internet address */ + enum enum_vio_type type; /* Type of connection */ + char desc[30]; /* String description */ + void *dest_thd; + char *packets, **last_packet; + char *where_in_packet, *end_of_packet; + my_bool reading; + MEM_ROOT root; +}; + +/* Initialize the communication buffer */ + +Vio *vio_new(my_socket sd, enum enum_vio_type type, my_bool localhost) +{ + DBUG_ENTER("vio_new"); + Vio * vio; + + if ((vio= (Vio *) my_malloc(sizeof(*vio),MYF(MY_WME|MY_ZEROFILL)))) + { + init_alloc_root(&vio->root, 8192, 8192); + vio->root.min_malloc = sizeof(char *) + 4; + vio->last_packet = &vio->packets; + } + DBUG_RETURN(vio); +} + + +#ifdef __WIN__ + +Vio *vio_new_win32pipe(HANDLE hPipe) +{ + return (NULL); +} + +#endif + +void vio_delete(Vio * vio) +{ + DBUG_ENTER("vio_delete"); + if (vio) + { + if (vio->type != VIO_CLOSED) + vio_close(vio); + free_root(&vio->root, MYF(0)); + my_free((gptr) vio, MYF(0)); + } + DBUG_VOID_RETURN; +} + +void vio_reset(Vio *vio) +{ + DBUG_ENTER("vio_reset"); + free_root(&vio->root, MYF(MY_KEEP_PREALLOC)); + vio->packets = vio->where_in_packet = vio->end_of_packet = 0; + vio->last_packet = &vio->packets; + DBUG_VOID_RETURN; +} + +int vio_errno(Vio *vio __attribute__((unused))) +{ + return socket_errno; /* On Win32 this mapped to WSAGetLastError() */ +} + +int vio_read(Vio * vio, gptr buf, int size) +{ + vio->reading = 1; + if (vio->where_in_packet >= vio->end_of_packet) + { + DBUG_ASSERT(vio->packets); + vio->where_in_packet = vio->packets + sizeof(char *) + 4; + vio->end_of_packet = vio->where_in_packet + + uint4korr(vio->packets + sizeof(char *)); + vio->packets = *(char **)vio->packets; + } + if (vio->where_in_packet + size > vio->end_of_packet) + size = vio->end_of_packet - vio->where_in_packet; + memcpy(buf, vio->where_in_packet, size); + vio->where_in_packet += size; + return (size); +} + +int vio_write(Vio * vio, const gptr buf, int size) +{ + DBUG_ENTER("vio_write"); + char *packet; + if (vio->reading) + { + vio->reading = 0; + vio_reset(vio); + } + if ((packet = alloc_root(&vio->root, sizeof(char*) + 4 + size))) + { + *vio->last_packet = packet; + vio->last_packet = (char **)packet; + *((char **)packet) = 0; /* Set forward link to 0 */ + packet += sizeof(char *); + int4store(packet, size); + memcpy(packet + 4, buf, size); + } + else + size= -1; + DBUG_RETURN(size); +} + +int vio_blocking(Vio * vio, my_bool set_blocking_mode) +{ + return (0); +} + +my_bool +vio_is_blocking(Vio * vio) +{ + return(0); +} + +int vio_fastsend(Vio * vio) +{ + return(0); +} + +int vio_keepalive(Vio* vio, my_bool set_keep_alive) +{ + return (0); +} + + +my_bool +vio_should_retry(Vio * vio __attribute__((unused))) +{ + int en = socket_errno; + return (en == SOCKET_EAGAIN || en == SOCKET_EINTR || + en == SOCKET_EWOULDBLOCK); +} + + +int vio_close(Vio * vio) +{ + return(0); +} + + +const char *vio_description(Vio * vio) +{ + return "embedded vio"; +} + +enum enum_vio_type vio_type(Vio* vio) +{ + return VIO_CLOSED; +} + +my_socket vio_fd(Vio* vio) +{ + return 0; +} + + +my_bool vio_peer_addr(Vio * vio, char *buf) +{ + return(0); +} + + +void vio_in_addr(Vio *vio, struct in_addr *in) +{ +} + +my_bool vio_poll_read(Vio *vio,uint timeout) +{ + return 0; +} + +#endif /* HAVE_VIO */ diff --git a/libmysqld/libmysqld.c b/libmysqld/libmysqld.c new file mode 100644 index 00000000000..59e6239c1e7 --- /dev/null +++ b/libmysqld/libmysqld.c @@ -0,0 +1,2091 @@ +/* Copyright (C) 2000 MySQL AB + + 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. + + 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. + + 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 */ + +#include "embedded_priv.h" +#include <my_sys.h> +#include <mysys_err.h> +#include <m_string.h> +#include <m_ctype.h> +#include "errmsg.h" +#include <violite.h> +#include <sys/stat.h> +#include <signal.h> +#include <time.h> +#ifdef HAVE_PWD_H +#include <pwd.h> +#endif +#if !defined(MSDOS) && !defined(__WIN__) +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#ifdef HAVE_SELECT_H +# include <select.h> +#endif +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif +#endif +#ifdef HAVE_SYS_UN_H +# include <sys/un.h> +#endif +#ifndef INADDR_NONE +#define INADDR_NONE -1 +#endif + +static my_bool mysql_client_init=0; +uint mysql_port=0; +my_string mysql_unix_port=0; + +#define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | CLIENT_TRANSACTIONS) + +#if defined(MSDOS) || defined(__WIN__) +#define ERRNO WSAGetLastError() +#define perror(A) +#else +#include <errno.h> +#define ERRNO errno +#define SOCKET_ERROR -1 +#define closesocket(A) close(A) +#endif + +static void mysql_once_init(void); +static MYSQL_DATA *read_rows (MYSQL *mysql,MYSQL_FIELD *fields, + uint field_count); +static int read_one_row(MYSQL *mysql,uint fields,MYSQL_ROW row, + ulong *lengths); +static void end_server(MYSQL *mysql); +static void read_user_name(char *name); +static void append_wild(char *to,char *end,const char *wild); +static int send_file_to_server(MYSQL *mysql,const char *filename); +static ulong mysql_sub_escape_string(CHARSET_INFO *charset_info, char *to, + const char *from, ulong length); + +#define init_sigpipe_variables +#define set_sigpipe(mysql) +#define reset_sigpipe(mysql) + +/***************************************************************************** +** read a packet from server. Give error message if socket was down +** or packet is an error message +*****************************************************************************/ + +ulong +net_safe_read(MYSQL *mysql) +{ + NET *net= &mysql->net; + uint len=0; + //init_sigpipe_variables + /* Don't give sigpipe errors if the client doesn't want them */ + set_sigpipe(mysql); + if (net->vio != 0) + len=my_net_read(net); + reset_sigpipe(mysql); + if (len == packet_error || len == 0) + { + DBUG_PRINT("error",("Wrong connection or packet. fd: %s len: %d", + vio_description(net->vio),len)); + end_server(mysql); + net->last_errno=(net->last_errno == ER_NET_PACKET_TOO_LARGE ? + CR_NET_PACKET_TOO_LARGE: + CR_SERVER_LOST); + strmov(net->last_error,ER(net->last_errno)); + return(packet_error); + } + if (net->read_pos[0] == 255) + { + + if (len > 3) + { + char *pos=(char*) net->read_pos+1; + if (mysql->protocol_version > 9) + { /* New client protocol */ + net->last_errno=uint2korr(pos); + pos+=2; + len-=2; + } + else + { + net->last_errno=CR_UNKNOWN_ERROR; + len--; + } + (void) strmake(net->last_error,(char*) pos, + min(len,sizeof(net->last_error)-1)); + } + else + { + net->last_errno=CR_UNKNOWN_ERROR; + (void) strmov(net->last_error,ER(net->last_errno)); + } + DBUG_PRINT("error",("Got error: %d (%s)", net->last_errno, + net->last_error)); + return(packet_error); + } + return len; +} + + +/* Get the length of next field. Change parameter to point at fieldstart */ +static ulong +net_field_length(uchar **packet) +{ + reg1 uchar *pos= *packet; + if (*pos < 251) + { + (*packet)++; + return (ulong) *pos; + } + if (*pos == 251) + { + (*packet)++; + return NULL_LENGTH; + } + if (*pos == 252) + { + (*packet)+=3; + return (ulong) uint2korr(pos+1); + } + if (*pos == 253) + { + (*packet)+=4; + return (ulong) uint3korr(pos+1); + } + (*packet)+=9; /* Must be 254 when here */ + return (ulong) uint4korr(pos+1); +} + +/* Same as above, but returns ulonglong values */ + +static my_ulonglong +net_field_length_ll(uchar **packet) +{ + reg1 uchar *pos= *packet; + if (*pos < 251) + { + (*packet)++; + return (my_ulonglong) *pos; + } + if (*pos == 251) + { + (*packet)++; + return (my_ulonglong) NULL_LENGTH; + } + if (*pos == 252) + { + (*packet)+=3; + return (my_ulonglong) uint2korr(pos+1); + } + if (*pos == 253) + { + (*packet)+=4; + return (my_ulonglong) uint3korr(pos+1); + } + (*packet)+=9; /* Must be 254 when here */ +#ifdef NO_CLIENT_LONGLONG + return (my_ulonglong) uint4korr(pos+1); +#else + return (my_ulonglong) uint8korr(pos+1); +#endif +} + + +static void free_rows(MYSQL_DATA *cur) +{ + if (cur) + { + free_root(&cur->alloc,MYF(0)); + my_free((gptr) cur,MYF(0)); + } +} + + +int +simple_command(MYSQL *mysql,enum enum_server_command command, const char *arg, + ulong length, my_bool skipp_check) +{ + NET *net= &mysql->net; + int result= -1; + + /* Check that we are calling the client functions in right order */ + if (mysql->status != MYSQL_STATUS_READY) + { + strmov(net->last_error,ER(mysql->net.last_errno=CR_COMMANDS_OUT_OF_SYNC)); + goto end; + } + + /* Clear result variables */ + mysql->net.last_error[0]=0; + mysql->net.last_errno=0; + mysql->info=0; + mysql->affected_rows= ~(my_ulonglong) 0; + + /* Clear receive buffer and vio packet list */ + net_clear(net); + vio_reset(net->vio); + + result = lib_dispatch_command(command, net, arg,length); + if (!skipp_check) + result= ((mysql->packet_length=net_safe_read(mysql)) == packet_error ? + -1 : 0); + end: + return result; +} + + +static void free_old_query(MYSQL *mysql) +{ + DBUG_ENTER("free_old_query"); + if (mysql->fields) + free_root(&mysql->field_alloc,MYF(0)); + init_alloc_root(&mysql->field_alloc,8192,0); /* Assume rowlength < 8192 */ + mysql->fields=0; + mysql->field_count=0; /* For API */ + DBUG_VOID_RETURN; +} + +#ifdef HAVE_GETPWUID +struct passwd *getpwuid(uid_t); +char* getlogin(void); +#endif + +#if !defined(MSDOS) && ! defined(VMS) && !defined(__WIN__) +static void read_user_name(char *name) +{ + DBUG_ENTER("read_user_name"); + if (geteuid() == 0) + (void) strmov(name,"root"); /* allow use of surun */ + else + { +#ifdef HAVE_GETPWUID + struct passwd *skr; + const char *str; +/*#ifdef __cplusplus + extern "C" struct passwd *getpwuid(uid_t); + extern "C" { char* getlogin(void); } +#else + char * getlogin(); + struct passwd *getpwuid(uid_t); +#endif +*/ + if ((str=getlogin()) == NULL) + { + if ((skr=getpwuid(geteuid())) != NULL) + str=skr->pw_name; + else if (!(str=getenv("USER")) && !(str=getenv("LOGNAME")) && + !(str=getenv("LOGIN"))) + str="UNKNOWN_USER"; + } + (void) strmake(name,str,USERNAME_LENGTH); +#elif HAVE_CUSERID + (void) cuserid(name); +#else + strmov(name,"UNKNOWN_USER"); +#endif + } + DBUG_VOID_RETURN; +} + +#else /* If MSDOS || VMS */ + +static void read_user_name(char *name) +{ + char *str=getenv("USER"); + strmov(name,str ? str : "ODBC"); /* ODBC will send user variable */ +} + +#endif + +#ifdef __WIN__ +static my_bool is_NT(void) +{ + char *os=getenv("OS"); + return (os && !strcmp(os, "Windows_NT")) ? 1 : 0; +} +#endif + +/* +** Expand wildcard to a sql string +*/ + +static void +append_wild(char *to, char *end, const char *wild) +{ + end-=5; /* Some extra */ + if (wild && wild[0]) + { + to=strmov(to," like '"); + while (*wild && to < end) + { + if (*wild == '\\' || *wild == '\'') + *to++='\\'; + *to++= *wild++; + } + if (*wild) /* Too small buffer */ + *to++='%'; /* Nicer this way */ + to[0]='\''; + to[1]=0; + } +} + + + +/************************************************************************** +** Init debugging if MYSQL_DEBUG environment variable is found +**************************************************************************/ + +void STDCALL +mysql_debug(const char *debug) +{ +#ifndef DBUG_OFF + char *env; + if (_db_on_) + return; /* Already using debugging */ + if (debug) + { + DEBUGGER_ON; + DBUG_PUSH(debug); + } + else if ((env = getenv("MYSQL_DEBUG"))) + { + DEBUGGER_ON; + DBUG_PUSH(env); +#if !defined(_WINVER) && !defined(WINVER) + puts("\n-------------------------------------------------------"); + puts("MYSQL_DEBUG found. libmysql started with the following:"); + puts(env); + puts("-------------------------------------------------------\n"); +#else + { + char buff[80]; + strmov(strmov(buff,"libmysql: "),env); + MessageBox((HWND) 0,"Debugging variable MYSQL_DEBUG used",buff,MB_OK); + } +#endif + } +#endif +} + +/************************************************************************** +** Shut down connection +**************************************************************************/ + +static void +end_server(MYSQL *mysql) +{ + DBUG_ENTER("end_server"); + if (mysql->net.vio != 0) + { + end_embedded_connection(&mysql->net); + mysql->net.vio= 0; /* Marker */ + } + net_end(&mysql->net); + free_old_query(mysql); + DBUG_VOID_RETURN; +} + + +void STDCALL +mysql_free_result(MYSQL_RES *result) +{ + DBUG_ENTER("mysql_free_result"); + DBUG_PRINT("enter",("mysql_res: %lx",result)); + if (result) + { + if (result->handle && result->handle->status == MYSQL_STATUS_USE_RESULT) + { + DBUG_PRINT("warning",("Not all rows in set were read; Ignoring rows")); + for (;;) + { + uint pkt_len; + if ((pkt_len=(uint) net_safe_read(result->handle)) == packet_error) + break; + if (pkt_len == 1 && result->handle->net.read_pos[0] == 254) + break; /* End of data */ + } + result->handle->status=MYSQL_STATUS_READY; + } + free_rows(result->data); + if (result->fields) + free_root(&result->field_alloc,MYF(0)); + if (result->row) + my_free((gptr) result->row,MYF(0)); + my_free((gptr) result,MYF(0)); + } + DBUG_VOID_RETURN; +} + + +/**************************************************************************** +** Get options from my.cnf +****************************************************************************/ + +static const char *default_options[]= +{"port","socket","compress","password","pipe", "timeout", "user", + "init-command", "host", "database", "debug", "return-found-rows", + "ssl_key" ,"ssl_cert" ,"ssl_ca" ,"ssl_capath", + "character-set-dir", "default-character-set", + NullS +}; + +static TYPELIB option_types={array_elements(default_options)-1, + "options",default_options}; + +static void mysql_read_default_options(struct st_mysql_options *options, + const char *filename,const char *group) +{ + int argc; + char *argv_buff[1],**argv; + const char *groups[3]; + DBUG_ENTER("mysql_read_default_options"); + DBUG_PRINT("enter",("file: %s group: %s",filename,group ? group :"NULL")); + + argc=1; argv=argv_buff; argv_buff[0]= (char*) "client"; + groups[0]= (char*) "client"; groups[1]= (char*) group; groups[2]=0; + + load_defaults(filename, groups, &argc, &argv); + if (argc != 1) /* If some default option */ + { + char **option=argv; + while (*++option) + { + /* DBUG_PRINT("info",("option: %s",option[0])); */ + if (option[0][0] == '-' && option[0][1] == '-') + { + char *end=strcend(*option,'='); + char *opt_arg=0; + if (*end) + { + opt_arg=end+1; + *end=0; /* Remove '=' */ + } + switch (find_type(*option+2,&option_types,2)) { + case 1: /* port */ + if (opt_arg) + options->port=atoi(opt_arg); + break; + case 2: /* socket */ + if (opt_arg) + { + my_free(options->unix_socket,MYF(MY_ALLOW_ZERO_PTR)); + options->unix_socket=my_strdup(opt_arg,MYF(MY_WME)); + } + break; + case 3: /* compress */ + options->compress=1; + break; + case 4: /* password */ + if (opt_arg) + { + my_free(options->password,MYF(MY_ALLOW_ZERO_PTR)); + options->password=my_strdup(opt_arg,MYF(MY_WME)); + } + break; + case 5: /* pipe */ + options->named_pipe=1; /* Force named pipe */ + break; + case 6: /* timeout */ + if (opt_arg) + options->connect_timeout=atoi(opt_arg); + break; + case 7: /* user */ + if (opt_arg) + { + my_free(options->user,MYF(MY_ALLOW_ZERO_PTR)); + options->user=my_strdup(opt_arg,MYF(MY_WME)); + } + break; + case 8: /* init-command */ + if (opt_arg) + { + my_free(options->init_command,MYF(MY_ALLOW_ZERO_PTR)); + options->init_command=my_strdup(opt_arg,MYF(MY_WME)); + } + break; + case 9: /* host */ + if (opt_arg) + { + my_free(options->host,MYF(MY_ALLOW_ZERO_PTR)); + options->host=my_strdup(opt_arg,MYF(MY_WME)); + } + break; + case 10: /* database */ + if (opt_arg) + { + my_free(options->db,MYF(MY_ALLOW_ZERO_PTR)); + options->db=my_strdup(opt_arg,MYF(MY_WME)); + } + break; + case 11: /* debug */ + mysql_debug(opt_arg ? opt_arg : "d:t:o,/tmp/client.trace"); + break; + case 12: /* return-found-rows */ + options->client_flag|=CLIENT_FOUND_ROWS; + break; + case 13: /* Ignore SSL options */ + case 14: + case 15: + case 16: + break; + case 17: /* charset-lib */ + my_free(options->charset_dir,MYF(MY_ALLOW_ZERO_PTR)); + options->charset_dir = my_strdup(opt_arg, MYF(MY_WME)); + break; + case 18: + my_free(options->charset_name,MYF(MY_ALLOW_ZERO_PTR)); + options->charset_name = my_strdup(opt_arg, MYF(MY_WME)); + break; + default: + DBUG_PRINT("warning",("unknown option: %s",option[0])); + } + } + } + } + free_defaults(argv); + DBUG_VOID_RETURN; +} + + +/*************************************************************************** +** Change field rows to field structs +***************************************************************************/ + +static MYSQL_FIELD * +unpack_fields(MYSQL_DATA *data,MEM_ROOT *alloc,uint fields, + my_bool default_value, my_bool long_flag_protocol) +{ + MYSQL_ROWS *row; + MYSQL_FIELD *field,*result; + DBUG_ENTER("unpack_fields"); + + field=result=(MYSQL_FIELD*) alloc_root(alloc,sizeof(MYSQL_FIELD)*fields); + if (!result) + DBUG_RETURN(0); + + for (row=data->data; row ; row = row->next,field++) + { + field->table= strdup_root(alloc,(char*) row->data[0]); + field->name= strdup_root(alloc,(char*) row->data[1]); + field->length= (uint) uint3korr(row->data[2]); + field->type= (enum enum_field_types) (uchar) row->data[3][0]; + if (long_flag_protocol) + { + field->flags= uint2korr(row->data[4]); + field->decimals=(uint) (uchar) row->data[4][2]; + } + else + { + field->flags= (uint) (uchar) row->data[4][0]; + field->decimals=(uint) (uchar) row->data[4][1]; + } + if (INTERNAL_NUM_FIELD(field)) + field->flags|= NUM_FLAG; + if (default_value && row->data[5]) + field->def=strdup_root(alloc,(char*) row->data[5]); + else + field->def=0; + field->max_length= 0; + } + free_rows(data); /* Free old data */ + DBUG_RETURN(result); +} + + +/* Read all rows (fields or data) from server */ + +static MYSQL_DATA *read_rows(MYSQL *mysql,MYSQL_FIELD *mysql_fields, + uint fields) +{ + uint field,pkt_len; + ulong len; + uchar *cp; + char *to; + MYSQL_DATA *result; + MYSQL_ROWS **prev_ptr,*cur; + NET *net = &mysql->net; + DBUG_ENTER("read_rows"); + + if ((pkt_len=(uint) net_safe_read(mysql)) == packet_error) + DBUG_RETURN(0); + if (!(result=(MYSQL_DATA*) my_malloc(sizeof(MYSQL_DATA), + MYF(MY_WME | MY_ZEROFILL)))) + { + net->last_errno=CR_OUT_OF_MEMORY; + strmov(net->last_error,ER(net->last_errno)); + DBUG_RETURN(0); + } + init_alloc_root(&result->alloc,8192,0); /* Assume rowlength < 8192 */ + result->alloc.min_malloc=sizeof(MYSQL_ROWS); + prev_ptr= &result->data; + result->rows=0; + result->fields=fields; + + while (*(cp=net->read_pos) != 254 || pkt_len != 1) + { + result->rows++; + if (!(cur= (MYSQL_ROWS*) alloc_root(&result->alloc, + sizeof(MYSQL_ROWS))) || + !(cur->data= ((MYSQL_ROW) + alloc_root(&result->alloc, + (fields+1)*sizeof(char *)+pkt_len)))) + { + free_rows(result); + net->last_errno=CR_OUT_OF_MEMORY; + strmov(net->last_error,ER(net->last_errno)); + DBUG_RETURN(0); + } + *prev_ptr=cur; + prev_ptr= &cur->next; + to= (char*) (cur->data+fields+1); + for (field=0 ; field < fields ; field++) + { + if ((len=(ulong) net_field_length(&cp)) == NULL_LENGTH) + { /* null field */ + cur->data[field] = 0; + } + else + { + cur->data[field] = to; + memcpy(to,(char*) cp,len); to[len]=0; + to+=len+1; + cp+=len; + if (mysql_fields) + { + if (mysql_fields[field].max_length < len) + mysql_fields[field].max_length=len; + } + } + } + cur->data[field]=to; /* End of last field */ + if ((pkt_len=net_safe_read(mysql)) == packet_error) + { + free_rows(result); + DBUG_RETURN(0); + } + } + *prev_ptr=0; /* last pointer is null */ + DBUG_PRINT("exit",("Got %d rows",result->rows)); + DBUG_RETURN(result); +} + + +/* +** Read one row. Uses packet buffer as storage for fields. +** When next packet is read, the previous field values are destroyed +*/ + + +static int +read_one_row(MYSQL *mysql,uint fields,MYSQL_ROW row, ulong *lengths) +{ + uint field; + ulong pkt_len,len; + uchar *pos,*prev_pos; + + if ((pkt_len=net_safe_read(mysql)) == packet_error) + return -1; + if (pkt_len == 1 && mysql->net.read_pos[0] == 254) + return 1; /* End of data */ + prev_pos= 0; /* allowed to write at packet[-1] */ + pos=mysql->net.read_pos; + for (field=0 ; field < fields ; field++) + { + if ((len=(ulong) net_field_length(&pos)) == NULL_LENGTH) + { /* null field */ + row[field] = 0; + *lengths++=0; + } + else + { + row[field] = (char*) pos; + pos+=len; + *lengths++=len; + } + if (prev_pos) + *prev_pos=0; /* Terminate prev field */ + prev_pos=pos; + } + row[field]=(char*) prev_pos+1; /* End of last field */ + *prev_pos=0; /* Terminate last field */ + return 0; +} + +/**************************************************************************** +** Init MySQL structure or allocate one +****************************************************************************/ + +MYSQL * STDCALL +mysql_init(MYSQL *mysql) +{ + mysql_once_init(); + if (!mysql) + { + if (!(mysql=(MYSQL*) my_malloc(sizeof(*mysql),MYF(MY_WME | MY_ZEROFILL)))) + return 0; + mysql->free_me=1; + mysql->net.vio = 0; + } + else + bzero((char*) (mysql),sizeof(*(mysql))); + return mysql; +} + + +static void mysql_once_init() +{ + if (!mysql_client_init) + { + mysql_client_init=1; + my_init(); /* Will init threads */ + init_client_errs(); + mysql_port = MYSQL_PORT; + mysql_debug(NullS); + } +#ifdef THREAD + else + my_thread_init(); /* Init if new thread */ +#endif +} + +/************************************************************************** +** Connect to sql server +** If host == 0 then use localhost +**************************************************************************/ + +MYSQL * STDCALL +mysql_connect(MYSQL *mysql,const char *host, + const char *user, const char *passwd) +{ + MYSQL *res; + mysql=mysql_init(mysql); /* Make it thread safe */ + { + DBUG_ENTER("mysql_connect"); + if (!(res=mysql_real_connect(mysql,host,user,passwd,NullS,0,NullS,0))) + { + if (mysql->free_me) + my_free((gptr) mysql,MYF(0)); + } + DBUG_RETURN(res); + } +} + + +/* +** Note that the mysql argument must be initialized with mysql_init() +** before calling mysql_real_connect ! +*/ + +MYSQL * STDCALL +mysql_real_connect(MYSQL *mysql,const char *host, const char *user, + const char *passwd, const char *db, + uint port, const char *unix_socket,uint client_flag) +{ + char buff[100],charset_name_buff[16],*end,*host_info, *charset_name; + uint pkt_length; + NET *net= &mysql->net; + DBUG_ENTER("mysql_real_connect"); + DBUG_PRINT("enter",("host: %s db: %s user: %s", + host ? host : "(Null)", + db ? db : "(Null)", + user ? user : "(Null)")); + + net->vio = 0; /* If something goes wrong */ + /* use default options */ + if (mysql->options.my_cnf_file || mysql->options.my_cnf_group) + { + mysql_read_default_options(&mysql->options, + (mysql->options.my_cnf_file ? + mysql->options.my_cnf_file : "my"), + mysql->options.my_cnf_group); + my_free(mysql->options.my_cnf_file,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->options.my_cnf_group,MYF(MY_ALLOW_ZERO_PTR)); + mysql->options.my_cnf_file=mysql->options.my_cnf_group=0; + } + + /* Some empty-string-tests are done because of ODBC */ + if (!host || !host[0]) + host=mysql->options.host; + if (!user || !user[0]) + user=mysql->options.user; + if (!passwd) + { + passwd=mysql->options.password; + } + if (!db || !db[0]) + db=mysql->options.db; + port=0; + unix_socket=0; + mysql->reconnect=1; /* Reconnect as default */ + mysql->server_status=SERVER_STATUS_AUTOCOMMIT; + host_info=(char*) ER(CR_EMBEDDED_CONNECTION); + if (my_net_init(net, net->vio)) + { + vio_delete(net->vio); + net->last_errno=CR_OUT_OF_MEMORY; + strmov(net->last_error,ER(net->last_errno)); + goto error; + } + + /* Get version info */ + mysql->protocol_version= PROTOCOL_VERSION; /* Assume this */ + start_embedded_connection(net); + + if ((pkt_length=net_safe_read(mysql)) == packet_error) + goto error; + + /* Check if version of protocoll matches current one */ + + mysql->protocol_version= net->read_pos[0]; + DBUG_DUMP("packet",(char*) net->read_pos,10); + DBUG_PRINT("info",("mysql protocol version %d, server=%d", + PROTOCOL_VERSION, mysql->protocol_version)); + if (mysql->protocol_version != PROTOCOL_VERSION && + mysql->protocol_version != PROTOCOL_VERSION-1) + { + net->last_errno= CR_VERSION_ERROR; + sprintf(net->last_error, ER(CR_VERSION_ERROR), mysql->protocol_version, + PROTOCOL_VERSION); + goto error; + } + end=strend((char*) net->read_pos+1); + mysql->thread_id=uint4korr(end+1); + end+=5; + strmake(mysql->scramble_buff,end,8); + end+=9; + if (pkt_length >= (uint) (end+1 - (char*) net->read_pos)) + mysql->server_capabilities=uint2korr(end); + if (pkt_length >= (uint) (end+18 - (char*) net->read_pos)) + { + /* New protocol with 16 bytes to describe server characteristics */ + mysql->server_language=end[2]; + mysql->server_status=uint2korr(end+3); + } + + /* Set character set */ + if ((charset_name=mysql->options.charset_name)) + { + const char *save=charsets_dir; + if (mysql->options.charset_dir) + charsets_dir=mysql->options.charset_dir; + mysql->charset=get_charset_by_name(mysql->options.charset_name, + MYF(MY_WME)); + charsets_dir=save; + } + else if (mysql->server_language) + { + charset_name=charset_name_buff; + sprintf(charset_name,"%d",mysql->server_language); /* In case of errors */ + mysql->charset=get_charset((uint8) mysql->server_language, MYF(MY_WME)); + } + else + mysql->charset=default_charset_info; + + if (!mysql->charset) + { + net->last_errno=CR_CANT_READ_CHARSET; + if (mysql->options.charset_dir) + sprintf(net->last_error,ER(net->last_errno), + charset_name ? charset_name : "unknown", + mysql->options.charset_dir); + else + { + char cs_dir_name[FN_REFLEN]; + get_charsets_dir(cs_dir_name); + sprintf(net->last_error,ER(net->last_errno), + charset_name ? charset_name : "unknown", + cs_dir_name); + } + goto error; + } + + /* Save connection information */ + if (!user) user=""; + if (!passwd) passwd=""; + host=LOCAL_HOST; + if (!my_multi_malloc(MYF(0), + &mysql->host_info, (uint) strlen(host_info)+1, + &mysql->host, (uint) strlen(host)+1, + &mysql->unix_socket,unix_socket ? + (uint) strlen(unix_socket)+1 : (uint) 1, + &mysql->server_version, + (uint) (end - (char*) net->read_pos), + NullS) || + !(mysql->user=my_strdup(user,MYF(0))) || + !(mysql->passwd=my_strdup(passwd,MYF(0)))) + { + strmov(net->last_error, ER(net->last_errno=CR_OUT_OF_MEMORY)); + goto error; + } + strmov(mysql->host_info,host_info); + strmov(mysql->host,host); + if (unix_socket) + strmov(mysql->unix_socket,unix_socket); + else + mysql->unix_socket=0; + strmov(mysql->server_version,(char*) net->read_pos+1); + mysql->port=port; + mysql->client_flag=client_flag | mysql->options.client_flag; + DBUG_PRINT("info",("Server version = '%s' capabilites: %ld status: %d", + mysql->server_version,mysql->server_capabilities, + mysql->server_status)); + + /* Send client information for access check */ + client_flag|=CLIENT_CAPABILITIES; + client_flag&= ~CLIENT_COMPRESS; + if (db) + client_flag|=CLIENT_CONNECT_WITH_DB; + int2store(buff,client_flag); + mysql->client_flag=client_flag; + + + int3store(buff+2,max_allowed_packet); + if (user && user[0]) + strmake(buff+5,user,32); + else + read_user_name((char*) buff+5); +#ifdef _CUSTOMCONFIG_ +#include "_cust_libmysql.h"; +#endif + DBUG_PRINT("info",("user: %s",buff+5)); + end=scramble(strend(buff+5)+1, mysql->scramble_buff, passwd, + (my_bool) (mysql->protocol_version == 9)); + + if (db) + { + end=strmov(end+1,db); + mysql->db=my_strdup(db,MYF(MY_WME)); + db=0; + } + if (my_net_write(net,buff,(ulong) (end-buff)) || net_flush(net)) + goto error; + + lib_connection_phase(net,2); + + if( net_safe_read(mysql) == packet_error) + goto error; + if (db && mysql_select_db(mysql,db)) + goto error; + if (mysql->options.init_command) + { + my_bool reconnect=mysql->reconnect; + mysql->reconnect=0; + if (mysql_query(mysql,mysql->options.init_command)) + goto error; + mysql_free_result(mysql_use_result(mysql)); + mysql->reconnect=reconnect; + } + + DBUG_PRINT("exit",("Mysql handler: %lx",mysql)); + reset_sigpipe(mysql); + DBUG_RETURN(mysql); + +error: + reset_sigpipe(mysql); + DBUG_PRINT("error",("message: %u (%s)",net->last_errno,net->last_error)); + { + /* Free alloced memory */ + my_bool free_me=mysql->free_me; + end_server(mysql); + mysql->free_me=0; + mysql_close(mysql); + mysql->free_me=free_me; + } + DBUG_RETURN(0); +} + + +/************************************************************************** +** Change user and database +**************************************************************************/ + +my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user, + const char *passwd, const char *db) +{ + char buff[512],*pos=buff; + DBUG_ENTER("mysql_change_user"); + + if (!user) + user=""; + if (!passwd) + passwd=""; + + pos=strmov(pos,user)+1; + pos=scramble(pos, mysql->scramble_buff, passwd, + (my_bool) (mysql->protocol_version == 9)); + pos=strmov(pos+1,db ? db : ""); + if (simple_command(mysql,COM_CHANGE_USER, buff,(ulong) (pos-buff),0)) + DBUG_RETURN(1); + + my_free(mysql->user,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->passwd,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->db,MYF(MY_ALLOW_ZERO_PTR)); + + mysql->user= my_strdup(user,MYF(MY_WME)); + mysql->passwd=my_strdup(passwd,MYF(MY_WME)); + mysql->db= db ? my_strdup(db,MYF(MY_WME)) : 0; + DBUG_RETURN(0); +} + + +/************************************************************************** +** Set current database +**************************************************************************/ + +int STDCALL +mysql_select_db(MYSQL *mysql, const char *db) +{ + int error; + DBUG_ENTER("mysql_select_db"); + DBUG_PRINT("enter",("db: '%s'",db)); + + if ((error=simple_command(mysql,COM_INIT_DB,db,(ulong) strlen(db),0))) + DBUG_RETURN(error); + my_free(mysql->db,MYF(MY_ALLOW_ZERO_PTR)); + mysql->db=my_strdup(db,MYF(MY_WME)); + DBUG_RETURN(0); +} + + +/************************************************************************* +** Send a QUIT to the server and close the connection +** If handle is alloced by mysql connect free it. +*************************************************************************/ + +void STDCALL +mysql_close(MYSQL *mysql) +{ + DBUG_ENTER("mysql_close"); + if (mysql) /* Some simple safety */ + { + if (mysql->net.vio != 0) + { + free_old_query(mysql); + mysql->status=MYSQL_STATUS_READY; /* Force command */ + simple_command(mysql,COM_QUIT,"",0,1); + end_server(mysql); + } + my_free((gptr) mysql->host_info,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->user,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->passwd,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->db,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->options.init_command,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->options.user,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->options.host,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->options.password,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->options.unix_socket,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->options.db,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->options.my_cnf_file,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->options.my_cnf_group,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->options.charset_dir,MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->options.charset_name,MYF(MY_ALLOW_ZERO_PTR)); + /* Clear pointers for better safety */ + mysql->host_info=mysql->user=mysql->passwd=mysql->db=0; + bzero((char*) &mysql->options,sizeof(mysql->options)); + mysql->net.vio = 0; +#ifdef HAVE_OPENSSL + ((VioConnectorFd*)(mysql->connector_fd))->delete(); + mysql->connector_fd = 0; +#endif /* HAVE_OPENSSL */ + if (mysql->free_me) + my_free((gptr) mysql,MYF(0)); + } + DBUG_VOID_RETURN; +} + + +/************************************************************************** +** Do a query. If query returned rows, free old rows. +** Read data by mysql_store_result or by repeat call of mysql_fetch_row +**************************************************************************/ + +int STDCALL +mysql_query(MYSQL *mysql, const char *query) +{ + return mysql_real_query(mysql,query, (ulong) strlen(query)); +} + +int STDCALL +mysql_send_query(MYSQL* mysql, const char* query, ulong length) +{ + return simple_command(mysql, COM_QUERY, query, length, 1); +} + + +int STDCALL +mysql_read_query_result(MYSQL *mysql) +{ + uchar *pos; + ulong field_count; + MYSQL_DATA *fields; + uint length; + DBUG_ENTER("mysql_read_query_result"); + + if ((length=net_safe_read(mysql)) == packet_error) + DBUG_RETURN(-1); + free_old_query(mysql); /* Free old result */ +get_info: + pos=(uchar*) mysql->net.read_pos; + if ((field_count= net_field_length(&pos)) == 0) + { + mysql->affected_rows= net_field_length_ll(&pos); + mysql->insert_id= net_field_length_ll(&pos); + if (mysql->server_capabilities & CLIENT_TRANSACTIONS) + { + mysql->server_status=uint2korr(pos); pos+=2; + } + if (pos < mysql->net.read_pos+length && net_field_length(&pos)) + mysql->info=(char*) pos; + DBUG_RETURN(0); + } + if (field_count == NULL_LENGTH) /* LOAD DATA LOCAL INFILE */ + { + int error=send_file_to_server(mysql,(char*) pos); + if ((length=net_safe_read(mysql)) == packet_error || error) + DBUG_RETURN(-1); + goto get_info; /* Get info packet */ + } + if (!(mysql->server_status & SERVER_STATUS_AUTOCOMMIT)) + mysql->server_status|= SERVER_STATUS_IN_TRANS; + + mysql->extra_info= net_field_length_ll(&pos); /* Maybe number of rec */ + if (!(fields=read_rows(mysql,(MYSQL_FIELD*) 0,5))) + DBUG_RETURN(-1); + if (!(mysql->fields=unpack_fields(fields,&mysql->field_alloc, + (uint) field_count,0, + (my_bool) test(mysql->server_capabilities & + CLIENT_LONG_FLAG)))) + DBUG_RETURN(-1); + mysql->status=MYSQL_STATUS_GET_RESULT; + mysql->field_count=field_count; + DBUG_RETURN(0); +} + +/**************************************************************************** +* A modified version of connect(). connect2() allows you to specify +* a timeout value, in seconds, that we should wait until we +* derermine we can't connect to a particular host. If timeout is 0, +* my_connect() will behave exactly like connect(). +* +* Base version coded by Steve Bernacki, Jr. <steve@navinet.net> +*****************************************************************************/ + +int my_connect(my_socket s, const struct sockaddr *name, uint namelen, + uint timeout) +{ +#if defined(__WIN__) || defined(OS2) + return connect(s, (struct sockaddr*) name, namelen); +#else + int flags, res, s_err; + SOCKOPT_OPTLEN_TYPE s_err_size = sizeof(uint); + fd_set sfds; + struct timeval tv; + time_t start_time, now_time; + + /* If they passed us a timeout of zero, we should behave + * exactly like the normal connect() call does. + */ + + if (timeout == 0) + return connect(s, (struct sockaddr*) name, namelen); + + flags = fcntl(s, F_GETFL, 0); /* Set socket to not block */ +#ifdef O_NONBLOCK + fcntl(s, F_SETFL, flags | O_NONBLOCK); /* and save the flags.. */ +#endif + + res = connect(s, (struct sockaddr*) name, namelen); + s_err = errno; /* Save the error... */ + fcntl(s, F_SETFL, flags); + if ((res != 0) && (s_err != EINPROGRESS)) + { + errno = s_err; /* Restore it */ + return(-1); + } + if (res == 0) /* Connected quickly! */ + return(0); + + /* Otherwise, our connection is "in progress." We can use + * the select() call to wait up to a specified period of time + * for the connection to suceed. If select() returns 0 + * (after waiting howevermany seconds), our socket never became + * writable (host is probably unreachable.) Otherwise, if + * select() returns 1, then one of two conditions exist: + * + * 1. An error occured. We use getsockopt() to check for this. + * 2. The connection was set up sucessfully: getsockopt() will + * return 0 as an error. + * + * Thanks goes to Andrew Gierth <andrew@erlenstar.demon.co.uk> + * who posted this method of timing out a connect() in + * comp.unix.programmer on August 15th, 1997. + */ + + FD_ZERO(&sfds); + FD_SET(s, &sfds); + /* + * select could be interrupted by a signal, and if it is, + * the timeout should be adjusted and the select restarted + * to work around OSes that don't restart select and + * implementations of select that don't adjust tv upon + * failure to reflect the time remaining + */ + start_time = time(NULL); + for (;;) + { + tv.tv_sec = (long) timeout; + tv.tv_usec = 0; + if ((res = select(s+1, NULL, &sfds, NULL, &tv)) >= 0) + break; + now_time=time(NULL); + timeout-= (uint) (now_time - start_time); + if (errno != EINTR || (int) timeout <= 0) + return -1; + } + + /* select() returned something more interesting than zero, let's + * see if we have any errors. If the next two statements pass, + * we've got an open socket! + */ + + s_err=0; + if (getsockopt(s, SOL_SOCKET, SO_ERROR, (char*) &s_err, &s_err_size) != 0) + return(-1); + + if (s_err) + { /* getsockopt could succeed */ + errno = s_err; + return(-1); /* but return an error... */ + } + return(0); /* It's all good! */ +#endif +} + + +int STDCALL +mysql_real_query(MYSQL *mysql, const char *query, ulong length) +{ + DBUG_ENTER("mysql_real_query"); + DBUG_PRINT("enter",("handle: %lx",mysql)); + DBUG_PRINT("query",("Query = \"%s\"",query)); + if (mysql_send_query(mysql, query, length)) + DBUG_RETURN(-1); + DBUG_RETURN(mysql_read_query_result(mysql)); +} + + +static int +send_file_to_server(MYSQL *mysql, const char *filename) +{ + int fd, readcount; + char buf[IO_SIZE*15],*tmp_name; + DBUG_ENTER("send_file_to_server"); + + fn_format(buf,filename,"","",4); /* Convert to client format */ + if (!(tmp_name=my_strdup(buf,MYF(0)))) + { + strmov(mysql->net.last_error, ER(mysql->net.last_errno=CR_OUT_OF_MEMORY)); + DBUG_RETURN(-1); + } + if ((fd = my_open(tmp_name,O_RDONLY, MYF(0))) < 0) + { + mysql->net.last_errno=EE_FILENOTFOUND; + sprintf(buf,EE(mysql->net.last_errno),tmp_name,errno); + strmake(mysql->net.last_error,buf,sizeof(mysql->net.last_error)-1); + my_net_write(&mysql->net,"",0); net_flush(&mysql->net); + my_free(tmp_name,MYF(0)); + DBUG_RETURN(-1); + } + + while ((readcount = (int) my_read(fd,buf,sizeof(buf),MYF(0))) > 0) + { + if (my_net_write(&mysql->net,buf,readcount)) + { + mysql->net.last_errno=CR_SERVER_LOST; + strmov(mysql->net.last_error,ER(mysql->net.last_errno)); + DBUG_PRINT("error",("Lost connection to MySQL server during LOAD DATA of local file")); + (void) my_close(fd,MYF(0)); + my_free(tmp_name,MYF(0)); + DBUG_RETURN(-1); + } + } + (void) my_close(fd,MYF(0)); + /* Send empty packet to mark end of file */ + if (my_net_write(&mysql->net,"",0) || net_flush(&mysql->net)) + { + mysql->net.last_errno=CR_SERVER_LOST; + sprintf(mysql->net.last_error,ER(mysql->net.last_errno),errno); + my_free(tmp_name,MYF(0)); + DBUG_RETURN(-1); + } + if (readcount < 0) + { + mysql->net.last_errno=EE_READ; /* the errmsg for not entire file read */ + sprintf(buf,EE(mysql->net.last_errno),tmp_name,errno); + strmake(mysql->net.last_error,buf,sizeof(mysql->net.last_error)-1); + my_free(tmp_name,MYF(0)); + DBUG_RETURN(-1); + } + DBUG_RETURN(0); +} + + +/************************************************************************** +** Alloc result struct for buffered results. All rows are read to buffer. +** mysql_data_seek may be used. +**************************************************************************/ + +MYSQL_RES * STDCALL +mysql_store_result(MYSQL *mysql) +{ + MYSQL_RES *result; + DBUG_ENTER("mysql_store_result"); + + if (!mysql->fields) + DBUG_RETURN(0); + if (mysql->status != MYSQL_STATUS_GET_RESULT) + { + strmov(mysql->net.last_error, + ER(mysql->net.last_errno=CR_COMMANDS_OUT_OF_SYNC)); + DBUG_RETURN(0); + } + mysql->status=MYSQL_STATUS_READY; /* server is ready */ + if (!(result=(MYSQL_RES*) my_malloc(sizeof(MYSQL_RES)+ + sizeof(ulong)*mysql->field_count, + MYF(MY_WME | MY_ZEROFILL)))) + { + mysql->net.last_errno=CR_OUT_OF_MEMORY; + strmov(mysql->net.last_error, ER(mysql->net.last_errno)); + DBUG_RETURN(0); + } + result->eof=1; /* Marker for buffered */ + result->lengths=(ulong*) (result+1); + if (!(result->data=read_rows(mysql,mysql->fields,mysql->field_count))) + { + my_free((gptr) result,MYF(0)); + DBUG_RETURN(0); + } + mysql->affected_rows= result->row_count= result->data->rows; + result->data_cursor= result->data->data; + result->fields= mysql->fields; + result->field_alloc= mysql->field_alloc; + result->field_count= mysql->field_count; + result->current_field=0; + result->current_row=0; /* Must do a fetch first */ + mysql->fields=0; /* fields is now in result */ + DBUG_RETURN(result); /* Data fetched */ +} + + +/************************************************************************** +** Alloc struct for use with unbuffered reads. Data is fetched by domand +** when calling to mysql_fetch_row. +** mysql_data_seek is a noop. +** +** No other queries may be specified with the same MYSQL handle. +** There shouldn't be much processing per row because mysql server shouldn't +** have to wait for the client (and will not wait more than 30 sec/packet). +**************************************************************************/ + +MYSQL_RES * STDCALL +mysql_use_result(MYSQL *mysql) +{ + MYSQL_RES *result; + DBUG_ENTER("mysql_use_result"); + + if (!mysql->fields) + DBUG_RETURN(0); + if (mysql->status != MYSQL_STATUS_GET_RESULT) + { + strmov(mysql->net.last_error, + ER(mysql->net.last_errno=CR_COMMANDS_OUT_OF_SYNC)); + DBUG_RETURN(0); + } + if (!(result=(MYSQL_RES*) my_malloc(sizeof(*result)+ + sizeof(ulong)*mysql->field_count, + MYF(MY_WME | MY_ZEROFILL)))) + DBUG_RETURN(0); + result->lengths=(ulong*) (result+1); + if (!(result->row=(MYSQL_ROW) + my_malloc(sizeof(result->row[0])*(mysql->field_count+1), MYF(MY_WME)))) + { /* Ptrs: to one row */ + my_free((gptr) result,MYF(0)); + DBUG_RETURN(0); + } + result->fields= mysql->fields; + result->field_alloc= mysql->field_alloc; + result->field_count= mysql->field_count; + result->current_field=0; + result->handle= mysql; + result->current_row= 0; + mysql->fields=0; /* fields is now in result */ + mysql->status=MYSQL_STATUS_USE_RESULT; + DBUG_RETURN(result); /* Data is read to be fetched */ +} + + + +/************************************************************************** +** Return next field of the query results +**************************************************************************/ + +MYSQL_FIELD * STDCALL +mysql_fetch_field(MYSQL_RES *result) +{ + if (result->current_field >= result->field_count) + return(NULL); + return &result->fields[result->current_field++]; +} + + +/************************************************************************** +** Return next row of the query results +**************************************************************************/ + +MYSQL_ROW STDCALL +mysql_fetch_row(MYSQL_RES *res) +{ + DBUG_ENTER("mysql_fetch_row"); + if (!res->data) + { /* Unbufferred fetch */ + if (!res->eof) + { + if (!(read_one_row(res->handle,res->field_count,res->row, res->lengths))) + { + res->row_count++; + DBUG_RETURN(res->current_row=res->row); + } + else + { + DBUG_PRINT("info",("end of data")); + res->eof=1; + res->handle->status=MYSQL_STATUS_READY; + } + } + DBUG_RETURN((MYSQL_ROW) NULL); + } + { + MYSQL_ROW tmp; + if (!res->data_cursor) + { + DBUG_PRINT("info",("end of data")); + DBUG_RETURN(res->current_row=(MYSQL_ROW) NULL); + } + tmp = res->data_cursor->data; + res->data_cursor = res->data_cursor->next; + DBUG_RETURN(res->current_row=tmp); + } +} + +/************************************************************************** +** Get column lengths of the current row +** If one uses mysql_use_result, res->lengths contains the length information, +** else the lengths are calculated from the offset between pointers. +**************************************************************************/ + +ulong * STDCALL +mysql_fetch_lengths(MYSQL_RES *res) +{ + ulong *lengths,*prev_length; + byte *start; + MYSQL_ROW column,end; + + if (!(column=res->current_row)) + return 0; /* Something is wrong */ + if (res->data) + { + start=0; + prev_length=0; /* Keep gcc happy */ + lengths=res->lengths; + for (end=column+res->field_count+1 ; column != end ; column++,lengths++) + { + if (!*column) + { + *lengths=0; /* Null */ + continue; + } + if (start) /* Found end of prev string */ + *prev_length= (uint) (*column-start-1); + start= *column; + prev_length=lengths; + } + } + return res->lengths; +} + +/************************************************************************** +** Move to a specific row and column +**************************************************************************/ + +void STDCALL +mysql_data_seek(MYSQL_RES *result, my_ulonglong row) +{ + MYSQL_ROWS *tmp=0; + DBUG_PRINT("info",("mysql_data_seek(%ld)",(long) row)); + if (result->data) + for (tmp=result->data->data; row-- && tmp ; tmp = tmp->next) ; + result->current_row=0; + result->data_cursor = tmp; +} + +/************************************************************************* +** put the row or field cursor one a position one got from mysql_row_tell() +** This doesn't restore any data. The next mysql_fetch_row or +** mysql_fetch_field will return the next row or field after the last used +*************************************************************************/ + +MYSQL_ROW_OFFSET STDCALL +mysql_row_seek(MYSQL_RES *result, MYSQL_ROW_OFFSET row) +{ + MYSQL_ROW_OFFSET return_value=result->data_cursor; + result->current_row= 0; + result->data_cursor= row; + return return_value; +} + + +MYSQL_FIELD_OFFSET STDCALL +mysql_field_seek(MYSQL_RES *result, MYSQL_FIELD_OFFSET field_offset) +{ + MYSQL_FIELD_OFFSET return_value=result->current_field; + result->current_field=field_offset; + return return_value; +} + +/***************************************************************************** +** List all databases +*****************************************************************************/ + +MYSQL_RES * STDCALL +mysql_list_dbs(MYSQL *mysql, const char *wild) +{ + char buff[255]; + DBUG_ENTER("mysql_list_dbs"); + + append_wild(strmov(buff,"show databases"),buff+sizeof(buff),wild); + if (mysql_query(mysql,buff)) + DBUG_RETURN(0); + DBUG_RETURN (mysql_store_result(mysql)); +} + + +/***************************************************************************** +** List all tables in a database +** If wild is given then only the tables matching wild is returned +*****************************************************************************/ + +MYSQL_RES * STDCALL +mysql_list_tables(MYSQL *mysql, const char *wild) +{ + char buff[255]; + DBUG_ENTER("mysql_list_tables"); + + append_wild(strmov(buff,"show tables"),buff+sizeof(buff),wild); + if (mysql_query(mysql,buff)) + DBUG_RETURN(0); + DBUG_RETURN (mysql_store_result(mysql)); +} + + +/************************************************************************** +** List all fields in a table +** If wild is given then only the fields matching wild is returned +** Instead of this use query: +** show fields in 'table' like "wild" +**************************************************************************/ + +MYSQL_RES * STDCALL +mysql_list_fields(MYSQL *mysql, const char *table, const char *wild) +{ + MYSQL_RES *result; + MYSQL_DATA *query; + char buff[257],*end; + DBUG_ENTER("mysql_list_fields"); + DBUG_PRINT("enter",("table: '%s' wild: '%s'",table,wild ? wild : "")); + + LINT_INIT(query); + + end=strmake(strmake(buff, table,128)+1,wild ? wild : "",128); + if (simple_command(mysql,COM_FIELD_LIST,buff,(uint) (end-buff),1) || + !(query = read_rows(mysql,(MYSQL_FIELD*) 0,6))) + DBUG_RETURN(NULL); + + free_old_query(mysql); + if (!(result = (MYSQL_RES *) my_malloc(sizeof(MYSQL_RES), + MYF(MY_WME | MY_ZEROFILL)))) + { + free_rows(query); + DBUG_RETURN(NULL); + } + result->field_alloc=mysql->field_alloc; + mysql->fields=0; + result->field_count = (uint) query->rows; + result->fields= unpack_fields(query,&result->field_alloc, + result->field_count,1, + (my_bool) test(mysql->server_capabilities & + CLIENT_LONG_FLAG)); + result->eof=1; + DBUG_RETURN(result); +} + +/* List all running processes (threads) in server */ + +MYSQL_RES * STDCALL +mysql_list_processes(MYSQL *mysql) +{ + MYSQL_DATA *fields; + uint field_count; + uchar *pos; + DBUG_ENTER("mysql_list_processes"); + + LINT_INIT(fields); + if (simple_command(mysql,COM_PROCESS_INFO,"",0,0)) + DBUG_RETURN(0); + free_old_query(mysql); + pos=(uchar*) mysql->net.read_pos; + field_count=(uint) net_field_length(&pos); + if (!(fields = read_rows(mysql,(MYSQL_FIELD*) 0,5))) + DBUG_RETURN(NULL); + if (!(mysql->fields=unpack_fields(fields,&mysql->field_alloc,field_count,0, + (my_bool) test(mysql->server_capabilities & + CLIENT_LONG_FLAG)))) + DBUG_RETURN(0); + mysql->status=MYSQL_STATUS_GET_RESULT; + mysql->field_count=field_count; + DBUG_RETURN(mysql_store_result(mysql)); +} + + +int STDCALL +mysql_create_db(MYSQL *mysql, const char *db) +{ + DBUG_ENTER("mysql_createdb"); + DBUG_PRINT("enter",("db: %s",db)); + DBUG_RETURN(simple_command(mysql,COM_CREATE_DB,db, (ulong) strlen(db),0)); +} + + +int STDCALL +mysql_drop_db(MYSQL *mysql, const char *db) +{ + DBUG_ENTER("mysql_drop_db"); + DBUG_PRINT("enter",("db: %s",db)); + DBUG_RETURN(simple_command(mysql,COM_DROP_DB,db,(ulong) strlen(db),0)); +} + + +int STDCALL +mysql_shutdown(MYSQL *mysql) +{ + DBUG_ENTER("mysql_shutdown"); + DBUG_RETURN(simple_command(mysql,COM_SHUTDOWN,"",0,0)); +} + + +int STDCALL +mysql_refresh(MYSQL *mysql,uint options) +{ + uchar bits[1]; + DBUG_ENTER("mysql_refresh"); + bits[0]= (uchar) options; + DBUG_RETURN(simple_command(mysql,COM_REFRESH,(char*) bits,1,0)); +} + +int STDCALL +mysql_kill(MYSQL *mysql,ulong pid) +{ + char buff[12]; + DBUG_ENTER("mysql_kill"); + int4store(buff,pid); + DBUG_RETURN(simple_command(mysql,COM_PROCESS_KILL,buff,4,0)); +} + + +int STDCALL +mysql_dump_debug_info(MYSQL *mysql) +{ + DBUG_ENTER("mysql_dump_debug_info"); + DBUG_RETURN(simple_command(mysql,COM_DEBUG,"",0,0)); +} + +const char * STDCALL +mysql_stat(MYSQL *mysql) +{ + DBUG_ENTER("mysql_stat"); + if (simple_command(mysql,COM_STATISTICS,"",0,0)) + return mysql->net.last_error; + mysql->net.read_pos[mysql->packet_length]=0; /* End of stat string */ + if (!mysql->net.read_pos[0]) + { + mysql->net.last_errno=CR_WRONG_HOST_INFO; + strmov(mysql->net.last_error, ER(mysql->net.last_errno)); + return mysql->net.last_error; + } + DBUG_RETURN((char*) mysql->net.read_pos); +} + + +int STDCALL +mysql_ping(MYSQL *mysql) +{ + DBUG_ENTER("mysql_ping"); + DBUG_RETURN(simple_command(mysql,COM_PING,"",0,0)); +} + + +const char * STDCALL +mysql_get_server_info(MYSQL *mysql) +{ + return((char*) mysql->server_version); +} + + +const char * STDCALL +mysql_get_host_info(MYSQL *mysql) +{ + return(mysql->host_info); +} + + +uint STDCALL +mysql_get_proto_info(MYSQL *mysql) +{ + return (mysql->protocol_version); +} + +const char * STDCALL +mysql_get_client_info(void) +{ + return MYSQL_SERVER_VERSION; +} + + +int STDCALL +mysql_options(MYSQL *mysql,enum mysql_option option, const char *arg) +{ + DBUG_ENTER("mysql_option"); + DBUG_PRINT("enter",("option: %d",(int) option)); + switch (option) { + case MYSQL_OPT_CONNECT_TIMEOUT: + mysql->options.connect_timeout= *(uint*) arg; + break; + case MYSQL_OPT_COMPRESS: + mysql->options.compress=1; /* Remember for connect */ + break; + case MYSQL_OPT_NAMED_PIPE: + mysql->options.named_pipe=1; /* Force named pipe */ + break; + case MYSQL_INIT_COMMAND: + my_free(mysql->options.init_command,MYF(MY_ALLOW_ZERO_PTR)); + mysql->options.init_command=my_strdup(arg,MYF(MY_WME)); + break; + case MYSQL_READ_DEFAULT_FILE: + my_free(mysql->options.my_cnf_file,MYF(MY_ALLOW_ZERO_PTR)); + mysql->options.my_cnf_file=my_strdup(arg,MYF(MY_WME)); + break; + case MYSQL_READ_DEFAULT_GROUP: + my_free(mysql->options.my_cnf_group,MYF(MY_ALLOW_ZERO_PTR)); + mysql->options.my_cnf_group=my_strdup(arg,MYF(MY_WME)); + break; + case MYSQL_SET_CHARSET_DIR: + my_free(mysql->options.charset_dir,MYF(MY_ALLOW_ZERO_PTR)); + mysql->options.charset_dir=my_strdup(arg,MYF(MY_WME)); + break; + case MYSQL_SET_CHARSET_NAME: + my_free(mysql->options.charset_name,MYF(MY_ALLOW_ZERO_PTR)); + mysql->options.charset_name=my_strdup(arg,MYF(MY_WME)); + break; + default: + DBUG_RETURN(-1); + } + DBUG_RETURN(0); +} + +/**************************************************************************** +** Functions to get information from the MySQL structure +** These are functions to make shared libraries more usable. +****************************************************************************/ + +/* MYSQL_RES */ +my_ulonglong STDCALL mysql_num_rows(MYSQL_RES *res) +{ + return res->row_count; +} + +unsigned int STDCALL mysql_num_fields(MYSQL_RES *res) +{ + return res->field_count; +} + +my_bool STDCALL mysql_eof(MYSQL_RES *res) +{ + return res->eof; +} + +MYSQL_FIELD * STDCALL mysql_fetch_field_direct(MYSQL_RES *res,uint fieldnr) +{ + return &(res)->fields[fieldnr]; +} + +MYSQL_FIELD * STDCALL mysql_fetch_fields(MYSQL_RES *res) +{ + return (res)->fields; +} + +MYSQL_ROWS * STDCALL mysql_row_tell(MYSQL_RES *res) +{ + return res->data_cursor; +} + +uint STDCALL mysql_field_tell(MYSQL_RES *res) +{ + return (res)->current_field; +} + +/* MYSQL */ + +unsigned int STDCALL mysql_field_count(MYSQL *mysql) +{ + return mysql->field_count; +} + +my_ulonglong STDCALL mysql_affected_rows(MYSQL *mysql) +{ + return (mysql)->affected_rows; +} + +my_ulonglong STDCALL mysql_insert_id(MYSQL *mysql) +{ + return (mysql)->insert_id; +} + +uint STDCALL mysql_errno(MYSQL *mysql) +{ + return (mysql)->net.last_errno; +} + +const char * STDCALL mysql_error(MYSQL *mysql) +{ + return (mysql)->net.last_error; +} + +const char *STDCALL mysql_info(MYSQL *mysql) +{ + return (mysql)->info; +} + +ulong STDCALL mysql_thread_id(MYSQL *mysql) +{ + return (mysql)->thread_id; +} + +const char * STDCALL mysql_character_set_name(MYSQL *mysql) +{ + return mysql->charset->name; +} + + +uint STDCALL mysql_thread_safe(void) +{ +#ifdef THREAD + return 1; +#else + return 0; +#endif +} + +/**************************************************************************** +** Some support functions +****************************************************************************/ + +/* +** Add escape characters to a string (blob?) to make it suitable for a insert +** to should at least have place for length*2+1 chars +** Returns the length of the to string +*/ + +ulong STDCALL +mysql_escape_string(char *to,const char *from,ulong length) +{ + return mysql_sub_escape_string(default_charset_info,to,from,length); +} + +ulong STDCALL +mysql_real_escape_string(MYSQL *mysql, char *to,const char *from, + ulong length) +{ + return mysql_sub_escape_string(mysql->charset,to,from,length); +} + + +static ulong +mysql_sub_escape_string(CHARSET_INFO *charset_info, char *to, + const char *from, ulong length) +{ + const char *to_start=to; + const char *end; +#ifdef USE_MB + my_bool use_mb_flag=use_mb(charset_info); +#endif + for (end=from+length; from != end ; from++) + { +#ifdef USE_MB + int l; + if (use_mb_flag && (l = my_ismbchar(charset_info, from, end))) + { + while (l--) + *to++ = *from++; + from--; + continue; + } +#endif + switch (*from) { + case 0: /* Must be escaped for 'mysql' */ + *to++= '\\'; + *to++= '0'; + break; + case '\n': /* Must be escaped for logs */ + *to++= '\\'; + *to++= 'n'; + break; + case '\r': + *to++= '\\'; + *to++= 'r'; + break; + case '\\': + *to++= '\\'; + *to++= '\\'; + break; + case '\'': + *to++= '\\'; + *to++= '\''; + break; + case '"': /* Better safe than sorry */ + *to++= '\\'; + *to++= '"'; + break; + case '\032': /* This gives problems on Win32 */ + *to++= '\\'; + *to++= 'Z'; + break; + default: + *to++= *from; + } + } + *to=0; + return (ulong) (to-to_start); +} + + +char * STDCALL +mysql_odbc_escape_string(MYSQL *mysql, + char *to, ulong to_length, + const char *from, ulong from_length, + void *param, + char * (*extend_buffer) + (void *, char *, ulong *)) +{ + char *to_end=to+to_length-5; + const char *end; +#ifdef USE_MB + my_bool use_mb_flag=use_mb(mysql->charset); +#endif + + for (end=from+from_length; from != end ; from++) + { + if (to >= to_end) + { + to_length = (ulong) (end-from)+512; /* We want this much more */ + if (!(to=(*extend_buffer)(param, to, &to_length))) + return to; + to_end=to+to_length-5; + } +#ifdef USE_MB + { + int l; + if (use_mb_flag && (l = my_ismbchar(mysql->charset, from, end))) + { + while (l--) + *to++ = *from++; + from--; + continue; + } + } +#endif + switch (*from) { + case 0: /* Must be escaped for 'mysql' */ + *to++= '\\'; + *to++= '0'; + break; + case '\n': /* Must be escaped for logs */ + *to++= '\\'; + *to++= 'n'; + break; + case '\r': + *to++= '\\'; + *to++= 'r'; + break; + case '\\': + *to++= '\\'; + *to++= '\\'; + break; + case '\'': + *to++= '\\'; + *to++= '\''; + break; + case '"': /* Better safe than sorry */ + *to++= '\\'; + *to++= '"'; + break; + case '\032': /* This gives problems on Win32 */ + *to++= '\\'; + *to++= 'Z'; + break; + default: + *to++= *from; + } + } + return to; +} + +void STDCALL +myodbc_remove_escape(MYSQL *mysql,char *name) +{ + char *to; +#ifdef USE_MB + my_bool use_mb_flag=use_mb(mysql->charset); + char *end; + LINT_INIT(end); + if (use_mb_flag) + for (end=name; *end ; end++) ; +#endif + + for (to=name ; *name ; name++) + { +#ifdef USE_MB + int l; + if (use_mb_flag && (l = my_ismbchar( mysql->charset, name , end ) ) ) + { + while (l--) + *to++ = *name++; + name--; + continue; + } +#endif + if (*name == '\\' && name[1]) + name++; + *to++= *name; + } + *to=0; +} diff --git a/libmysqld/libmysqld.def b/libmysqld/libmysqld.def new file mode 100644 index 00000000000..0e126aa9ceb --- /dev/null +++ b/libmysqld/libmysqld.def @@ -0,0 +1,72 @@ +LIBRARY LIBMYSQLD +DESCRIPTION 'MySQL 4.0 Embedded Server Library' +VERSION 4.0 +EXPORTS + mysql_server_end + mysql_server_init + mysql_use_result + mysql_thread_safe + mysql_thread_id + mysql_store_result + mysql_stat + mysql_shutdown + mysql_select_db + mysql_row_tell + mysql_row_seek + mysql_real_query + mysql_real_connect + mysql_query + mysql_ping + mysql_options + mysql_num_rows + mysql_num_fields + mysql_list_tables + mysql_list_processes + mysql_list_fields + mysql_list_dbs + mysql_kill + mysql_insert_id + mysql_init + mysql_info + mysql_get_server_info + mysql_get_proto_info + mysql_get_host_info + mysql_get_client_info + mysql_free_result + mysql_field_tell + mysql_field_count + mysql_field_seek + mysql_fetch_row + mysql_fetch_lengths + mysql_fetch_fields + mysql_fetch_field_direct + mysql_fetch_field + mysql_escape_string + mysql_real_escape_string + mysql_error + mysql_errno + mysql_eof + mysql_dump_debug_info + mysql_drop_db + mysql_debug + mysql_data_seek + mysql_create_db + mysql_character_set_name + mysql_change_user + mysql_connect + mysql_close + mysql_affected_rows + mysql_thread_init + mysql_thread_end + mysql_send_query + mysql_read_query_result + mysql_refresh + mysql_odbc_escape_string + myodbc_remove_escape + + + + + + + |