diff options
author | monty@work.mysql.com <> | 2001-05-31 12:56:05 +0200 |
---|---|---|
committer | monty@work.mysql.com <> | 2001-05-31 12:56:05 +0200 |
commit | df56b252a7c9ffa549b02d53b44fe4afa558e74d (patch) | |
tree | 7fc3c8f38fbab4a3cef1388c3bc2e53b0455c75c | |
parent | 052225a4e60d8da2cf74fcd6694b4c884775591c (diff) | |
parent | 8ad79ab09bd66c2e32f125c8fbb1a1603dbc59a2 (diff) | |
download | mariadb-git-df56b252a7c9ffa549b02d53b44fe4afa558e74d.tar.gz |
merge
69 files changed, 2979 insertions, 422 deletions
diff --git a/BUILD/FINISH.sh b/BUILD/FINISH.sh index 4f13f5f8e4d..368ab339c2b 100644 --- a/BUILD/FINISH.sh +++ b/BUILD/FINISH.sh @@ -15,6 +15,10 @@ $make -k clean || true aclocal && autoheader && aclocal && automake && autoconf (cd bdb/dist && sh s_all) (cd innobase && aclocal && autoheader && aclocal && automake && autoconf) +if [ -d gemini ] +then + (cd gemini && aclocal && autoheader && aclocal && automake && autoconf) +fi CFLAGS=\"$cflags\" CXX=gcc CXXFLAGS=\"$cxxflags\" $configure" diff --git a/BUILD/SETUP.sh b/BUILD/SETUP.sh index 1f45c5c18cb..778625e9e75 100644 --- a/BUILD/SETUP.sh +++ b/BUILD/SETUP.sh @@ -43,8 +43,11 @@ alpha_cflags="-mcpu=ev6 -Wa,-mev6" # Not used yet pentium_cflags="-mpentiumpro" sparc_cflags="" +# be as fast as we can be without losing our ability to backtrace fast_cflags="-O3 -fno-omit-frame-pointer" -reckless_cflags="-O3 -fomit-frame-pointer -ffixed-ebp" +# this is one is for someone who thinks 1% speedup is worth not being +# able to backtrace +reckless_cflags="-O3 -fomit-frame-pointer " debug_cflags="-DEXTRA_DEBUG -DFORCE_INIT_OF_VARS -DSAFEMALLOC -DSAFE_MUTEX -O2" base_cxxflags="-felide-constructors -fno-exceptions -fno-rtti" diff --git a/BUILD/compile-pentium b/BUILD/compile-pentium index 9607ca03e7e..11559be93de 100755 --- a/BUILD/compile-pentium +++ b/BUILD/compile-pentium @@ -3,7 +3,7 @@ path=`dirname $0` . "$path/SETUP.sh" -extra_flags="$pentium_cflags $reckless_cflags" +extra_flags="$pentium_cflags $fast_cflags" extra_configs="$pentium_configs" strip=yes diff --git a/BitKeeper/etc/logging_ok b/BitKeeper/etc/logging_ok index affc2c7b7cd..ab0a87859ba 100644 --- a/BitKeeper/etc/logging_ok +++ b/BitKeeper/etc/logging_ok @@ -1,2 +1,6 @@ -tonu@hundin.mysql.fi +jani@janikt.pp.saunalahti.fi +monty@hundin.mysql.fi +mwagner@evoq.mwagner.org sasha@mysql.sashanet.com +tonu@hundin.mysql.fi +monty@work.mysql.com diff --git a/Docs/Flags/latvia.gif b/Docs/Flags/latvia.gif Binary files differnew file mode 100644 index 00000000000..8a898328ebe --- /dev/null +++ b/Docs/Flags/latvia.gif diff --git a/Docs/Flags/latvia.txt b/Docs/Flags/latvia.txt new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/Docs/Flags/latvia.txt diff --git a/Docs/manual.texi b/Docs/manual.texi index f63e2c17ff8..02d698810b3 100644 --- a/Docs/manual.texi +++ b/Docs/manual.texi @@ -314,7 +314,6 @@ Windows Notes * Windows and SSH:: Connecting to a remote @strong{MySQL} from Windows with SSH * Windows symbolic links:: Splitting data across different disks under Win32 * Windows compiling:: Compiling MySQL clients on Windows. -* Windows and BDB tables.:: Windows and BDB Tables * Windows vs Unix:: @strong{MySQL}-Windows compared to Unix @strong{MySQL} Post-installation Setup and Testing @@ -530,10 +529,25 @@ BDB or Berkeley_DB Tables GEMINI Tables -* GEMINI overview:: -* GEMINI start:: -* GEMINI features:: -* GEMINI TODO:: +* GEMINI Overview:: +* Using GEMINI Tables:: + +GEMINI Overview + +* GEMINI Features:: +* GEMINI Concepts:: +* GEMINI Limitations:: + +Using GEMINI Tables + +* Startup Options:: +* Creating GEMINI Tables:: +* Backing Up GEMINI Tables:: +* Restoring GEMINI Tables:: +* Using Auto_Increment Columns With GEMINI Tables:: +* Performance Considerations:: +* Sample Configurations:: +* When To Use GEMINI Tables:: InnoDB Tables @@ -554,7 +568,7 @@ InnoDB Tables Creating InnoDB table space -* Error creating InnoDB:: +* Error creating InnoDB:: Backing up and recovering an InnoDB database @@ -717,6 +731,7 @@ MySQL Utilites Maintaining a MySQL Installation * Table maintenance:: Table maintenance and crash recovery +* Using mysqlcheck:: Using mysqlcheck for maintenance and recovery * Maintenance regimen:: Setting up a table maintenance regimen * Table-info:: Getting information about a table * Crash recovery:: Using @code{myisamchk} for crash recovery @@ -2835,7 +2850,7 @@ PTS: Project Tracking System. @item @uref{http://tomato.nvgc.vt.edu/~hroberts/mot} Job and software tracking system. -@item @uref{http://www.cynergi.net/non-secure/exportsql/} +@item @uref{http://www.cynergi.net/exportsql/} ExportSQL: A script to export data from Access95+. @item @uref{http://SAL.KachinaTech.COM/H/1/MYSQL.html} @@ -3060,21 +3075,21 @@ from the local @strong{MySQL} list. The following @strong{MySQL} mailing lists exist: @table @code -@item @email{announce-subscribe@@lists.mysql.com, announce} +@item @email{announce-subscribe@@lists.mysql.com} announce This is for announcement of new versions of @strong{MySQL} and related programs. This is a low volume list all @strong{MySQL} users should subscribe to. -@item @email{mysql-subscribe@@lists.mysql.com, mysql} +@item @email{mysql-subscribe@@lists.mysql.com} mysql The main list for general @strong{MySQL} discussion. Please note that some topics are better discussed on the more-specialized lists. If you post to the wrong list, you may not get an answer! -@item @email{mysql-digest-subscribe@@lists.mysql.com, mysql-digest} +@item @email{mysql-digest-subscribe@@lists.mysql.com} mysql-digest The @code{mysql} list in digest form. That means you get all individual messages, sent as one large mail message once a day. -@item @email{bugs-subscribe@@lists.mysql.com, bugs} +@item @email{bugs-subscribe@@lists.mysql.com} bugs On this list you should only post a full, repeatable bug report using the @code{mysqlbug} script (if you are running on Windows, you should include a description of the operating system and the @strong{MySQL} version). @@ -3085,55 +3100,45 @@ bugs posted on this list will be corrected or documented in the next @strong{MySQL} release! If there are only small code changes involved, we will also post a patch that fixes the problem. -@item @email{bugs-digest-subscribe@@lists.mysql.com, bugs-digest} +@item @email{bugs-digest-subscribe@@lists.mysql.com} bugs-digest The @code{bugs} list in digest form. -@item @email{developer-subscribe@@lists.mysql.com, developer} -This list has been depreciated in favor of the -@email{internals-subscribe@@lists.mysql.com, internals} list (below). - -@item @email{developer-digest-subscribe@@lists.mysql.com, developer-digest} -This list has been deprecated in favor of the -@email{internals-digest-subscribe@@lists.mysql.com, internals-digest} -list (below). - -@item @email{internals-subscribe@@lists.mysql.com, internals} +@item @email{internals-subscribe@@lists.mysql.com} internals A list for people who work on the @strong{MySQL} code. On this list one can also discuss @strong{MySQL} development and post patches. -@item @email{internals-digest-subscribe@@lists.mysql.com, internals-digest} -A digest version of the @email{internals-subscribe@@lists.mysql.com, internals} -list. +@item @email{internals-digest-subscribe@@lists.mysql.com} internals-digest +A digest version of the @code{internals} list. -@item @email{java-subscribe@@lists.mysql.com, java} +@item @email{java-subscribe@@lists.mysql.com} java Discussion about @strong{MySQL} and Java. Mostly about the JDBC drivers. -@item @email{java-digest-subscribe@@lists.mysql.com, java-digest} +@item @email{java-digest-subscribe@@lists.mysql.com} java-digest A digest version of the @code{java} list. -@item @email{win32-subscribe@@lists.mysql.com, win32} +@item @email{win32-subscribe@@lists.mysql.com} win32 All things concerning @strong{MySQL} on Microsoft operating systems such as Win95, Win98, NT, and Win2000. -@item @email{win32-digest-subscribe@@lists.mysql.com, win32-digest} +@item @email{win32-digest-subscribe@@lists.mysql.com} win32-digest A digest version of the @code{win32} list. -@item @email{myodbc-subscribe@@lists.mysql.com, myodbc} +@item @email{myodbc-subscribe@@lists.mysql.com} myodbc All things about connecting to @strong{MySQL} with ODBC. -@item @email{myodbc-digest-subscribe@@lists.mysql.com, myodbc-digest} +@item @email{myodbc-digest-subscribe@@lists.mysql.com} myodbc-digest A digest version of the @code{myodbc} list. -@item @email{plusplus-subscribe@@lists.mysql.com, plusplus} +@item @email{plusplus-subscribe@@lists.mysql.com} plusplus All things concerning programming with the C++ API to @strong{MySQL}. -@item @email{plusplus-digest-subscribe@@lists.mysql.com, plusplus-digest} +@item @email{plusplus-digest-subscribe@@lists.mysql.com} plusplus-digest A digest version of the @code{plusplus} list. -@item @email{msql-mysql-modules-subscribe@@lists.mysql.com, msql-mysql-modules} -A list about the Perl support in @strong{MySQL}. +@item @email{msql-mysql-modules-subscribe@@lists.mysql.com} msql-mysql-modules +A list about the Perl support in @strong{MySQL}. msql-mysql-modules -@item @email{msql-mysql-modules-digest-subscribe@@lists.mysql.com, msql-mysql-modules-digest} +@item @email{msql-mysql-modules-digest-subscribe@@lists.mysql.com} msql-mysql-modules-digest A digest version of the @code{msql-mysql-modules} list. @end table @@ -3149,16 +3154,16 @@ English. Note that these are not operated by @strong{MySQL AB}, so we can't guarantee the quality on these. @table @code -@item @email{mysql-france-subscribe@@yahoogroups.com, A French mailing list} -@item @email{list@@tinc.net, A Korean mailing list} +@item @email{mysql-france-subscribe@@yahoogroups.com} A French mailing list +@item @email{list@@tinc.net} A Korean mailing list Email @code{subscribe mysql your@@email.address} to this list. -@item @email{mysql-de-request@@lists.4t2.com, A German mailing list} +@item @email{mysql-de-request@@lists.4t2.com} A German mailing list Email @code{subscribe mysql-de your@@email.address} to this list. You can find information about this mailing list at @uref{http://www.4t2.com/mysql}. -@item @email{mysql-br-request@@listas.linkway.com.br, A Portugese mailing list} +@item @email{mysql-br-request@@listas.linkway.com.br} A Portugese mailing list Email @code{subscribe mysql-br your@@email.address} to this list. -@item @email{mysql-alta@@elistas.net, A Spanish mailing list} +@item @email{mysql-alta@@elistas.net} A Spanish mailing list Email @code{subscribe mysql your@@email.address} to this list. @end table @@ -4517,6 +4522,12 @@ Please report bad or out-of-date mirrors to @email{webmaster@@mysql.com}. @uref{ftp://ftp.esat.net/mirrors/download.sourceforge.net/pub/mirrors/mysql/, FTP} @item +@c Added 20010524 +@c EMAIL: arvids@parks.lv (Arvids) +@image{Flags/latvia} Latvia [linux.lv] @ +@uref{ftp://ftp.linux.lv/pub/software/mysql/, FTP} + +@item @c Added 20001125 @c EMAIL: mleicher@silverpoint.nl (Marcel Leicher) @image{Flags/netherlands} Netherlands [Silverpoint] @ @@ -4796,10 +4807,10 @@ Please report bad or out-of-date mirrors to @email{webmaster@@mysql.com}. @uref{http://mysql.linuxwired.net/, WWW} @uref{ftp://ftp.linuxwired.net/pub/mirrors/mysql/, FTP} -@item +@c @item @c EMAIL: dan@surfsouth.com (Dan Muntz) -@image{Flags/usa} USA [Venoma.Org/Valdosta, GA] @ -@uref{http://mysql.venoma.org/, WWW} +@c @image{Flags/usa} USA [Venoma.Org/Valdosta, GA] @ +@c @uref{http://mysql.venoma.org/, WWW} @item @c EMAIL: hkind@adgrafix.com (Hans Kind) @@ -4999,8 +5010,8 @@ Please report bad or out-of-date mirrors to @email{webmaster@@mysql.com}. @c Added 980610 @c EMAIL: jason@dstc.edu.au (Jason Andrade) @image{Flags/australia} Australia [AARNet/Queensland] @ -@uref{http://mirror.aarnet.edu.au/mysql, WWW} -@uref{ftp://mirror.aarnet.edu.au/pub/mysql, FTP} +@uref{http://mysql.mirror.aarnet.edu.au/, WWW} +@uref{ftp://mysql.mirror.aarnet.edu.au/, FTP} @c @item @c Added 980805. Removed 000102 'no such directory' @@ -8479,7 +8490,7 @@ The following @code{configure} command should work: @example shell> CFLAGS="-D_XOPEN_XPG4" CXX=gcc CXXFLAGS="-D_XOPEN_XPG4" \ ./configure \ - --with-debug --prefix=/usr/local/mysql \ + --prefix=/usr/local/mysql \ --with-named-thread-libs="-lgthreads -lsocket -lgen -lgthreads" \ --with-named-curses-libs="-lcurses" @end example @@ -8876,7 +8887,6 @@ This is also described in the @file{README} file that comes with the * Windows and SSH:: Connecting to a remote @strong{MySQL} from Windows with SSH * Windows symbolic links:: Splitting data across different disks under Win32 * Windows compiling:: Compiling MySQL clients on Windows. -* Windows and BDB tables.:: Windows and BDB Tables * Windows vs Unix:: @strong{MySQL}-Windows compared to Unix @strong{MySQL} @end menu @@ -8925,9 +8935,8 @@ symbolic links, BDB and InnoDB tables. @item @code{mysqld-opt} @tab Optimized binary with no support for transactional tables. @item @code{mysqld-nt} @tab -Optimized for a Pentium Pro processor. Has support for -named pipes. You can run this version on Win98, but in -this case no named pipes are created and you must +Optimized binary for NT with support for named pipes. You can run this +version on Win98, but in this case no named pipes are created and you must have TCP/IP installed. @item @code{mysqld-max} @tab Optimized binary with support for symbolic links, BDB and InnoDB tables. @@ -9229,7 +9238,7 @@ text @code{D:\data\foo}. After that, all tables created in the database @cindex compiling, on Windows @cindex Windows, compiling on -@node Windows compiling, Windows and BDB tables., Windows symbolic links, Windows +@node Windows compiling, Windows vs Unix, Windows symbolic links, Windows @subsection Compiling MySQL Clients on Windows In your source files, you should include @file{windows.h} before you include @@ -9249,19 +9258,9 @@ with the static @file{mysqlclient.lib} library. Note that as the mysqlclient libraries are compiled as threaded libraries, you should also compile your code to be multi-threaded! -@cindex BDB tables -@cindex tables, BDB -@node Windows and BDB tables., Windows vs Unix, Windows compiling, Windows -@subsection Windows and BDB Tables - -We will shortly do a full test on the new BDB interface on Windows. -When this is done we will start to release binary distributions (for -Windows and Unix) of @strong{MySQL} that will include support for BDB -tables. - @cindex Windows, versus Unix @cindex operating systems, Windows versus Unix -@node Windows vs Unix, , Windows and BDB tables., Windows +@node Windows vs Unix, , Windows compiling, Windows @subsection MySQL-Windows Compared to Unix MySQL @strong{MySQL}-Windows has by now proven itself to be very stable. This version @@ -9528,19 +9527,19 @@ and are configured with the following compilers and options: @code{CC=gcc CXX=gcc CXXFLAGS="-O3 -felide-constructors" ./configure --prefix=/usr/local/mysql --disable-shared --with-extra-charsets=complex --enable-assembler} @item SunOS 5.5.1 sun4u with @code{egcs} 1.0.3a -@code{CC=gcc CFLAGS="-O3 -fomit-frame-pointer" CXX=gcc CXXFLAGS="-O3 -fomit-frame-pointer -felide-constructors -fno-exceptions -fno-rtti" ./configure --prefix=/usr/local/mysql --with-low-memory --with-extra-charsets=complex} +@code{CC=gcc CFLAGS="-O3" CXX=gcc CXXFLAGS="-O3 -felide-constructors -fno-exceptions -fno-rtti" ./configure --prefix=/usr/local/mysql --with-low-memory --with-extra-charsets=complex} @item SunOS 5.6 sun4u with @code{egcs} 2.90.27 -@code{CC=gcc CFLAGS="-O3 -fomit-frame-pointer" CXX=gcc CXXFLAGS="-O3 -fomit-frame-pointer -felide-constructors -fno-exceptions -fno-rtti" ./configure --prefix=/usr/local/mysql --with-low-memory --with-extra-charsets=complex} +@code{CC=gcc CFLAGS="-O3" CXX=gcc CXXFLAGS="-O3 -felide-constructors -fno-exceptions -fno-rtti" ./configure --prefix=/usr/local/mysql --with-low-memory --with-extra-charsets=complex} @item SunOS 5.6 i86pc with @code{gcc} 2.8.1 @code{CC=gcc CXX=gcc CXXFLAGS=-O3 ./configure --prefix=/usr/local/mysql --with-low-memory --with-extra-charsets=complex} @item Linux 2.0.33 i386 with @code{pgcc} 2.90.29 (@code{egcs} 1.0.3a) -@code{CFLAGS="-O3 -mpentium -mstack-align-double -fomit-frame-pointer" CXX=gcc CXXFLAGS="-O3 -mpentium -mstack-align-double -fomit-frame-pointer -felide-constructors -fno-exceptions -fno-rtti" ./configure --prefix=/usr/local/mysql --enable-assembler --with-mysqld-ldflags=-all-static --with-extra-charsets=complex} +@code{CFLAGS="-O3 -mpentium -mstack-align-double" CXX=gcc CXXFLAGS="-O3 -mpentium -mstack-align-double -felide-constructors -fno-exceptions -fno-rtti" ./configure --prefix=/usr/local/mysql --enable-assembler --with-mysqld-ldflags=-all-static --with-extra-charsets=complex} @item Linux 2.2.x with x686 with @code{gcc} 2.95.2 -@code{CFLAGS="-O3 -mpentiumpro -fomit-frame-pointer" CXX=gcc CXXFLAGS="-O3 -mpentiumpro -fomit-frame-pointer -felide-constructors -fno-exceptions -fno-rtti" ./configure --prefix=/usr/local/mysql --enable-assembler --with-mysqld-ldflags=-all-static --disable-shared --with-extra-charset=complex} +@code{CFLAGS="-O3 -mpentiumpro" CXX=gcc CXXFLAGS="-O3 -mpentiumpro -felide-constructors -fno-exceptions -fno-rtti" ./configure --prefix=/usr/local/mysql --enable-assembler --with-mysqld-ldflags=-all-static --disable-shared --with-extra-charset=complex} @item SCO 3.2v5.0.4 i386 with @code{gcc} 2.7-95q4 @code{CC=gcc CXX=gcc CXXFLAGS=-O3 ./configure --prefix=/usr/local/mysql --with-extra-charsets=complex} @@ -10115,7 +10114,7 @@ If you are using BDB (Berkeley DB) tables, you should familiarize yourself with the different BDB specific startup options. @xref{BDB start}. If you are using Gemini tables, refer to the Gemini-specific startup options. -@xref{GEMINI start}. +@xref{Using GEMINI Tables}. If you are using InnoDB tables, refer to the InnoDB-specific startup options. @xref{InnoDB start}. @@ -10743,8 +10742,8 @@ the old @code{ISAM} type. You don't have to convert your old tables to use these with Version 3.23. By default, all new tables will be created with type @code{MyISAM} (unless you start @code{mysqld} with the @code{--default-table-type=isam} option). You can change an @code{ISAM} -table to a @code{MyISAM} table with @code{ALTER TABLE} or the Perl script -@code{mysql_convert_table_format}. +table to a @code{MyISAM} table with @code{ALTER TABLE table_name TYPE=MyISAM} +or the Perl script @code{mysql_convert_table_format}. Version 3.22 and 3.21 clients will work without any problems with a Version 3.23 server. @@ -18865,10 +18864,10 @@ When you insert a value of @code{NULL} (recommended) or @code{0} into an If you delete the row containing the maximum value for an @code{AUTO_INCREMENT} column, the value will be reused with an -@code{ISAM}, @code{BDB} or @code{INNODB} table but not with a -@code{MyISAM} table. If you delete all rows in the table with -@code{DELETE FROM table_name} (without a @code{WHERE}) in -@code{AUTOCOMMIT} mode, the sequence starts over for both table types. +@code{ISAM}, @code{GEMINI} or @code{BDB} table but not with a +@code{MyISAM} or @code{InnoDB} table. If you delete all rows in the table +with @code{DELETE FROM table_name} (without a @code{WHERE}) in +@code{AUTOCOMMIT} mode, the sequence starts over for all table types. @strong{NOTE:} There can be only one @code{AUTO_INCREMENT} column per table, and it must be indexed. @strong{MySQL} Version 3.23 will also only @@ -24635,87 +24634,849 @@ not in @code{auto_commit} mode, until this problem is fixed (the fix is not trivial). @end itemize -@cindex tables, @code{GEMINI} +@cindex GEMINI tables @node GEMINI, InnoDB, BDB, Table types @section GEMINI Tables +@cindex GEMINI tables, overview @menu -* GEMINI overview:: -* GEMINI start:: -* GEMINI features:: -* GEMINI TODO:: +* GEMINI Overview:: +* Using GEMINI Tables:: @end menu -@node GEMINI overview, GEMINI start, GEMINI, GEMINI -@subsection Overview of GEMINI tables +@node GEMINI Overview, Using GEMINI Tables, GEMINI, GEMINI +@subsection GEMINI Overview + +@code{GEMINI} is a transaction-safe table handler for @strong{MySQL}. It +provides row-level locking, robust transaction support and reliable +crash recovery. It is targeted for databases that need to handle heavy +multi-user updates typical of transaction processing applications while +still providing excellent performance for read-intensive operations. The +@code{GEMINI} table type is developed and supported by NuSphere +Corporation (see @url{http://www.nusphere.com}). + +@code{GEMINI} provides full ACID transaction properties (Atomic, +Consistent, Independent, and Durable) with a programming model that +includes support for statement atomicity and all four standard isolation +levels (Read Uncommitted, Read Committed, Repeatable Read, and +Serializable) defined in the SQL standard. + +The @code{GEMINI} tables support row-level and table-level locking to +increase concurrency in applications and allow reading of tables without +locking for maximum concurrency in a heavy update environment. The +transaction, locking, and recovery mechanisms are tightly integrated to +eliminate unnecessary administration overhead. + +In general, if @code{GEMINI} tables are selected for an application, it +is recommended that all tables updated in the application be +@code{GEMINI} tables to provide well-defined system behavior. If +non-@code{GEMINI} tables are mixed into the application then, ACID +transaction properties cannot be maintained. While there are clearly +cases where mixing table types is appropriate, it should always be done +with careful consideration of the impact on transaction consistency and +recoverability needs of the application and underlying database. + +The @code{GEMINI} table type is derived from a successful commercial +database and uses the storage kernel technology tightly integrated with +@strong{MySQL} server. The basic @code{GEMINI} technology is in use by +millions of users worldwide in production environments today. This +maturity allows @code{GEMINI} tables to provide a solution for those +users who require transaction-based behavior as part of their +applications. -The @code{GEMINI} table type is developed and supported by NuSphere Corporation -(@uref{http://www.nusphere.com}). It features row-level locking, transaction -support (@code{COMMIT} and @code{ROLLBACK}), and automatic crash recovery. +The @code{GEMINI} table handler supports a configurable data cache that +allows a significant portion of any database to be maintained in memory +while still allowing durable updates. -@code{GEMINI} tables will be included in some future @strong{MySQL} 3.23.X -source distribution. +@cindex GEMINI tables, features +@menu +* GEMINI Features:: +* GEMINI Concepts:: +* GEMINI Limitations:: +@end menu -@node GEMINI start, GEMINI features, GEMINI overview, GEMINI -@subsection GEMINI startup options +@node GEMINI Features, GEMINI Concepts, GEMINI Overview, GEMINI Overview +@subsubsection GEMINI Features -If you are running with @code{AUTOCOMMIT=0} then your changes in @code{GEMINI} -tables will not be updated until you execute @code{COMMIT}. Instead of commit -you can execute @code{ROLLBACK} to forget your changes. @xref{COMMIT}. +The following summarizes the major features provided by @code{GEMINI} +tables. -If you are running with @code{AUTOCOMMIT=1} (the default), your changes -will be committed immediately. You can start an extended transaction with -the @code{BEGIN WORK} SQL command, after which your changes will not be -committed until you execute @code{COMMIT} (or decide to @code{ROLLBACK} -the changes). +@itemize @bullet +@item +Supports all optimization statistics used by the @strong{MySQL} optimizer +including table cardinality, index range estimates and multi-component +selectivity to insure optimal query performance. -The following options to @code{mysqld} can be used to change the behavior of -GEMINI tables: +@item +Maintains exact cardinality information for each table so @code{SELECT +COUNT(*) FROM} table-name always returns an answer immediately. + +@item +Supports index-only queries; when index data is sufficient to resolve a +query no record data is read (for non character types). + +@item +@code{GEMINI} uses block based I/O for better performance. There is no +performance penalty for using @code{VARCHAR} fields. The maximum record size is +currently 32K. + +@item +The number of rows in a single @code{GEMINI} table can be 4 quintillion +(full use of 64 bits). + +@item +Individual tables can be as large as 16 petabytes. + +@item +Locking is done at a record or row level rather than at table level +unless table locks are explicitly requested. When a row is inserted into +a table, other rows can be updated, inserted or deleted without waiting +for the inserted row to be committed. + +@item +Provides durable transactions backed by a crash recovery mechanism that +returns the database to a known consistent state in the event of an +unexpected failure. + +@item +Support for all isolation levels and statement atomicity defined in the +SQL standard. + +@item +Reliable Master Replication; the master database can survive system +failure and recover all committed transactions. +@end itemize + +@cindex GEMINI tables, concepts +@node GEMINI Concepts, GEMINI Limitations, GEMINI Features, GEMINI Overview +@subsubsection GEMINI Concepts + +This section highlights some of the important concepts behind +@code{GEMINI} and the @code{GEMINI} programming model, including: + +@itemize @bullet +@item +ACID Transactions +@item +Transaction COMMIT/ROLLBACK +@item +Statement Atomicity +@item +Recovery +@item +Isolation Levels +@item +Row-Level Locking +@end itemize + +These features are described below. + +@cindex GEMINI tables, ACID transactions +@noindent +@strong{ACID Transactions} + +ACID in the context of transactions is an acronym which stands for +@emph{Atomicity}, @emph{Consistency}, @emph{Isolation}, @emph{Durability}. + +@multitable @columnfractions .25 .75 +@item @sc{Attribute} @tab @sc{Description} +@item +@strong{Atomicity} +@tab A transaction allows for the grouping of one or more changes to +tables and rows in the database to form an atomic or indivisible +operation. That is, either all of the changes occur or none of them +do. If for any reason the transaction cannot be completed, everything +this transaction changed can be restored to the state it was in prior to +the start of the transaction via a rollback operation. + +@item +@strong{Consistency} +@tab +Transactions always operate on a consistent view of the data and when +they end always leave the data in a consistent state. Data may be said to +be consistent as long as it conforms to a set of invariants, such as no +two rows in the customer table have the same customer ID and all orders +have an associated customer row. While a transaction executes, these +invariants may be violated, but no other transaction will be allowed to +see these inconsistencies, and all such inconsistencies will have been +eliminated by the time the transaction ends. + +@item +@strong{Isolation} +@tab To a given transaction, it should appear as though it is running +all by itself on the database. The effects of concurrently running +transactions are invisible to this transaction, and the effects of this +transaction are invisible to others until the transaction is committed. + +@item +@strong{Durability} +@tab Once a transaction is committed, its effects are guaranteed to +persist even in the event of subsequent system failures. Until the +transaction commits, not only are any changes made by that transaction +not durable, but are guaranteed not to persist in the face of a system +failures, as crash recovery will rollback their effects. +@end multitable + +@cindex GEMINI tables, COMMIT/ROLLBACK +@noindent +@strong{Transaction COMMIT/ROLLBACK} + +As stated above, a transaction is a group of work being done to +data. Unless otherwise directed, @strong{MySQL} considers each statement +a transaction in itself. Multiple updates can be accomplished by placing +them in a single statement, however they are limited to a single table. + +Applications tend to require more robust use of transaction +concepts. Take, for example, a system that processes an order: A row may +be inserted in an order table, additional rows may be added to an +order-line table, updates may be made to inventory tables, etc. It is +important that if the order completes, all the changes are made to all +the tables involved; likewise if the order fails, none of the changes to +the tables must occur. To facilitate this requirement, @strong{MySQL} +has syntax to start a transaction called @code{BEGIN WORK}. All +statements that occur after the @code{BEGIN WORK} statement are grouped +into a single transaction. The end of this transaction occurs when a +@code{COMMIT} or @code{ROLLBACK} statement is encountered. After the +@code{COMMIT} or @code{ROLLBACK} the system returns back to the behavior +before the @code{BEGIN WORK} statement was encountered where every +statement is a transaction. + +To permanently turn off the behavior where every statement is a +transaction, @strong{MySQL} added a variable called +@code{AUTOCOMMIT}. The @code{AUTOCOMMIT} variable can have two values, +@code{1} and @code{0}. The mode where every statement is a transaction +is when @code{AUTOCOMMIT} is set to @code{1} (@code{AUTOCOMMIT=1}). When +@code{AUTOCOMMIT} is set to @code{0} (@code{AUTOCOMMIT=0}), then every +statement is part of the same transaction until the transaction end by +either @code{COMMIT} or @code{ROLLBACK}. Once a transaction completes, a +new transaction is immediately started and the process repeats. + +Here is an example of the SQL statements that you may find in a typical +order: + +@example +BEGIN WORK; + INSERT INTO order VALUES ...; + INSERT INTO order-lines VALUES ...; + INSERT INTO order-lines VALUES ...; + INSERT INTO order-lines VALUES ...; + UPDATE inventory WHERE ...; +COMMIT; +@end example + +This example shows how to use the @code{BEGIN WORK} statement to start a +transaction. If the variable @code{AUTOCOMMIT} is set to @code{0}, then +a transaction would have been started already. In this case, the +@code{BEGIN WORK} commits the current transaction and starts a new one. + +@cindex GEMINI tables, statement atomicity +@noindent +@strong{Statement Atomicity} + +As mentioned above, when running with @code{AUTOCOMMIT} set to @code{1}, +each statement executes as a single transaction. When a statement has an +error, then all changes make by the statement must be +undone. Transactions support this behavior. Non-transaction safe table +handlers would have a partial statement update where some of the changes +from the statement would be contained in the database and other changes +from the statement would not. Work would need to be done to manually +recover from the error. + +@cindex GEMINI tables, recovery +@noindent +@strong{Recovery} + +Transactions are the basis for database recovery. Recovery is what +supports the Durability attribute of the ACID transaction. + +@code{GEMINI} uses a separate file called the Recovery Log located in +the @code{$DATADIR} directory named @code{gemini.rl}. This file +maintains the integrity of all the @code{GEMINI} tables. @code{GEMINI} +can not recover any data from non-@code{GEMINI} tables. In addition, the +@code{gemini.rl} file is used to rollback transactions in support of the +@code{ROLLBACK} statement. + +In the event of a system failure, the next time the @strong{MySQL} +server is started, @code{GEMINI} will automatically go through its +crash recovery process. The result of crash recovery is that all the +@code{GEMINI} tables will contain the latest changes made to them, and +all transactions that were open at the time of the crash will have been +rolled back. + +The @code{GEMINI} Recovery Log reuses space when it can. Space can be +reused when information in the Recovery Log is no longer needed for +crash recovery or rollback. + +@cindex GEMINI tables, isolation levels +@noindent +@strong{Isolation Levels} + +There are four isolation levels supported by @code{GEMINI}: + +@itemize @bullet +@item +READ UNCOMMITTED +@item +READ COMMITTED +@item +REPEATABLE READ +@item +SERIALIZABLE +@end itemize + +These isolation levels apply only to shared locks obtained by select +statements, excluding select for update. Statements that get exclusive +locks always retain those locks until the transaction commits or rolls +back. + +By default, @code{GEMINI} operates at the @code{READ COMMITTED} +level. You can override the default using the following command: + +@example +SET [GLOBAL | SESSION] TRANSACTION ISOLATION LEVEL [READ UNCOMMITTED | +READ COMMITTED | REPEATABLE READ | SERIALIZABLE ] +@end example + +If the @code{SESSION} qualifier used, the specified isolation level +persists for the entire session. If the @code{GLOBAL} qualifier is used, +the specified isolation level is applied to all new connections from +this point forward. Note that the specified isolation level will not +change the behavior for existing connections including the connection +that exectues the @code{SET GLOBAL TRANSACTION ISOLATION LEVEL} +statement. @multitable @columnfractions .30 .70 -@item @strong{Option} @tab @strong{Meaning} -@item @code{--gemini-full-recovery} @tab Default. -@item @code{--gemini-no-recovery} @tab Turn off recovery logging. Not recommended. -@item @code{--gemini-lazy-commit} @tab Relaxes the flush log at commit rule. -@item @code{--gemini-unbuffered-io} @tab All database writes bypass OS cache. -@item @code{--skip-gemini} @tab Don't use Gemini. -@item @code{--O gemini_db_buffers=#} @tab Number of database buffers in database cache. -@item @code{--O gemini_connection_limit=#} @tab Maximum number of connections to Gemini. -@item @code{--O gemini_spin_retries=#} @tab Spin lock retries (optimization). -@item @code{--O gemini_io_threads=#} @tab Number of background I/O threads. -@item @code{--O gemini_lock_table_size=#} @tab Set the maximum number of locks. Default 4096. +@item @sc{Isolation Level} @tab @sc{Description} + +@item +@strong{READ UNCOMMITTED} +@tab Does not obtain any locks when reading rows. This means that if a +row is locked by another process in a transaction that has a more strict +isolation level, the @code{READ UNCOMMITTED} query will not wait until +the locks are released before reading the row. You will get an error if +attempt any updates while running at this isolation level. + +@item +@strong{READ COMMITTED} +@tab Locks the requested rows long enough to copy the row from the +database block to the client row buffer. If a @code{READ COMMITTED} +query finds that a row is locked exclusively by another process, it will +wait until either the row has been released, or the lock timeout value +has expired. + +@item +@strong{REPEATABLE READ} +@tab Locks all the rows needed to satisfy the query. These locks are +held until the transaction ends (commits or rolls back). If a +@code{REPEATABLE READ} query finds that a row is locked exclusively by +another process, it will wait until either the row has been released, or +the lock timeout value has expired. + +@item +@strong{SERIALIZABLE} +@tab Locks the table that contains the rows needed to satisfy the +query. This lock is held until the transaction ends (commits or rolls +back). If a @code{SERIALIZABLE} query finds that a row is exclusively +locked by another process, it will wait until either the row has been +released, or the lock timeout value has expired. @end multitable -If you use @code{--skip-gemini}, @strong{MySQL} will not initialize the -Gemini table handler, saving memory; you cannot use Gemini tables if you -use @code{--skip-gemini}. +The statements that get exclusive locks are @code{INSERT}, +@code{UPDATE}, @code{DELETE} and @code{SELECT ... FOR UPDATE}. Select +statements without the @code{FOR UPDATE} qualifier get shared locks +which allow other not ''for update'' select statements to read the same +rows but block anyone trying to update the row from accessing it. Rows +or tables with exclusive locks block all access to the row from other +transactions until the transaction ends. + +In general terms, the higher the Isolation level the more likelihood of +having concurrent locks and therefore lock conflicts. In such cases, +adjust the @code{-O gemini_lock_table_size} accordingly. + +@cindex GEMINI tables, row-level locking +@noindent +@strong{Row-Level Locking} + +@code{GEMINI} uses row locks, which allows high concurrency for requests +on the same table. + +In order to avoid lock table overflow, SQL statements that require +applying locks to a large number of rows should either be run at the +serializable isolation level or should be covered by a lock table +statement. + +Memory must be pre-allocated for the lock table. The mysqld server +startup option @code{-0 gemini_lock_table_size} can be used to adjust +the number of concurrent locks. + +@cindex GEMINI tables, limitations +@node GEMINI Limitations, , GEMINI Concepts, GEMINI Overview +@subsubsection GEMINI Limitations + +The following limitations are in effect for the current version of +@code{GEMINI}: + +@itemize @bullet +@item +@code{DROP DATABASE} does not work with @code{GEMINI} tables; instead, +drop all the tables in the database first, then drop the database. + +@item +Maximum number of @code{GEMINI} tables is 1012. + +@item +Maximum number of @code{GEMINI} files a server can manage is 1012. Each +table consumes one file; an additional file is consumed if the table has +any indexes defined on it. + +@item +Maximum size of BLOBs is 16MB. + +@item +@code{FULLTEXT} indexes are not supported with @code{GEMINI} tables. + +@item +There is no support for multi-component @code{AUTO_INCREMENT} fields +that provide alternating values at the component level. If you try to +create such a field, @code{GEMINI} will refuse. + +@item +@code{TEMPORARY TABLES} are not supported by @code{GEMINI}. The +statement @code{CREATE TEMPORARY TABLE ... TYPE=GEMINI} will generate +the response: @code{ERROR 1005: Can't create table '/tmp/#sqlxxxxx' +(errno: 0)}. + +@item +@code{FLUSH TABLES} has not been implemented with @code{GEMINI} tables. +@end itemize + +@cindex GEMINI tables, using +@node Using GEMINI Tables, , GEMINI Overview, GEMINI +@subsection Using GEMINI Tables + +This section explains the various startup options you can use with +@code{GEMINI} tables, how to backup @code{GEMINI} tables, some +performance considerations and sample configurations, and a brief +discussion of when to use @code{GEMINI} tables. -@node GEMINI features, GEMINI TODO, GEMINI start, GEMINI -@subsection Features of @code{GEMINI} tables: +Specifically, the topics covered in this section are: @itemize @bullet @item -If a query result can be resolved solely from the index key, Gemini will -not read the actual row stored in the database. +Startup Options @item -Locking on Gemini tables is done at row level. +Creating @code{GEMINI} Tables @item -@code{SELECT COUNT(*) FROM table_name} is fast; Gemini maintains a count -of the number of rows in the table. +Backing Up @code{GEMINI} Tables +@item +Using Auto_Increment Columns With @code{GEMINI} Tables +@item +Performance Considerations +@item +Sample Configurations +@item +When To Use @code{GEMINI} Tables @end itemize -@node GEMINI TODO, , GEMINI features, GEMINI -@subsection Current limitations of @code{GEMINI} tables: +@cindex GEMINI tables, startup options +@menu +* Startup Options:: +* Creating GEMINI Tables:: +* Backing Up GEMINI Tables:: +* Restoring GEMINI Tables:: +* Using Auto_Increment Columns With GEMINI Tables:: +* Performance Considerations:: +* Sample Configurations:: +* When To Use GEMINI Tables:: +@end menu + +@node Startup Options, Creating GEMINI Tables, Using GEMINI Tables, Using GEMINI Tables +@subsubsection Startup Options + +The table below lists options to mysqld that can be used to change the +behavior of @code{GEMINI} tables. + +@multitable @columnfractions .40 .60 +@item @sc{Option} @tab @sc{Description} + +@item +@code{--default-table-type=gemini} +@tab Sets the default table handler to be @code{GEMINI}. All create +table statements will create @code{GEMINI} tables unless otherwise +specified with @code{TYPE=@var{table-type}}. As noted above, there is +currently a limitation with @code{TEMPORARY} tables using @code{GEMINI}. + +@item +@code{--gemini-flush-log-at-commit} +@tab Forces the recovery log buffers to be flushed after every +commit. This can have a serious performance penalty, so use with +caution. + +@item +@code{--gemini-recovery=FULL | NONE | FORCE} +@tab Sets the recovery mode. Default is @code{FULL}. @code{NONE} is +useful for performing repeatable batch operations because the updates +are not recorded in the recovery log. @code{FORCE} skips crash recovery +upon startup; this corrupts the database, and should be used in +emergencies only. + +@item +@code{--gemini-unbuffered-io} +@tab All database writes bypass the OS cache. This can provide a +performance boost on heavily updated systems where most of the dataset +being worked on is cached in memory with the @code{gemini_buffer_cache} +parameter. + +@item +@code{--O gemini_buffer_cache=size} +@tab Amount of memory to allocate for database buffers, including Index +and Record information. It is recommended that this number be 10% of the +total size of all @code{GEMINI} tables. Do not exceed amount of memory +on the system! + +@item +@code{--O gemini_connection_limit=#} +@tab Maximum number of connections to @code{GEMINI}; default is +@code{100}. Each connection consumes about 1K of memory. + +@item +@code{--O gemini_io_threads=#} +@tab Number of background I/O threads; default is @code{2}. Increase the +number when using @code{--gemini-unbuffered-io} + +@item +@code{--O gemini_lock_table_size=#} +@tab Sets the maximum number of concurrent locks; default is 4096. Using +@code{SET [ GLOBAL | SESSION ] TRANSACTION ISOLATION = ...} will +determine how long a program will hold row locks. + +@item +@code{--O gemini_lock_wait_timeout=seconds} +@tab Number of seconds to wait for record locks when performing queries; +default is 10 seconds. Using @code{SET [ GLOBAL | SESSION ] TRANSACTION +ISOLATION = ...} will determine how long a program will hold row locks. + +@item +@code{--skip-gemini} +@tab Do not use @code{GEMINI}. If you use @code{--skip-gemini}, @strong{MySQL} +will not initialize the @code{GEMINI} table handler, saving memory; you +cannot use @code{GEMINI} tables if you use @code{--skip-gemini}. + +@item +@code{--transaction-isolation=READ-UNCOMMITTED | READ-COMMITTED | REPEATABLE-READ | SERIALIZABLE} +@tab Sets the GLOBAL transaction isolation level for all users that +connect to the server; can be overridden with the SET ISOLATION LEVEL +statement. +@end multitable + +@cindex GEMINI tables, creating +@node Creating GEMINI Tables, Backing Up GEMINI Tables, Startup Options, Using GEMINI Tables +@subsubsection Creating GEMINI Tables + +@code{GEMINI} tables can be created by either using the @code{CREATE +TABLE} syntax or the @code{ALTER TABLE} syntax. @itemize @bullet @item -BLOB columns are not supported in @code{GEMINI} tables. +The syntax for creating a @code{GEMINI} table is: + +@example +CREATE TABLE @var{table-name} (....) TYPE=GEMINI; +@end example + @item -The maximum number of concurrent users accessing @code{GEMINI} tables is -limited by @code{gemini_connection_limit}. The default is 100 users. +The syntax to convert a table to @code{GEMINI} is: + +@example +ALTER TABLE @var{table-name} TYPE=GEMINI; +@end example @end itemize -NuSphere is working on removing these limitations. +@xref{Tutorial}, for more information on how to create and use +@code{MySQL} tables. + +@cindex GEMINI tables, backing up +@node Backing Up GEMINI Tables, Restoring GEMINI Tables, Creating GEMINI Tables, Using GEMINI Tables +@subsubsection Backing Up GEMINI Tables + +@code{GEMINI} supports both @code{BACKUP TABLE} and @code{RESTORE TABLE} +syntax. To learn more about how to use @code{BACKUP} and @code{RESTORE}, +see @ref{BACKUP TABLE} and @ref{RESTORE TABLE}. + +To backup @code{GEMINI} tables outside of the @code{MySQL} environment, +you must first shut down the @code{MySQL} server. Once the server is +shut down, you can copy the files associated with @code{GEMINI} to a +different location. The files that make up the @code{GEMINI} table +handler are: + +@itemize @bullet +@item +All files associated with a table with a @code{.gmd} extention below the +@code{$DATADIR} directory. Such files include @code{@var{table}.gmd}, +@code{@var{table}.gmi}, and @code{@var{table}.frm} +@item +@code{gemini.db} in the @code{$DATADIR} directory +@item +@code{gemini.rl} in the @code{$DATADIR} directory +@item +@code{gemini.lg} in the @code{$DATADIR} directory +@end itemize + +All the @code{GEMINI} files must be copied together. You can not copy +just the @code{.gmi} and @code{.gmd} files to a different +@code{$DATADIR} and have them become part of a new database. You can +copy an entire @code{$DATADIR} directory to another location and start a +@strong{MySQL} server using the new @code{$DATADIR}. + +@cindex GEMINI tables, restoring +@node Restoring GEMINI Tables, Using Auto_Increment Columns With GEMINI Tables, Backing Up GEMINI Tables, Using GEMINI Tables +@subsubsection Restoring GEMINI Tables + +To restore @code{GEMINI} tables outside of the @code{MySQL} environment, +you must first shut down the @code{MySQL} server. Once the server is +shut down, you can remove all @code{GEMINI} files in the target +@code{$DATADIR} and then copy the files previously backed up into the +@code{$DATADIR} directory. + +As mentioned above, the files that make up the @code{GEMINI} table +handler are: + +@itemize @bullet +@item +All files associated with a table with a @code{.gmd} extention below the +@code{$DATADIR} directory. Such files include @code{@var{table}.gmd}, +@code{@var{table}.gmi}, and @code{@var{table}.frm} +@item +@code{gemini.db} in the @code{$DATADIR} directory +@item +@code{gemini.rl} in the @code{$DATADIR} directory +@item +@code{gemini.lg} in the @code{$DATADIR} directory +@end itemize + +When restoring a table, all the @code{GEMINI} files must be copied +together. You can not restore just the @code{.gmi} and @code{.gmd} +files. + +@cindex GEMINI tables, auto_increment +@node Using Auto_Increment Columns With GEMINI Tables, Performance Considerations, Restoring GEMINI Tables, Using GEMINI Tables +@subsubsection Using Auto_Increment Columns With GEMINI Tables + +As mentioned previously, @code{GEMINI} tables support row-level and +table-level locking to increase concurrency in applications and to allow +reading of tables without locking for maximum concurrency in heavy +update environments. This feature has several implications when working +with @code{auto_increment} tables. + +In @code{MySQL}, when a column is defined as an @code{auto_increment} +column, and a row is inserted into the table with a @code{NULL} for the +column, the @code{auto_increment} column is updated to be 1 higher than +the highest value in the column. + +With @code{MyISAM} tables, the @code{auto_increment} function is +implemented by looking in the index and finding the highest value and +adding 1 to it. This is possible because the entire @code{ISAM} table is +locked during the update period and the increment value is therefore +guaranteed to not be changing. + +With @code{GEMINI} tables, the @code{auto_increment} function is +implemented by maintaining a counter in a separate location from the +table data. Instead of looking at the highest value in the table index, +@code{GEMINI} tables look at this separately maintained counter. This +means that in a transactional model, unlike the bottleneck inherent in +the @code{MyISAM} approach, @code{GEMINI} users do @b{not} have to wait +until the transaction that added the last value either commits or +rollbacks before looking at the value. + +Two side-effects of the @code{GEMINI} implementation are: + +@itemize @bullet +@item +If an insert is done where the column with the @code{auto_increment} is +specified, and this specified value is the highest value, @code{MyISAM} +uses it as its @code{auto_increment} value, and every subsequent insert +is based on this. By contrast, @code{GEMINI} does not use this value, +but instead uses the value maintained in the separate @code{GEMINI} +counter location. + +@item +To set the counter to a specific value, you can use @code{SET +insert_id=#} and insert a new row in the table. However, as a general +rule, values should not be inserted into an @code{auto_increment} +column; the database manager should be maintaining this field, not the +application. @code{SET insert_id} is a recovery mechanism that should be +used in case of error only. +@end itemize + +Note that if you delete the row containing the maximum value for an +@code{auto_increment} column, the value will be reused with a +@code{GEMINI} table but not with a @code{MyISAM} table. + +See @ref{CREATE TABLE} for more information about creating +@code{auto_increment} columns. + +@cindex GEMINI tables, peformance considerations +@node Performance Considerations, Sample Configurations, Using Auto_Increment Columns With GEMINI Tables, Using GEMINI Tables +@subsubsection Performance Considerations + +In addition to designing the best possible application, configuration of +the data and the server startup parameters need to be considered. How +the hardware is being used can have a dramatic affect on how fast the +system will respond to queries. Disk Drives and Memory must both be +considered. + +@noindent +@strong{Disk Drives} + +For best performance, you want to spread the data out over as many disks +as possible. Using RAID 10 stripes work very well. If there are a lot of +updates then the recovery log (@code{gemini.rl}) should be on a +relatively quiet disk drive. + +To spread the data out without using RAID 10, you can do the following: + +@itemize @bullet +@item +Group all the tables into three categories: Heavy Use, Moderate Use, +Light Use. + +@item +Take the number of disk drives available and use a round-robin approach +to the three categories grouping the tables on a disk drive. The result +will be an equal distribution of Heavy/Moderate/Light tables assigned to +each disk drive. + +@item +Once the tables have been converted to @code{GEMINI} by using the +@code{ALTER TABLE <name> TYPE=GEMINI} statements, move (@code{mv}) the +@code{.gmd} and @code{.gmi} files to a different disk drive and link +(@code{ln -s}) them back to the original directory where the @code{.frm} +file resides. + +@item +Finally, move the @code{gemini.rl} file to its quiet disk location and link +the file back to the @code{$DATADIR} directory. +@end itemize + +@noindent +@strong{Memory} + +The more data that can be placed in memory the faster the access to the +data. Figure out how large the @code{GEMINI} data is by adding up the +@code{.gmd} and @code{.gmi} file sizes. If you can, put at least 10% of +the data into memory. You allocate memory for the rows and indexes by +using the @code{gemini_buffer_cache} startup parameter. For example: + +@example +mysqld -O gemini_buffer_cache=800M +@end example + +@noindent +would allocate 800 MB of memory for the @code{GEMINI} buffer cache. + +@cindex GEMINI tables, sample configurations +@node Sample Configurations, When To Use GEMINI Tables, Performance Considerations, Using GEMINI Tables +@subsubsection Sample Configurations + +Based on the performance considerations above, we can look at some +examples for how to get the best performance out of the system when +using @code{GEMINI} tables. + +@multitable @columnfractions .30 .70 +@item @sc{Hardware} @tab @sc{Configuration} +@item +One CPU, 128MB memory, one disk drive +@tab Allocate 80MB of memory for reading and updating @code{GEMINI} +tables by starting the mysqld server with the following option: + +@example +-O gemini_buffer_cache=80M +@end example + +@item +Two CPUs, 512MB memory, four disk drives +@tab Use RAID 10 to stripe the data across all available disks, or use +the method described in the performance considerations section, +above. Allocate 450MB of memory for reading/updating @code{GEMINI} +tables: + +@example +-O gemini_buffer_cache=450M +@end example +@end multitable + +@cindex GEMINI tables, when to use +@node When To Use GEMINI Tables, , Sample Configurations, Using GEMINI Tables +@subsubsection When To Use GEMINI Tables + +Because the @code{GEMINI} table handler provides crash recovery and +transaction support, there is extra overhead that is not found in other +non-transaction safe table handlers. Here are some general guidelines +for when to employ @code{GEMINI} and when to use other non-transaction +safe tables (@code{NTST}). + +@multitable @columnfractions .30 .25 .45 +@item +@sc{Access Trends} @tab @sc{Table Type} @tab @sc{Reason} +@item +Read-only +@tab @code{NTST} +@tab Less overhead and faster +@item +Critical data +@tab @code{GEMINI} +@tab Crash recovery protection +@item +High concurrency +@tab @code{GEMINI} +@tab Row-level locking +@item +Heavy update +@tab @code{GEMINI} +@tab Row-level locking +@end multitable + +The table below shows how a typical application schema could be defined. + +@multitable @columnfractions .15 .30 .25 .30 +@item +@sc{Table} @tab @sc{Contents} @tab @sc{Table Type} @tab @sc{Reason} +@item +account +@tab Customer account data +@tab @code{GEMINI} +@tab Critical data, heavy update +@item +order +@tab Orders for a customer +@tab @code{GEMINI} +@tab Critical data, heavy update +@item +orderline +@tab Orderline detail for an order +@tab @code{GEMINI} +@tab Critical data, heavy update +@item +invdesc +@tab Inventory description +@tab @code{NTST} +@tab Read-only, frequent access +@item +salesrep +@tab Sales rep information +@tab @code{NTST} +@tab Infrequent update +@item +inventory +@tab Inventory information +@tab @code{GEMINI} +@tab High concurrency, critical data +@item +config +@tab System configuration +@tab @code{NTST} +@tab Read-only +@end multitable @node InnoDB, , GEMINI, Table types @section InnoDB Tables @@ -24918,6 +25679,17 @@ in its own lock table and rolls back the transaction. If you use than InnoDB in the same transaction, then a deadlock may arise which InnoDB cannot notice. In cases like this the timeout is useful to resolve the situation. +@item @code{innodb_unix_file_flush_method} @tab +(Available from 3.23.39 up.) +The default value for this is @code{fdatasync}. +Another option is @code{O_DSYNC}. +Options @code{littlesync} and @code{nosync} have the +risk that in an operating system crash or a power outage you may easily +end up with a half-written database page, and you have to do a recovery +from a backup. See the section "InnoDB performance tuning", item 6, below +for tips on how to set this parameter. If you are happy with your database +performance it is wisest not to specify this parameter at all, in which +case it will get the default value. @end multitable @node InnoDB init, Using InnoDB tables, InnoDB start, InnoDB @@ -24979,7 +25751,7 @@ mysqld: ready for connections @end example @menu -* Error creating InnoDB:: +* Error creating InnoDB:: @end menu @node Error creating InnoDB, , InnoDB init, InnoDB init @@ -25035,6 +25807,46 @@ InnoDB has its own internal data dictionary, and you will get problems if the @strong{MySQL} @file{.frm} files are out of 'sync' with the InnoDB internal data dictionary. +@subsubsection Converting MyISAM tables to InnoDB + +InnoDB does not have a special optimization for separate index creation. +Therefore it does not pay to export and import the table and create indexes +afterwards. +The fastest way to alter a table to InnoDB is to do the inserts +directly to an InnoDB table, that is, use @code{ALTER TABLE ... TYPE=INNODB}, +or create an empty InnoDB table with identical definitions and insert +the rows with @code{INSERT INTO ... SELECT * FROM ...}. + +To get better control over the insertion process, it may be good to insert +big tables in pieces: + +@example +INSERT INTO newtable SELECT * FROM oldtable WHERE yourkey > something + AND yourkey <= somethingelse; +@end example + +After all data has been inserted you can rename the tables. + +During the conversion of big tables you should set the InnoDB +buffer pool size big +to reduce disk i/o. Not bigger than 80 % of the physical memory, though. +You should set InnoDB log files big, and also the log buffer large. + +Make sure you do not run out of tablespace: InnoDB tables take a lot +more space than MyISAM tables. If an @code{ALTER TABLE} runs out +of space, it will start a rollback, and that can take hours if it is +disk-bound. +In inserts InnoDB uses the insert buffer to merge secondary index records +to indexes in batches. That saves a lot of disk i/o. In rollback no such +mechanism is used, and the rollback can take 30 times longer than the +insertion. + +In the case of a runaway rollback, if you do not have valuable data in your +database, +it is better that you kill the database process and delete all InnoDB data +and log files and all InnoDB table @file{.frm} files, and start +your job again, rather than wait for millions of disk i/os to complete. + @node Adding and removing, Backing up, Using InnoDB tables, InnoDB @subsection Adding and removing InnoDB data and log files @@ -25435,6 +26247,103 @@ set by the SQL statement may be preserved. This is because InnoDB stores row locks in a format where it cannot afterwards know which was set by which SQL statement. +@subsection Performance tuning tips + +@strong{1.} +If the Unix @file{top} or the Windows @file{Task Manager} shows that +the CPU usage percentage with your workload is less than 70 %, +your workload is probably +disk-bound. Maybe you are making too many transaction commits, or the +buffer pool is too small. +Making the buffer pool bigger can help, but do not set +it bigger than 80 % of physical memory. + +@strong{2.} +Wrap several modifications into one transaction. InnoDB must +flush the log to disk at each transaction commit, if that transaction +made modifications to the database. Since the rotation speed of a disk +is typically +at most 167 revolutions/second, that constrains the number of commits +to the same 167/second if the disk does not fool the operating system. + +@strong{3.} +If you can afford the loss of some latest committed transactions, you can +set the @file{my.cnf} parameter @code{innodb_flush_log_at_trx_commit} +to zero. InnoDB tries to flush the log anyway once in a second, +though the flush is not guaranteed. + +@strong{4.} +Make your log files big, even as big as the buffer pool. When InnoDB +has written the log files full, it has to write the modified contents +of the buffer pool to disk in a checkpoint. Small log files will cause many +unnecessary disk writes. The drawback in big log files is that recovery +time will be longer. + +@strong{5.} +Also the log buffer should be quite big, say 8 MB. + +@strong{6.} (Relevant from 3.23.39 up.) +In some versions of Linux and other Unixes flushing files to disk with the Unix +@code{fdatasync} and other similar methods is surprisingly slow. +The default method InnoDB uses is the @code{fdatasync} function. +If you are not satisfied with the database write performance, you may +try setting @code{innodb_unix_file_flush_method} in @file{my.cnf} +to @code{O_DSYNC}, though O_DSYNC seems to be slower on most systems. +You can also try setting it to @code{littlesync}, which means that +InnoDB does not call the file flush for every write it does to a +file, but only +in log flush at transaction commits and data file flush at a checkpoint. +The drawback in @code{littlesync} is that if the operating system +crashes, you can easily end up with a half-written database page, +and you have to +do a recovery from a backup. With @code{nosync} you have even less safety: +InnoDB will only flush the database files to disk at database shutdown + +@strong{7.} In importing data to InnoDB, make sure that MySQL does not have +@code{autocommit=1} on. Then every insert requires a log flush to disk. +Put before your plain SQL import file line + +@example +set autocommit=0; +@end example + +and after it + +@example +commit; +@end example + +If you use the @file{mysqldump} option @code{--opt}, you will get dump +files which are fast to import also to an InnoDB table, even without wrapping +them to the above @code{set autocommit=0; ... commit;} wrappers. + +@strong{8.} +Beware of big rollbacks of mass inserts: InnoDB uses the insert buffer +to save disk i/o in inserts, but in a corresponding rollback no such +mechanism is used. A disk-bound rollback can take 30 times the time +of the corresponding insert. Killing the database process will not +help because the rollback will start again at the database startup. The +only way to get rid of a runaway rollback is to increase the buffer pool +so that the rollback becomes CPU-bound and runs fast, or delete the whole +InnoDB database. + +@strong{9.} +Beware also of other big disk-bound operations. +Use @code{DROP TABLE} +or @code{TRUNCATE} (from MySQL-4.0 up) to empty a table, not +@code{DELETE FROM yourtable}. + +@strong{10.} +Use the multi-line @code{INSERT} to reduce +communication overhead between the client and the server if you need +to insert many rows: + +@example +INSERT INTO yourtable VALUES (1, 2), (5, 5); +@end example + +This tip is of course valid for inserts into any table type, not just InnoDB. + @node Implementation, Table and index, InnoDB transaction model, InnoDB @subsection Implementation of multiversioning @@ -25787,6 +26696,11 @@ they roll back the corresponding SQL statement. @subsection Some restrictions on InnoDB tables @itemize @bullet + +@item @code{SHOW TABLE STATUS} does not give accurate statistics +on InnoDB tables, except for the physical size reserved by the table. +The row count is only a rough estimate used in SQL optimization. + @item If you try to create an unique index on a prefix of a column you will get an error: @@ -25835,7 +26749,7 @@ files your operating system supports. Support for > 4 GB files will be added to InnoDB in a future version. @item The maximum tablespace size is 4 billion database pages. This is also -the maximum size for a table. +the maximum size for a table. The minimum tablespace size is 10 MB. @end itemize @node InnoDB contact information, , InnoDB restrictions, InnoDB @@ -33764,7 +34678,8 @@ to start using the new table. @cindex maintaining, tables @cindex tables, maintaining @cindex databases, maintaining -@cindex @code{mysiamchk} +@cindex @code{myisamchk} +@cindex @code{mysqlcheck} @cindex crash, recovery @cindex recovery, from crash @node Maintenance, Adding functions, Tools, Top @@ -33772,6 +34687,7 @@ to start using the new table. @menu * Table maintenance:: Table maintenance and crash recovery +* Using mysqlcheck:: Using mysqlcheck for maintenance and recovery * Maintenance regimen:: Setting up a table maintenance regimen * Table-info:: Getting information about a table * Crash recovery:: Using @code{myisamchk} for crash recovery @@ -33782,7 +34698,7 @@ This chapter covers what you should know about maintaining a @strong{MySQL} distribution. You will learn how to care for your tables on a regular basis, and what to do when disaster strikes. -@node Table maintenance, Maintenance regimen, Maintenance, Maintenance +@node Table maintenance, Using mysqlcheck, Maintenance, Maintenance @section Using @code{myisamchk} for Table Maintenance and Crash Recovery Starting with @strong{MySQL} Version 3.23.13, you can check MyISAM @@ -34164,9 +35080,132 @@ This space is allocated on the temporary disk (specified by @code{TMPDIR} or If you have a problem with disk space during repair, you can try to use @code{--safe-recover} instead of @code{--recover}. +@node Using mysqlcheck, Maintenance regimen, Table maintenance, Maintenance +@section Using @code{mysqlcheck} for Table Maintenance and Crash Recovery + +Since @strong{MySQL} version 3.23.38 you will be able to use a new +checking and repairing tool for @code{MyISAM} tables. The difference to +@code{myisamchk} is that @code{mysqlcheck} should be used when the +@code{mysqld} server is running, where as @code{myisamchk} should be used +when it is not. The benefit is that you no longer have to take the +server down for checking or repairing your tables. + +@code{mysqlcheck} uses @strong{MySQL} server commands @code{CHECK}, +@code{REPAIR}, @code{ANALYZE} and @code{OPTIMIZE} in a convenient way +for the user. + +There are three alternative ways to invoke @code{mysqlcheck}: + +@example +shell> mysqlcheck [OPTIONS] database [tables] +shell> mysqlcheck [OPTIONS] --databases DB1 [DB2 DB3...] +shell> mysqlcheck [OPTIONS] --all-databases +@end example + +So it can be used in a similar way as @code{mysqldump} when it +comes to what databases and tables you want to choose. + +@code{mysqlcheck} does have a special feature compared to the other +clients; the default behavior, checking tables (-c), can be changed by +renaming the binary. So if you want to have a tool that repairs tables +by default, you should just copy @code{mysqlcheck} to your harddrive +with a new name, @code{mysqlrepair}, or alternatively make a symbolic +link to @code{mysqlrepair} and name the symbolic link as +@code{mysqlrepair}. If you invoke @code{mysqlrepair} now, it will repair +tables by default. + +The names that you can use to change @code{mysqlcheck} default behavior +are here: + +@example +mysqlrepair: The default option will be -r +mysqlanalyze: The default option will be -a +mysqloptimize: The default option will be -o +@end example + +The options available for @code{mysqlcheck} are listed here, please +check what your version supports with @code{mysqlcheck --help}. + +@table @code +@item -A, --all-databases +Check all the databases. This will be same as --databases with all +databases selected +@item -1, --all-in-1 +Instead of making one query for each table, execute all queries in 1 +query separately for each database. Table names will be in a comma +separated list. +@item -a, --analyze +Analyze given tables. +@item --auto-repair +If a checked table is corrupted, automatically fix it. Repairing will be +done after all tables have been checked, if corrupted ones were found. +@item -#, --debug=... +Output debug log. Often this is 'd:t:o,filename' +@item --character-sets-dir=... +Directory where character sets are +@item -c, --check +Check table for errors +@item -C, --check-only-changed +Check only tables that have changed since last check or haven't been +closed properly. +@item --compress +Use compression in server/client protocol. +@item -?, --help +Display this help message and exit. +@item -B, --databases +To check several databases. Note the difference in usage; In this case +no tables are given. All name arguments are regarded as database names. +@item --default-character-set=... +Set the default character set +@item -F, --fast +Check only tables that hasn't been closed properly +@item -f, --force +Continue even if we get an sql-error. +@item -e, --extended +If you are using this option with CHECK TABLE, it will ensure that the +table is 100 percent consistent, but will take a long time. + +If you are using this option with REPAIR TABLE, it will run an extended +repair on the table, which may not only take a long time to execute, but +may produce a lot of garbage rows also! +@item -h, --host=... +Connect to host. +@item -m, --medium-check +Faster than extended-check, but only finds 99.99 percent of all +errors. Should be good enough for most cases. +@item -o, --optimize +Optimize table +@item -p, --password[=...] +Password to use when connecting to server. If password is not given +it's solicited on the tty. +@item -P, --port=... +Port number to use for connection. +@item -q, --quick +If you are using this option with CHECK TABLE, it prevents the check +from scanning the rows to check for wrong links. This is the fastest +check. + +If you are using this option with REPAIR TABLE, it will try to repair +only the index tree. This is the fastest repair method for a table. +@item -r, --repair +Can fix almost anything except unique keys that aren't unique. +@item -s, --silent +Print only error messages. +@item -S, --socket=... +Socket file to use for connection. +@item --tables +Overrides option --databases (-B). +@item -u, --user=# +User for login if not current user. +@item -v, --verbose +Print info about the various stages. +@item -V, --version +Output version information and exit. +@end table + @cindex maintaining, tables @cindex tables, maintenance regimen -@node Maintenance regimen, Table-info, Table maintenance, Maintenance +@node Maintenance regimen, Table-info, Using mysqlcheck, Maintenance @section Setting Up a Table Maintenance Regimen Starting with @strong{MySQL} Version 3.23.13, you can check MyISAM @@ -42648,7 +43687,7 @@ attachments, you should ftp all the relevant files to: @end itemize @node Reporting mysqltest bugs, , extending mysqltest, MySQL test suite -@subsection Extending the MySQL Test Suite +@subsection Reporting bugs in the MySQL Test Suite If your @strong{MySQL} version doesn't pass the test suite you should do the following: @@ -42680,6 +43719,10 @@ description of your system, the version of the mysqld binary and how you compiled it. @item +Try also to run @code{mysql-test-run} with the @code{--force} option to +see if there is any other test that fails. + +@item If you have compiled @strong{MySQL} yourself, check our manual for how to compile @strong{MySQL} on your platform or, preferable, use one of the binaries we have compiled for you at @@ -42919,6 +43962,8 @@ An online magazine featuring music, literature, arts, and design content. @item @uref{http://kids.msfc.nasa.gov, NASA KIDS} @item @uref{http://science.nasa.gov, Sience@@NASA} +@item @uref{http://www.handy.de/, handy.de} + @item @uref{http://lindev.jmc.tju.edu/qwor, Qt Widget and Object Repository} @item @uref{http://www.samba-choro.com.br, Brazilian samba site (in Portuguese)} @@ -42933,6 +43978,9 @@ tickets for this event is implemented using @strong{MySQL} and tcl/tk. More than service with millions of users.} @item @uref{http://f1.tauzero.se, Forza Motorsport} + +@item @uref{http://www.dreamhost.com/, DreamHost Web Hosting} + @end itemize @cindex services @@ -43270,16 +44318,16 @@ interface, you should fetch the @code{Data-Dumper}, @code{DBI}, and Perl @code{Data-Dumper} module. Useful with @code{DBI}/@code{DBD} support for older Perl installations. -@item @uref{http://www.mysql.com/Downloads/Contrib/DBI-1.14.tar.gz, DBI-1.14.tar.gz} +@item @uref{http://www.mysql.com/Downloads/Contrib/DBI-1.15.tar.gz, DBI-1.15.tar.gz} Perl @code{DBI} module. -@item @uref{http://www.mysql.com/Downloads/Contrib/KAMXbase1.0.tar.gz,KAMXbase1.0.tar.gz} +@item @uref{http://www.mysql.com/Downloads/Contrib/KAMXbase1.2.tar.gz,KAMXbase1.2.tar.gz} Convert between @file{.dbf} files and @strong{MySQL} tables. Perl module written by Pratap Pereira @email{pereira@@ee.eng.ohio-state.edu}, extended by Kevin A. McGrail @email{kmcgrail@@digital1.peregrinehw.com}. This converter can handle MEMO fields. -@item @uref{http://www.mysql.com/Downloads/Contrib/Msql-Mysql-modules-1.2215.tar.gz, Msql-Mysql-modules-1.2215.tar.gz} +@item @uref{http://www.mysql.com/Downloads/Contrib/Msql-Mysql-modules-1.2216.tar.gz, Msql-Mysql-modules-1.2216.tar.gz} Perl @code{DBD} module to access mSQL and @strong{MySQL} databases. @item @uref{http://www.mysql.com/Downloads/Contrib/Data-ShowTable-3.3.tar.gz, Data-ShowTable-3.3.tar.gz} @@ -43569,6 +44617,11 @@ Some features: @itemize @bullet @item Manage servers, databases, tables, columns, indexes, and users @item Import wizard to import structure and data from MS Access, MS Excel, Dbase, FoxPro, Paradox, and ODBC Databases. + +@item @uref{http://www.mysql.com/Downloads/Contrib/KMYENG113.zip,KMYENG113.zip} +An administrator GUI for @strong{MySQL}. Works only on windows, no source. +Available in English and Japanese. By Mitunobu Kaneko. +Home page: @uref{http://sql.jnts.ne.jp/} @end itemize @item @uref{http://www.mysql.com/Downloads/Contrib/xmysqladmin-1.0.tar.gz, xmysqladmin-1.0.tar.gz} @@ -43839,7 +44892,7 @@ detection of @code{TIMESTAMP} fields), provides warnings and suggestions while converting, quotes @strong{all} special characters in text and binary data, and so on. It will also convert to @code{mSQL} v1 and v2, and is free of charge for anyone. See -@uref{http://www.cynergi.net/prod/exportsql/} for the latest version. By +@uref{http://www.cynergi.net/exportsql/} for the latest version. By Pedro Freire, @email{support@@cynergi.net}. NOTE: Doesn't work with Access2! @item @uref{http://www.mysql.com/Downloads/Contrib/access_to_mysql.txt, access_to_mysql.txt} @@ -43876,6 +44929,11 @@ By Steve Shreeve. Perl program to convert Oracle databases to @strong{MySQL}. By Johan Andersson. @item @uref{http://www.mysql.com/Downloads/Contrib/excel2mysql, excel2mysql} Perl program to import Excel spreadsheets into a @strong{MySQL} database. By Stephen Hurd @email{shurd@@sk.sympatico.ca} + +@item @uref{http://www.mysql.com/Downloads/Contrib/T2S_100.ZIP, T2S_100.ZIP}. +Windows program to convert text files to @strong{MySQL} databases. By +Asaf Azulay. + @end itemize @appendixsec Using MySQL with Other Products @@ -44538,6 +45596,8 @@ Added @code{ORDER BY} syntax to @code{UPDATE} and @code{DELETE}. @item Optimized queries of type: @code{SELECT DISTINCT * from table_name ORDER by key_part1 LIMIT #} +@item +Added support for sym-linking of MyISAM tables. @end itemize @node News-3.23.x, News-3.22.x, News-4.0.x, News @@ -44631,6 +45691,11 @@ not yet 100% confident in this code. @appendixsubsec Changes in release 3.23.39 @itemize @bullet @item +We are now using the @code{-lcma} thread library on HPUX 10.20 to +get @strong{MySQL} more stabile on HPUX. +@item +Fixed problem with @code{IF()} and number of decimals in the result. +@item Fixed that date-part extract functions works with dates where day and/or month is 0. @item @@ -45861,7 +46926,7 @@ Added table locks to Berkeley DB. Fixed a bug with @code{LEFT JOIN} and @code{ORDER BY} where the first table had only one matching row. @item -Added 4 sample @code{my.cfg} example files in the @file{support-files} +Added 4 sample @code{my.cnf} example files in the @file{support-files} directory. @item Fixed @code{duplicated key} problem when doing big @code{GROUP BY}'s. @@ -49964,7 +51029,7 @@ of @code{analyze} is run on all sub tables. @end itemize @node TODO future, TODO sometime, TODO MySQL 4.0, TODO -@appendixsec Things that must done in the real near future +@appendixsec Things that must be done in the real near future @itemize @bullet @item diff --git a/acinclude.m4 b/acinclude.m4 index 81fedcc5827..d2c74d1c0a4 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -1097,10 +1097,10 @@ dnl echo "DBG_GEM1: gemini='$gemini'" gemini_includes= gemini_libs= case "$gemini" in - no | default | *) + no) AC_MSG_RESULT([Not using Gemini DB]) ;; - yes ) + yes | default | *) have_gemini_db="yes" gemini_includes="-I../gemini/incl -I../gemini" gemini_libs="\ diff --git a/client/mysqladmin.c b/client/mysqladmin.c index 1e6bf3c5219..3570cefc4ae 100644 --- a/client/mysqladmin.c +++ b/client/mysqladmin.c @@ -28,7 +28,7 @@ #include <my_pthread.h> /* because of signal() */ #endif -#define ADMIN_VERSION "8.20" +#define ADMIN_VERSION "8.21" #define MAX_MYSQL_VAR 64 #define SHUTDOWN_DEF_TIMEOUT 3600 /* Wait for shutdown */ #define MAX_TRUNC_LENGTH 3 @@ -870,7 +870,7 @@ static int drop_db(MYSQL *mysql, const char *db) return -1; } } - sprintf(name_buff,"drop database %.*s",FN_REFLEN,db); + sprintf(name_buff,"drop database `%.*s`",FN_REFLEN,db); if (mysql_query(mysql,name_buff)) { my_printf_error(0,"DROP DATABASE %s failed;\nerror: '%s'",MYF(ME_BELL), diff --git a/client/mysqlcheck.c b/client/mysqlcheck.c index 3d4d4597ef5..a9379837847 100644 --- a/client/mysqlcheck.c +++ b/client/mysqlcheck.c @@ -338,7 +338,7 @@ static int get_options(int *argc, char ***argv) { int pnlen = strlen(my_progname); - if (pnlen < 6) // name too short + if (pnlen < 6) /* name too short */ what_to_do = DO_CHECK; else if (!strcmp("repair", my_progname + pnlen - 6)) what_to_do = DO_REPAIR; diff --git a/configure.in b/configure.in index cbc6fe85619..26b74804d43 100644 --- a/configure.in +++ b/configure.in @@ -285,8 +285,10 @@ export CC CFLAGS LD LDFLAGS if test "$GXX" = "yes" then - # mysqld requires this when compiled with gcc - CXXFLAGS="$CXXFLAGS -fno-implicit-templates" + # mysqld requires -fno-implicit-templates. + # Disable exceptions as they seams to create problems with gcc and threads. + # mysqld doesn't use run-time-type-checking, so we disable it. + CXXFLAGS="$CXXFLAGS -fno-implicit-templates -fno-exceptions -fno-rtti" fi # Avoid bug in fcntl on some versions of linux @@ -786,6 +788,11 @@ case $SYSTEM_TYPE in echo "Enabling snprintf workaround for hpux 10.20" CFLAGS="$CFLAGS -DHAVE_BROKEN_SNPRINTF -DSIGNALS_DONT_BREAK_READ" CXXFLAGS="$CXXFLAGS -DHAVE_BROKEN_SNPRINTF -D_INCLUDE_LONGLONG -DSIGNALS_DONT_BREAK_READ" + if test "$with_named_thread" = "no" + then + echo "Using --with-named-thread=-lpthread" + with_named_thread="-lcma" + fi ;; *hpux11.*) echo "Enabling pread/pwrite workaround for hpux 11" @@ -1082,7 +1089,7 @@ fi AC_MSG_CHECKING("named thread libs:") if test "$with_named_thread" != "no" then - LIBS="$LIBS $with_named_thread" + LIBS="$with_named_thread $LIBS $with_named_thread" with_posix_threads="yes" with_mit_threads="no" AC_MSG_RESULT("$with_named_thread") @@ -2074,6 +2081,17 @@ EOF echo "END OF INNODB CONFIGURATION" fi + if test "X$have_gemini_db" = "Xyes"; then + sql_server_dirs="gemini $sql_server_dirs" + echo "CONFIGURING FOR GEMINI DB" + (cd gemini && sh ./configure) \ + || AC_MSG_ERROR([could not configure Gemini DB]) + + echo "END OF GEMINI DB CONFIGURATION" + + AC_DEFINE(HAVE_GEMINI_DB) + fi + if test "$with_posix_threads" = "no" -o "$with_mit_threads" = "yes" then # MIT user level threads diff --git a/include/my_base.h b/include/my_base.h index aee9f7af3f1..bb2e4128195 100644 --- a/include/my_base.h +++ b/include/my_base.h @@ -213,6 +213,7 @@ enum ha_base_keytype { #define HA_ERR_CRASHED_ON_USAGE 145 /* Table must be repaired */ #define HA_ERR_LOCK_WAIT_TIMEOUT 146 #define HA_ERR_LOCK_TABLE_FULL 147 +#define HA_ERR_READ_ONLY_TRANSACTION 148 /* Updates not allowed */ /* Other constants */ diff --git a/include/my_sys.h b/include/my_sys.h index b5d59d2e801..dc4eff7bda5 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -62,6 +62,8 @@ extern int NEAR my_errno; /* Last error in mysys */ #define MY_DONT_CHECK_FILESIZE 128 /* Option to init_io_cache() */ #define MY_LINK_WARNING 32 /* my_redel() gives warning if links */ #define MY_COPYTIME 64 /* my_redel() copys time */ +#define MY_DELETE_OLD 256 /* my_create_with_symlink() */ +#define MY_RESOLVE_LINK 128 /* my_realpath(); Only resolve links */ #define MY_HOLD_ORIGINAL_MODES 128 /* my_copy() holds to file modes */ #define MY_REDEL_MAKE_BACKUP 256 #define MY_SEEK_NOT_DONE 32 /* my_lock may have to do a seek */ @@ -382,6 +384,12 @@ extern File my_create(const char *FileName,int CreateFlags, int AccsesFlags, myf MyFlags); extern int my_close(File Filedes,myf MyFlags); extern int my_mkdir(const char *dir, int Flags, myf MyFlags); +extern int my_readlink(char *to, const char *filename, myf MyFlags); +extern int my_realpath(char *to, const char *filename, myf MyFlags); +extern File my_create_with_symlink(const char *linkname, const char *filename, + int createflags, int access_flags, + myf MyFlags); +extern int my_symlink(const char *content, const char *linkname, myf MyFlags); extern uint my_read(File Filedes,byte *Buffer,uint Count,myf MyFlags); extern uint my_pread(File Filedes,byte *Buffer,uint Count,my_off_t offset, myf MyFlags); @@ -432,8 +440,14 @@ extern int my_redel(const char *from, const char *to, int MyFlags); extern int my_copystat(const char *from, const char *to, int MyFlags); extern my_string my_filename(File fd); +#ifndef THREAD extern void dont_break(void); extern void allow_break(void); +#else +#define dont_break() +#define allow_break() +#endif + extern void my_remember_signal(int signal_number,sig_handler (*func)(int)); extern void caseup(my_string str,uint length); extern void casedn(my_string str,uint length); diff --git a/include/mysqld_error.h b/include/mysqld_error.h index 2a9b7514f82..5c3d47c2994 100644 --- a/include/mysqld_error.h +++ b/include/mysqld_error.h @@ -205,6 +205,9 @@ #define ER_SLAVE_THREAD 1202 #define ER_TOO_MANY_USER_CONNECTIONS 1203 #define ER_SET_CONSTANTS_ONLY 1204 -#define ER_CONNECT_TO_MASTER 1205 -#define ER_QUERY_ON_MASTER 1206 -#define ER_ERROR_MESSAGES 207 +#define ER_LOCK_WAIT_TIMEOUT 1205 +#define ER_LOCK_TABLE_FULL 1206 +#define ER_READ_ONLY_TRANSACTION 1207 +#define ER_CONNECT_TO_MASTER 1208 +#define ER_QUERY_ON_MASTER 1209 +#define ER_ERROR_MESSAGES 210 diff --git a/include/mysys_err.h b/include/mysys_err.h index b379f5bcbc9..2d23ead36b6 100644 --- a/include/mysys_err.h +++ b/include/mysys_err.h @@ -22,7 +22,7 @@ extern "C" { #endif #define GLOB 0 /* Error maps */ -#define GLOBERRS 24 /* Max number of error messages in map's */ +#define GLOBERRS 27 /* Max number of error messages in map's */ #define EE(X) globerrs[ X ] /* Defines to add error to right map */ extern const char * NEAR globerrs[]; /* my_error_messages is here */ @@ -51,6 +51,9 @@ extern const char * NEAR globerrs[]; /* my_error_messages is here */ #define EE_CANT_MKDIR 21 #define EE_UNKNOWN_CHARSET 22 #define EE_OUT_OF_FILERESOURCES 23 +#define EE_CANT_READLINK 24 +#define EE_CANT_SYMLINK 25 +#define EE_REALPATH 26 #ifdef __cplusplus } diff --git a/innobase/configure.in b/innobase/configure.in index 83d302c6dc4..48bb9504219 100644 --- a/innobase/configure.in +++ b/innobase/configure.in @@ -38,6 +38,7 @@ AC_CHECK_HEADERS(aio.h sched.h) AC_CHECK_SIZEOF(int, 4) AC_CHECK_FUNCS(sched_yield) AC_CHECK_FUNCS(fdatasync) +AC_CHECK_FUNCS(localtime_r) #AC_C_INLINE Already checked in MySQL AC_C_BIGENDIAN @@ -95,6 +96,11 @@ case "$target_os" in CFLAGS="$CFLAGS -DUNIV_MUST_NOT_INLINE";; esac +case "$target" in + i[[4567]]86-*-*) + CFLAGS="$CFLAGS -DUNIV_INTEL_X86";; +esac + AC_OUTPUT(Makefile os/Makefile ut/Makefile btr/Makefile buf/Makefile com/Makefile data/Makefile dict/Makefile dyn/Makefile diff --git a/innobase/include/sync0sync.ic b/innobase/include/sync0sync.ic index a937ac5d579..5a872c6b093 100644 --- a/innobase/include/sync0sync.ic +++ b/innobase/include/sync0sync.ic @@ -86,6 +86,20 @@ mutex_test_and_set( /* mutex_fence(); */ return(res); +#elif defined(__GNUC__) && defined(UNIV_INTEL_X86) + ulint* lw; + ulint res; + + lw = &(mutex->lock_word); + + /* In assembly we use the so-called AT & T syntax where + the order of operands is inverted compared to the ordinary Intel + syntax. The 'l' after the mnemonics denotes a 32-bit operation. */ + + asm volatile("movl $1, %%eax; xchgl (%%ecx), %%eax" : + "=eax" (res): + "ecx" (lw)); + return(res); #else ibool ret; @@ -120,9 +134,10 @@ mutex_reset_lock_word( __asm XCHG EDX, DWORD PTR [ECX] #else mutex->lock_word = 0; - +#if !(defined(__GNUC__) && defined(UNIV_INTEL_X86)) os_fast_mutex_unlock(&(mutex->os_fast_mutex)); #endif +#endif } /********************************************************************** diff --git a/innobase/include/ut0ut.h b/innobase/include/ut0ut.h index f2c4781c167..1e93a2b8a36 100644 --- a/innobase/include/ut0ut.h +++ b/innobase/include/ut0ut.h @@ -136,6 +136,13 @@ ut_difftime( /* out: time2 - time1 expressed in seconds */ ib_time_t time2, /* in: time */ ib_time_t time1); /* in: time */ +/************************************************************** +Prints a timestamp to a file. */ + +void +ut_print_timestamp( +/*===============*/ + FILE* file); /* in: file where to print */ /***************************************************************** Runs an idle loop on CPU. The argument gives the desired delay in microseconds on 100 MHz Pentium + Visual C++. */ diff --git a/innobase/log/log0log.c b/innobase/log/log0log.c index 46fcf400d34..31cf595e59e 100644 --- a/innobase/log/log0log.c +++ b/innobase/log/log0log.c @@ -2634,8 +2634,9 @@ logs_empty_and_mark_files_at_shutdown(void) { dulint lsn; ulint arch_log_no; - - fprintf(stderr, "InnoDB: Starting shutdown...\n"); + + ut_print_timestamp(stderr); + fprintf(stderr, " InnoDB: Starting shutdown...\n"); /* Wait until the master thread and all other operations are idle: our algorithm only works if the server is idle at shutdown */ @@ -2725,7 +2726,8 @@ loop: fil_flush_file_spaces(FIL_TABLESPACE); - fprintf(stderr, "InnoDB: Shutdown completed\n"); + ut_print_timestamp(stderr); + fprintf(stderr, " InnoDB: Shutdown completed\n"); } /********************************************************** diff --git a/innobase/srv/srv0start.c b/innobase/srv/srv0start.c index 29ddf2a21c8..b584b663e43 100644 --- a/innobase/srv/srv0start.c +++ b/innobase/srv/srv0start.c @@ -813,7 +813,8 @@ innobase_start_or_create_for_mysql(void) /* Create the thread which watches the timeouts for lock waits */ os_thread_create(&srv_lock_timeout_monitor_thread, NULL, thread_ids + 2 + SRV_MAX_N_IO_THREADS); - fprintf(stderr, "InnoDB: Started\n"); + ut_print_timestamp(stderr); + fprintf(stderr, " InnoDB: Started\n"); srv_was_started = TRUE; srv_is_being_started = FALSE; @@ -835,8 +836,9 @@ innobase_shutdown_for_mysql(void) { if (!srv_was_started) { if (srv_is_being_started) { + ut_print_timestamp(stderr); fprintf(stderr, - "InnoDB: Warning: shutting down not properly started database\n"); + " InnoDB: Warning: shutting down a not properly started database\n"); } return(DB_SUCCESS); } diff --git a/innobase/ut/ut0ut.c b/innobase/ut/ut0ut.c index cfd714fc275..1436f6a10a3 100644 --- a/innobase/ut/ut0ut.c +++ b/innobase/ut/ut0ut.c @@ -49,6 +49,51 @@ ut_difftime( return(difftime(time2, time1)); } +/************************************************************** +Prints a timestamp to a file. */ + +void +ut_print_timestamp( +/*===============*/ + FILE* file) /* in: file where to print */ +{ +#ifdef __WIN__ + SYSTEMTIME cal_tm; + + GetLocalTime(&cal_tm); + + fprintf(file,"%02d%02d%02d %2d:%02d:%02d", + (int)cal_tm.wYear % 100, + (int)cal_tm.wMonth, + (int)cal_tm.wDay, + (int)cal_tm.wHour, + (int)cal_tm.wMinute, + (int)cal_tm.wSecond); +#else + + struct tm cal_tm; + struct tm* cal_tm_ptr; + time_t tm; + + time(&tm); + +#ifdef HAVE_LOCALTIME_R + localtime_r(&tm, &cal_tm); + cal_tm_ptr = &cal_tm; +#else + cal_tm_ptr = localtime(&tm); +#endif + + fprintf(file,"%02d%02d%02d %2d:%02d:%02d", + cal_tm_ptr->tm_year % 100, + cal_tm_ptr->tm_mon+1, + cal_tm_ptr->tm_mday, + cal_tm_ptr->tm_hour, + cal_tm_ptr->tm_min, + cal_tm_ptr->tm_sec); +#endif +} + /***************************************************************** Runs an idle loop on CPU. The argument gives the desired delay in microseconds on 100 MHz Pentium + Visual C++. */ diff --git a/libmysql/net.c b/libmysql/net.c index 44c082c1693..809cfb41e83 100644 --- a/libmysql/net.c +++ b/libmysql/net.c @@ -39,7 +39,6 @@ #include <signal.h> #include <errno.h> #include <sys/types.h> -#include <violite.h> #include <assert.h> #ifdef MYSQL_SERVER diff --git a/mysql-test/mysql-test-run.sh b/mysql-test/mysql-test-run.sh index 368ab69509d..969255d5e6e 100644 --- a/mysql-test/mysql-test-run.sh +++ b/mysql-test/mysql-test-run.sh @@ -310,8 +310,9 @@ show_failed_diff () echo "-------------------------------------------------------" $DIFF -c $result_file $reject_file echo "-------------------------------------------------------" - echo "Please e-mail the above, along with the output of mysqlbug" - echo "and any other relevant info to bugs@lists.mysql.com" + echo "Please follow the instructions outlined at" + echo "http://www.mysql.com/doc/R/e/Reporting_mysqltest_bugs.html" + echo "to find the reason to this problem and how to report this." fi } diff --git a/mysql-test/r/func_test.result b/mysql-test/r/func_test.result index 3dc0fc19848..5d2211baf50 100644 --- a/mysql-test/r/func_test.result +++ b/mysql-test/r/func_test.result @@ -34,3 +34,5 @@ this is a 2 2.0 1 1 1 and 0 or 2 2 or 1 and 0 1 1 +sum(if(num is null,0.00,num)) +144.54 diff --git a/mysql-test/t/func_test.test b/mysql-test/t/func_test.test index 9562ae5f77b..0439a96f077 100644 --- a/mysql-test/t/func_test.test +++ b/mysql-test/t/func_test.test @@ -24,3 +24,13 @@ select -1.49 or -1.49,0.6 or 0.6; select 5 between 0 and 10 between 0 and 1,(5 between 0 and 10) between 0 and 1; select 1 and 2 between 2 and 10, 2 between 2 and 10 and 1; select 1 and 0 or 2, 2 or 1 and 0; + +# +# Problem with IF() +# + +drop table if exists t1; +create table t1 (num double(12,2)); +insert into t1 values (144.54); +select sum(if(num is null,0.00,num)) from t1; +drop table t1; diff --git a/mysys/Makefile.am b/mysys/Makefile.am index 5a7293bc680..827367ac755 100644 --- a/mysys/Makefile.am +++ b/mysys/Makefile.am @@ -33,6 +33,7 @@ libmysys_a_SOURCES = my_init.c my_getwd.c mf_getdate.c\ my_alloc.c safemalloc.c my_fopen.c my_fstream.c \ my_error.c errors.c my_div.c my_messnc.c \ mf_format.c mf_same.c mf_dirname.c mf_fn_ext.c \ + my_symlink.c \ mf_pack.c mf_pack2.c mf_unixpath.c mf_stripp.c \ mf_casecnv.c mf_soundex.c mf_wcomp.c mf_wfile.c \ mf_qsort.c mf_qsort2.c mf_sort.c \ diff --git a/mysys/errors.c b/mysys/errors.c index 6e9f1fabab0..77e52c2f0b3 100644 --- a/mysys/errors.c +++ b/mysys/errors.c @@ -46,6 +46,9 @@ const char * NEAR globerrs[GLOBERRS]= "Can't create directory '%s' (Errcode: %d)", "Character set '%s' is not a compiled character set and is not specified in the '%s' file", "Out of resources when opening file '%s' (Errcode: %d)", + "Can't read value for symlink '%s' (Error %d)", + "Can't create symlink '%s' pointing at '%s' (Error %d)", + "Error on realpath() on '%s' (Error %d)", }; void init_glob_errs(void) @@ -81,6 +84,9 @@ void init_glob_errs() EE(EE_DISK_FULL) = "Disk is full writing '%s'. Waiting for someone to free space..."; EE(EE_CANT_MKDIR) ="Can't create directory '%s' (Errcode: %d)"; EE(EE_UNKNOWN_CHARSET)= "Character set is not a compiled character set and is not specified in the %s file"; - EE(EE_OUT_OF_FILERESOURCES)="Out of resources when opening file '%s' (Errcode: %d)", + EE(EE_OUT_OF_FILERESOURCES)="Out of resources when opening file '%s' (Errcode: %d)"; + EE(EE_CANT_READLINK)="Can't read value for symlink '%s' (Error %d)"; + EE(EE_CANT_SYMLINK)="Can't create symlink '%s' pointing at '%s' (Error %d)"; + EE(EE_REALPATH)="Error on realpath() on '%s' (Error %d)"; } #endif diff --git a/mysys/mf_brkhant.c b/mysys/mf_brkhant.c index 4e4bc2410f9..debf5d9a712 100644 --- a/mysys/mf_brkhant.c +++ b/mysys/mf_brkhant.c @@ -24,17 +24,15 @@ /* Set variable that we can't break */ +#if !defined(THREAD) void dont_break(void) { -#if !defined(THREAD) my_dont_interrupt=1; -#endif return; } /* dont_break */ void allow_break(void) { -#if !defined(THREAD) { reg1 int index; @@ -54,8 +52,8 @@ void allow_break(void) _my_signals=0; } } -#endif } /* dont_break */ +#endif /* Set old status */ diff --git a/mysys/mf_pack.c b/mysys/mf_pack.c index c18d37888b8..b442af7e9e5 100644 --- a/mysys/mf_pack.c +++ b/mysys/mf_pack.c @@ -236,11 +236,16 @@ void symdirget(char *dir) *pos++=temp; *pos=0; /* Restore old filename */ if (fp) { - if (fgets(buff, sizeof(buff), fp)) + if (fgets(buff, sizeof(buff)-1, fp)) { for (pos=strend(buff); pos > buff && (iscntrl(pos[-1]) || isspace(pos[-1])) ; pos --); + + /* Ensure that the symlink ends with the directory symbol */ + if (pos == buff || pos[-1] != FN_LIBCHAR) + *pos++=FN_LIBCHAR; + strmake(dir,buff, (uint) (pos-buff)); } my_fclose(fp,MYF(0)); diff --git a/mysys/my_symlink.c b/mysys/my_symlink.c new file mode 100644 index 00000000000..e195adcd4c5 --- /dev/null +++ b/mysys/my_symlink.c @@ -0,0 +1,171 @@ +/* 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 */ + +#include "mysys_priv.h" +#include "mysys_err.h" +#include <m_string.h> +#ifdef HAVE_REALPATH +#include <sys/param.h> +#include <sys/stat.h> +#endif + +/* + Reads the content of a symbolic link + If the file is not a symbolic link, return the original file name in to. +*/ + +int my_readlink(char *to, const char *filename, myf MyFlags) +{ +#ifndef HAVE_READLINK + strmov(to,filename); + return 0; +#else + int result=0; + int length; + DBUG_ENTER("my_readlink"); + + if ((length=readlink(filename, to, FN_REFLEN-1)) < 0) + { + /* Don't give an error if this wasn't a symlink */ + if ((my_errno=errno) == EINVAL) + { + strmov(to,filename); + } + else + { + if (MyFlags & MY_WME) + my_error(EE_CANT_READLINK, MYF(0), filename, errno); + result= -1; + } + } + else + to[length]=0; + DBUG_RETURN(result); +#endif /* HAVE_READLINK */ +} + + +/* Create a symbolic link */ + +int my_symlink(const char *content, const char *linkname, myf MyFlags) +{ +#ifndef HAVE_READLINK + return 0; +#else + int result; + DBUG_ENTER("my_symlink"); + + result= 0; + if (symlink(content, linkname)) + { + result= -1; + my_errno=errno; + if (MyFlags & MY_WME) + my_error(EE_CANT_SYMLINK, MYF(0), linkname, content, errno); + } + DBUG_RETURN(result); +#endif /* HAVE_READLINK */ +} + + +/* + Create a file and a symbolic link that points to this file + If linkname is a null pointer or equal to filename, we don't + create a link. + */ + + +File my_create_with_symlink(const char *linkname, const char *filename, + int createflags, int access_flags, myf MyFlags) +{ + File file; + int tmp_errno; + DBUG_ENTER("my_create_with_symlink"); + if ((file=my_create(filename, createflags, access_flags, MyFlags)) >= 0) + { + /* Test if we should create a link */ + if (linkname && strcmp(linkname,filename)) + { + /* Delete old link/file */ + if (MyFlags & MY_DELETE_OLD) + my_delete(linkname, MYF(0)); + /* Create link */ + if (my_symlink(filename, linkname, MyFlags)) + { + /* Fail, remove everything we have done */ + tmp_errno=my_errno; + my_close(file,MYF(0)); + my_delete(filename, MYF(0)); + file= -1; + my_errno=tmp_errno; + } + } + } + DBUG_RETURN(file); +} + + +/* + Resolve all symbolic links in path + 'to' may be equal to 'filename' + + Because purify gives a lot of UMR errors when using realpath(), + this code is disabled when using purify. + + If MY_RESOLVE_LINK is given, only do realpath if the file is a link. +*/ + +#if defined(SCO) +#define BUFF_LEN 4097 +#elif defined(MAXPATHLEN) +#define BUFF_LEN MAXPATHLEN +#else +#define BUFF_LEN FN_LEN +#endif + +int my_realpath(char *to, const char *filename, myf MyFlags) +{ +#if defined(HAVE_REALPATH) && !defined(HAVE_purify) && !defined(HAVE_BROKEN_REALPATH) + int result=0; + char buff[BUFF_LEN]; + struct stat stat_buff; + DBUG_ENTER("my_realpath"); + + if (!(MyFlags & MY_RESOLVE_LINK) || + (!lstat(filename,&stat_buff) && S_ISLNK(stat_buff.st_mode))) + { + char *ptr; + if ((ptr=realpath(filename,buff))) + strmake(to,ptr,FN_REFLEN-1); + else + { + /* Realpath didn't work; Use original name */ + my_errno=errno; + if (MyFlags & MY_WME) + my_error(EE_REALPATH, MYF(0), filename, my_errno); + if (to != filename) + strmov(to,filename); + result= -1; + } + } + return result; +#else + if (to != filename) + strmov(to,filename); + return 0; +#endif +} diff --git a/scripts/mysql_config.sh b/scripts/mysql_config.sh index 09f81c70a1f..ed344f4b1e3 100644 --- a/scripts/mysql_config.sh +++ b/scripts/mysql_config.sh @@ -45,7 +45,7 @@ EOF exit 1 } -if ! test $# -gt 0; then usage; fi +if test $# -le 0; then usage; fi while test $# -gt 0; do case $1 in diff --git a/scripts/safe_mysqld.sh b/scripts/safe_mysqld.sh index 6c006e96768..6eda1740ad6 100644 --- a/scripts/safe_mysqld.sh +++ b/scripts/safe_mysqld.sh @@ -114,7 +114,7 @@ fi pid_file= err_log= -# Get first arguments from the my.cfg file, groups [mysqld] and [safe_mysqld] +# Get first arguments from the my.cnf file, groups [mysqld] and [safe_mysqld] # and then merge with the command line arguments if test -x ./bin/my_print_defaults then diff --git a/sql/field.cc b/sql/field.cc index 1f1f00b161b..629ae899494 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -4087,6 +4087,59 @@ const char *Field_blob::unpack(char *to, const char *from) } +#ifdef HAVE_GEMINI_DB +/* Blobs in Gemini tables are stored separately from the rows which contain +** them (except for tiny blobs, which are stored in the row). For all other +** blob types (blob, mediumblob, longblob), the row contains the length of +** the blob data and a blob id. These methods (pack_id, get_id, and +** unpack_id) handle packing and unpacking blob fields in Gemini rows. +*/ +char *Field_blob::pack_id(char *to, const char *from, ulonglong id, uint max_length) +{ + char *save=ptr; + ptr=(char*) from; + ulong length=get_length(); // Length of from string + if (length > max_length) + { + ptr=to; + length=max_length; + store_length(length); // Store max length + ptr=(char*) from; + } + else + memcpy(to,from,packlength); // Copy length + if (length) + { + int8store(to+packlength, id); + } + ptr=save; // Restore org row pointer + return to+packlength+sizeof(id); +} + + +ulonglong Field_blob::get_id(const char *from) +{ + ulonglong id = 0; + ulong length=get_length(from); + if (length) + longlongget(id, from+packlength); + return id; +} + + +const char *Field_blob::unpack_id(char *to, const char *from, const char *bdata) +{ + memcpy(to,from,packlength); + ulong length=get_length(from); + from+=packlength; + if (length) + memcpy_fixed(to+packlength, &bdata, sizeof(bdata)); + else + bzero(to+packlength,sizeof(bdata)); + return from+sizeof(ulonglong); +} +#endif /* HAVE_GEMINI_DB */ + /* Keys for blobs are like keys on varchars */ int Field_blob::pack_cmp(const char *a, const char *b, uint key_length) diff --git a/sql/field.h b/sql/field.h index 2f03d849c9b..b5d7c613701 100644 --- a/sql/field.h +++ b/sql/field.h @@ -869,6 +869,13 @@ public: } char *pack(char *to, const char *from, uint max_length= ~(uint) 0); const char *unpack(char *to, const char *from); +#ifdef HAVE_GEMINI_DB + char *pack_id(char *to, const char *from, ulonglong id, + uint max_length= ~(uint) 0); + ulonglong get_id(const char *from); + const char *unpack_id(char *to, const char *from, const char *bdata); + enum_field_types blobtype() { return (packlength == 1 ? FIELD_TYPE_TINY_BLOB : FIELD_TYPE_BLOB);} +#endif char *pack_key(char *to, const char *from, uint max_length); char *pack_key_from_key_image(char* to, const char *from, uint max_length); int pack_cmp(const char *a, const char *b, uint key_length); diff --git a/sql/ha_gemini.cc b/sql/ha_gemini.cc index 73241c60be7..733f0aa3a7d 100644 --- a/sql/ha_gemini.cc +++ b/sql/ha_gemini.cc @@ -19,10 +19,13 @@ #pragma implementation // gcc: Class implementation #endif -#include "mysql_priv.h" -#ifdef HAVE_GEMINI_DB +#include <string.h> +#include "mysql_priv.h" #include "my_pthread.h" + +#ifdef HAVE_GEMINI_DB +#include "ha_gemini.h" #include "dbconfig.h" #include "dsmpub.h" #include "recpub.h" @@ -34,7 +37,17 @@ #include <hash.h> #include <stdarg.h> #include "geminikey.h" -#include "ha_gemini.h" + +#define gemini_msg MSGD_CALLBACK + +pthread_mutex_t gem_mutex; + +static HASH gem_open_tables; +static GEM_SHARE *get_share(const char *table_name, TABLE *table); +static int free_share(GEM_SHARE *share, bool mutex_is_locked); +static byte* gem_get_key(GEM_SHARE *share,uint *length, + my_bool not_used __attribute__((unused))); +static void gemini_lock_table_overflow_error(dsmContext_t *pcontext); const char *ha_gemini_ext=".gmd"; const char *ha_gemini_idx_ext=".gmi"; @@ -48,6 +61,7 @@ long gemini_locktablesize; long gemini_lock_wait_timeout; long gemini_spin_retries; long gemini_connection_limit; +char *gemini_basedir; const char gemini_dbname[] = "gemini"; dsmContext_t *pfirstContext = NULL; @@ -61,7 +75,7 @@ TYPELIB gemini_recovery_typelib= {array_elements(gemini_recovery_names),"", const int start_of_name = 2; /* Name passed as ./<db>/<table-name> and we're not interested in the ./ */ -static const int keyBufSize = MYMAXKEYSIZE * 2; +static const int keyBufSize = MAXKEYSZ + FULLKEYHDRSZ + MAX_REF_PARTS + 16; static int gemini_tx_begin(THD *thd); static void print_msg(THD *thd, const char *table_name, const char *op_name, @@ -87,40 +101,56 @@ bool gemini_init(void) goto badret; } + /* dsmContextCreate and dsmContextSetString(DSM_TAGDB_DBNAME) must + ** be the first DSM calls we make so that we can log any errors which + ** occur in subsequent DSM calls. DO NOT INSERT ANY DSM CALLS IN + ** BETWEEN THIS COMMENT AND THE COMMENT THAT SAYS "END OF CODE..." + */ /* Gotta connect to the database regardless of the operation */ rc = dsmContextCreate(&pfirstContext); if( rc != 0 ) { - printf("dsmContextCreate failed %ld\n",rc); + gemini_msg(pfirstContext, "dsmContextCreate failed %l",rc); goto badret; } + /* This call will also open the log file */ rc = dsmContextSetString(pfirstContext, DSM_TAGDB_DBNAME, strlen(gemini_dbname), (TEXT *)gemini_dbname); if( rc != 0 ) { - printf("Dbname tag failed %ld\n", rc); + gemini_msg(pfirstContext, "Dbname tag failed %l", rc); goto badret; } + /* END OF CODE NOT TO MESS WITH */ fn_format(pmsgsfile, GEM_MSGS_FILE, language, ".db", 2 | 4); rc = dsmContextSetString(pfirstContext, DSM_TAGDB_MSGS_FILE, strlen(pmsgsfile), (TEXT *)pmsgsfile); if( rc != 0 ) { - printf("MSGS_DIR tag failed %ld\n", rc); + gemini_msg(pfirstContext, "MSGS_DIR tag failed %l", rc); + goto badret; + } + + strxmov(pmsgsfile, gemini_basedir, GEM_SYM_FILE, NullS); + rc = dsmContextSetString(pfirstContext, DSM_TAGDB_SYMFILE, + strlen(pmsgsfile), (TEXT *)pmsgsfile); + if( rc != 0 ) + { + gemini_msg(pfirstContext, "SYMFILE tag failed %l", rc); goto badret; } rc = dsmContextSetLong(pfirstContext,DSM_TAGDB_ACCESS_TYPE,DSM_ACCESS_STARTUP); if ( rc != 0 ) { - printf("ACCESS TAG set failed %ld\n",rc); + gemini_msg(pfirstContext, "ACCESS TAG set failed %l",rc); goto badret; } rc = dsmContextSetLong(pfirstContext,DSM_TAGDB_ACCESS_ENV, DSM_SQL_ENGINE); if( rc != 0 ) { - printf("ACCESS_ENV set failed %ld",rc); + gemini_msg(pfirstContext, "ACCESS_ENV set failed %l",rc); goto badret; } @@ -129,7 +159,7 @@ bool gemini_init(void) (TEXT *)mysql_real_data_home); if( rc != 0 ) { - printf("Datadir tag failed %ld\n", rc); + gemini_msg(pfirstContext, "Datadir tag failed %l", rc); goto badret; } @@ -137,7 +167,7 @@ bool gemini_init(void) gemini_connection_limit); if(rc != 0) { - printf("MAX_USERS tag set failed %ld",rc); + gemini_msg(pfirstContext, "MAX_USERS tag set failed %l",rc); goto badret; } @@ -145,7 +175,7 @@ bool gemini_init(void) gemini_lock_wait_timeout); if(rc != 0) { - printf("MAX_LOCK_ENTRIES tag set failed %ld",rc); + gemini_msg(pfirstContext, "MAX_LOCK_ENTRIES tag set failed %l",rc); goto badret; } @@ -153,7 +183,7 @@ bool gemini_init(void) gemini_locktablesize); if(rc != 0) { - printf("MAX_LOCK_ENTRIES tag set failed %ld",rc); + gemini_msg(pfirstContext, "MAX_LOCK_ENTRIES tag set failed %l",rc); goto badret; } @@ -161,7 +191,7 @@ bool gemini_init(void) gemini_spin_retries); if(rc != 0) { - printf("SPIN_AMOUNT tag set failed %ld",rc); + gemini_msg(pfirstContext, "SPIN_AMOUNT tag set failed %l",rc); goto badret; } @@ -172,22 +202,22 @@ bool gemini_init(void) gemini_buffer_cache); if(rc != 0) { - printf("DB_BUFFERS tag set failed %ld",rc); + gemini_msg(pfirstContext, "DB_BUFFERS tag set failed %l",rc); goto badret; } rc = dsmContextSetLong(pfirstContext, DSM_TAGDB_FLUSH_AT_COMMIT, - ((gemini_options & GEMOPT_FLUSH_LOG) ? 1 : 0)); + ((gemini_options & GEMOPT_FLUSH_LOG) ? 0 : 1)); if(rc != 0) { - printf("FLush_Log_At_Commit tag set failed %ld",rc); + gemini_msg(pfirstContext, "FLush_Log_At_Commit tag set failed %l",rc); goto badret; } rc = dsmContextSetLong(pfirstContext, DSM_TAGDB_DIRECT_IO, ((gemini_options & GEMOPT_UNBUFFERED_IO) ? 1 : 0)); if(rc != 0) { - printf("DIRECT_IO tag set failed %ld",rc); + gemini_msg(pfirstContext, "DIRECT_IO tag set failed %l",rc); goto badret; } @@ -195,10 +225,20 @@ bool gemini_init(void) ((gemini_recovery_options & GEMINI_RECOVERY_FULL) ? 1 : 0)); if(rc != 0) { - printf("CRASH_PROTECTION tag set failed %ld",rc); + gemini_msg(pfirstContext, "CRASH_PROTECTION tag set failed %l",rc); goto badret; } + if (gemini_recovery_options & GEMINI_RECOVERY_FORCE) + { + rc = dsmContextSetLong(pfirstContext, DSM_TAGDB_FORCE_ACCESS, 1); + if(rc != 0) + { + printf("CRASH_PROTECTION tag set failed %ld",rc); + goto badret; + } + } + /* cluster size will come in bytes, need to convert it to 16 K units. */ gemini_log_cluster_size = (gemini_log_cluster_size + 16383) / 16384; @@ -207,7 +247,7 @@ bool gemini_init(void) if(rc != 0) { - printf("CRASH_PROTECTION tag set failed %ld",rc); + gemini_msg(pfirstContext, "CRASH_PROTECTION tag set failed %l",rc); goto badret; } @@ -215,12 +255,20 @@ bool gemini_init(void) DSM_DB_OPENDB | DSM_DB_OPENFILE); if( rc != 0 ) { - printf("dsmUserConnect failed rc = %ld\n",rc); + /* Message is output in dbenv() */ goto badret; } /* Set access to shared for subsequent user connects */ rc = dsmContextSetLong(pfirstContext,DSM_TAGDB_ACCESS_TYPE,DSM_ACCESS_SHARED); + rc = gemini_helper_threads(pfirstContext); + + + (void) hash_init(&gem_open_tables,32,0,0, + (hash_get_key) gem_get_key,0,0); + pthread_mutex_init(&gem_mutex,NULL); + + DBUG_RETURN(0); badret: @@ -231,30 +279,40 @@ badret: static int gemini_helper_threads(dsmContext_t *pContext) { int rc = 0; + int i; + pthread_attr_t thr_attr; + pthread_t hThread; DBUG_ENTER("gemini_helper_threads"); - rc = pthread_create (&hThread, 0, gemini_watchdog, (void *)pContext); + + (void) pthread_attr_init(&thr_attr); +#if !defined(HAVE_DEC_3_2_THREADS) + pthread_attr_setscope(&thr_attr,PTHREAD_SCOPE_SYSTEM); + (void) pthread_attr_setdetachstate(&thr_attr,PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&thr_attr,32768); +#endif + rc = pthread_create (&hThread, &thr_attr, gemini_watchdog, (void *)pContext); if (rc) { - printf("Can't create gemini watchdog thread"); + gemini_msg(pContext, "Can't Create gemini watchdog thread"); goto done; } if(!gemini_io_threads) goto done; - rc = pthread_create(&hThread, 0, gemini_rl_writer, (void *)pContext); + rc = pthread_create(&hThread, &thr_attr, gemini_rl_writer, (void *)pContext); if(rc) { - printf("Can't create gemini recovery log writer thread"); + gemini_msg(pContext, "Can't create Gemini recovery log writer thread"); goto done; } - for( int i = gemini_io_threads - 1;i;i--) + for(i = gemini_io_threads - 1;i;i--) { - rc = pthread_create(&hThread, 0, gemini_apw, (void *)pContext); + rc = pthread_create(&hThread, &thr_attr, gemini_apw, (void *)pContext); if(rc) { - printf("Can't create gemini page writer thread"); + gemini_msg(pContext, "Can't create Gemini database page writer thread"); goto done; } } @@ -273,7 +331,7 @@ pthread_handler_decl(gemini_watchdog,arg ) rc = dsmContextCopy(pcontext,&pmyContext, DSMCONTEXTDB); if( rc != 0 ) { - printf("dsmContextCopy failed for watchdog %d\n",rc); + gemini_msg(pcontext, "dsmContextCopy failed for Gemini watchdog %d",rc); return 0; } @@ -281,7 +339,7 @@ pthread_handler_decl(gemini_watchdog,arg ) if( rc != 0 ) { - printf("dsmUserConnect failed for watchdog %d\n",rc); + gemini_msg(pcontext, "dsmUserConnect failed for Gemini watchdog %d",rc); return 0; } @@ -311,7 +369,7 @@ pthread_handler_decl(gemini_rl_writer,arg ) rc = dsmContextCopy(pcontext,&pmyContext, DSMCONTEXTDB); if( rc != 0 ) { - printf("dsmContextCopy failed for recovery log writer %d\n",rc); + gemini_msg(pcontext, "dsmContextCopy failed for Gemini recovery log writer %d",rc); return 0; } @@ -319,7 +377,7 @@ pthread_handler_decl(gemini_rl_writer,arg ) if( rc != 0 ) { - printf("dsmUserConnect failed for recovery log writer %d\n",rc); + gemini_msg(pcontext, "dsmUserConnect failed for Gemini recovery log writer %d",rc); return 0; } @@ -348,7 +406,7 @@ pthread_handler_decl(gemini_apw,arg ) rc = dsmContextCopy(pcontext,&pmyContext, DSMCONTEXTDB); if( rc != 0 ) { - printf("dsmContextCopy failed for gemini page writer %d\n",rc); + gemini_msg(pcontext, "dsmContextCopy failed for Gemini page writer %d",rc); my_thread_end(); return 0; } @@ -356,7 +414,7 @@ pthread_handler_decl(gemini_apw,arg ) if( rc != 0 ) { - printf("dsmUserConnect failed for gemini page writer %d\n",rc); + gemini_msg(pcontext, "dsmUserConnect failed for Gemini page writer %d",rc); my_thread_end(); return 0; } @@ -388,7 +446,7 @@ int gemini_set_option_long(int optid, long optval) } if (rc) { - printf("SPIN_AMOUNT tag set failed %ld",rc); + gemini_msg(pfirstContext, "SPIN_AMOUNT tag set failed %l",rc); } else { @@ -410,7 +468,7 @@ static int gemini_connect(THD *thd) DSMCONTEXTDB); if( rc != 0 ) { - printf("dsmContextCopy failed %ld\n",rc); + gemini_msg(pfirstContext, "dsmContextCopy failed %l",rc); return(rc); } @@ -418,7 +476,7 @@ static int gemini_connect(THD *thd) if( rc != 0 ) { - printf("dsmUserConnect failed %ld\n",rc); + gemini_msg(pfirstContext, "dsmUserConnect failed %l",rc); return(rc); } @@ -444,6 +502,9 @@ bool gemini_end(void) THD *thd; DBUG_ENTER("gemini_end"); + + hash_free(&gem_open_tables); + pthread_mutex_destroy(&gem_mutex); if(pfirstContext) { rc = dsmShutdownSet(pfirstContext, DSM_SHUTDOWN_NORMAL); @@ -534,6 +595,24 @@ int gemini_rollback_to_savepoint(THD *thd) DBUG_RETURN(rc); } +int gemini_recovery_logging(THD *thd, bool on) +{ + int error; + int noLogging; + + if(!thd->gemini.context) + return 0; + + if(on) + noLogging = 0; + else + noLogging = 1; + + error = dsmContextSetLong((dsmContext_t *)thd->gemini.context, + DSM_TAGCONTEXT_NO_LOGGING,noLogging); + return error; +} + /* gemDataType - translates from mysql data type constant to gemini key services data type contstant */ int gemDataType ( int mysqlType ) @@ -599,8 +678,13 @@ int ha_gemini::open(const char *name, int mode, uint test_if_locked) DBUG_ENTER("ha_gemini::open"); thd = current_thd; - thr_lock_init(&alock); - thr_lock_data_init(&alock,&lock,(void*)0); + /* Init shared structure */ + if (!(share=get_share(name,table))) + { + DBUG_RETURN(1); /* purecov: inspected */ + } + thr_lock_data_init(&share->lock,&lock,(void*) 0); + ref_length = sizeof(dsmRecid_t); if(thd->gemini.context == NULL) @@ -610,7 +694,7 @@ int ha_gemini::open(const char *name, int mode, uint test_if_locked) if(rc) return rc; } - if (!(rec_buff=my_malloc(table->rec_buff_length, + if (!(rec_buff=(byte*)my_malloc(table->rec_buff_length, MYF(MY_WME)))) { DBUG_RETURN(1); @@ -635,6 +719,12 @@ int ha_gemini::open(const char *name, int mode, uint test_if_locked) rc = dsmObjectNameToNum((dsmContext_t *)thd->gemini.context, (dsmText_t *)name_buff, &tableId); + if (rc) + { + gemini_msg((dsmContext_t *)thd->gemini.context, + "Unable to find table number for %s", name_buff); + DBUG_RETURN(rc); + } } tableNumber = tableId; @@ -649,8 +739,33 @@ int ha_gemini::open(const char *name, int mode, uint test_if_locked) crashed while being in the midst of a repair operation */ rc = dsmTableStatus((dsmContext_t *)thd->gemini.context, tableNumber,&tableStatus); - if(tableStatus) + if(tableStatus == DSM_OBJECT_IN_REPAIR) tableStatus = HA_ERR_CRASHED; + + pthread_mutex_lock(&share->mutex); + share->use_count++; + pthread_mutex_unlock(&share->mutex); + + if (table->blob_fields) + { + /* Allocate room for the blob ids from an unpacked row. Note that + ** we may not actually need all of this space because tiny blobs + ** are stored in the packed row, not in a separate storage object + ** like larger blobs. But we allocate an entry for all blobs to + ** keep the code simpler. + */ + pBlobDescs = (gemBlobDesc_t *)my_malloc( + table->blob_fields * sizeof(gemBlobDesc_t), + MYF(MY_WME | MY_ZEROFILL)); + } + else + { + pBlobDescs = 0; + } + + get_index_stats(thd); + info(HA_STATUS_CONST); + DBUG_RETURN (rc); } @@ -680,6 +795,12 @@ int ha_gemini::index_open(char *tableName) rc = dsmObjectNameToNum((dsmContext_t *)thd->gemini.context, (dsmText_t *)tableName, &objectNumber); + if (rc) + { + gemini_msg((dsmContext_t *)thd->gemini.context, + "Unable to file Index number for %s", tableName); + DBUG_RETURN(rc); + } pindexNumbers[i] = objectNumber; } } @@ -692,12 +813,22 @@ int ha_gemini::index_open(char *tableName) int ha_gemini::close(void) { DBUG_ENTER("ha_gemini::close"); - thr_lock_delete(&alock); - my_free(rec_buff,MYF(MY_ALLOW_ZERO_PTR)); + my_free((char*)rec_buff,MYF(MY_ALLOW_ZERO_PTR)); rec_buff = 0; my_free((char *)pindexNumbers,MYF(MY_ALLOW_ZERO_PTR)); pindexNumbers = 0; - DBUG_RETURN(0); + + if (pBlobDescs) + { + for (uint i = 0; i < table->blob_fields; i++) + { + my_free((char*)pBlobDescs[i].pBlob, MYF(MY_ALLOW_ZERO_PTR)); + } + my_free((char *)pBlobDescs, MYF(0)); + pBlobDescs = 0; + } + + DBUG_RETURN(free_share(share, 0)); } @@ -709,7 +840,7 @@ int ha_gemini::write_row(byte * record) DBUG_ENTER("write_row"); - if(tableStatus) + if(tableStatus == HA_ERR_CRASHED) DBUG_RETURN(tableStatus); thd = current_thd; @@ -737,10 +868,11 @@ int ha_gemini::write_row(byte * record) /* A set insert-id statement so set the auto-increment value if this value is higher than it's current value */ error = dsmTableAutoIncrement((dsmContext_t *)thd->gemini.context, - tableNumber, (ULONG64 *)&nr); + tableNumber, (ULONG64 *)&nr,1); if(thd->next_insert_id > nr) { - error = dsmTableAutoIncrementSet((dsmContext_t *)thd->gemini.context,tableNumber, + error = dsmTableAutoIncrementSet((dsmContext_t *)thd->gemini.context, + tableNumber, (ULONG64)thd->next_insert_id); } } @@ -749,11 +881,13 @@ int ha_gemini::write_row(byte * record) } dsmRecord.table = tableNumber; - dsmRecord.maxLength = table->reclength; + dsmRecord.maxLength = table->rec_buff_length; if ((error=pack_row((byte **)&dsmRecord.pbuffer, (int *)&dsmRecord.recLength, - record))) + record, FALSE))) + { DBUG_RETURN(error); + } error = dsmRecordCreate((dsmContext_t *)thd->gemini.context, &dsmRecord,0); @@ -769,6 +903,8 @@ int ha_gemini::write_row(byte * record) thd->gemini.needSavepoint = 1; } } + if(error == DSM_S_RQSTREJ) + error = HA_ERR_LOCK_WAIT_TIMEOUT; DBUG_RETURN(error); } @@ -777,10 +913,17 @@ longlong ha_gemini::get_auto_increment() { longlong nr; int error; + int update; THD *thd=current_thd; + if(thd->lex.sql_command == SQLCOM_SHOW_TABLES) + update = 0; + else + update = 1; + error = dsmTableAutoIncrement((dsmContext_t *)thd->gemini.context, - tableNumber, (ULONG64 *)&nr); + tableNumber, (ULONG64 *)&nr, + update); return nr; } @@ -828,8 +971,8 @@ int ha_gemini::handleIndexEntry(const byte * record, dsmRecid_t recid, expects that the three lead bytes of the header are not counted in this length -- But cxKeyPrepare also expects that these three bytes are present in the keystr */ - theKey.akey.keyLen = (COUNT)keyStringLen - 3; - theKey.akey.unknown_comp = thereIsAnull; + theKey.akey.keyLen = (COUNT)keyStringLen - FULLKEYHDRSZ; + theKey.akey.unknown_comp = (dsmBoolean_t)thereIsAnull; theKey.akey.word_index = 0; theKey.akey.descending_key =0; if(option == KEY_CREATE) @@ -880,6 +1023,7 @@ int ha_gemini::createKeyString(const byte * record, KEY *pkeyinfo, int componentLen; int fieldType; int isNull; + uint key_part_length; KEY_PART_INFO *key_part; @@ -892,21 +1036,35 @@ int ha_gemini::createKeyString(const byte * record, KEY *pkeyinfo, unsigned char *pos; key_part = pkeyinfo->key_part + i; + key_part_length = key_part->length; fieldType = gemDataType(key_part->field->type()); - if(fieldType == GEM_CHAR) + switch (fieldType) { + case GEM_CHAR: + { /* Save the current ptr to the field in case we're building a key to remove an old key value when an indexed character column gets updated. */ char *ptr = key_part->field->ptr; key_part->field->ptr = (char *)record + key_part->offset; - key_part->field->sort_string(rec_buff, key_part->length); + key_part->field->sort_string((char*)rec_buff, key_part->length); key_part->field->ptr = ptr; pos = (unsigned char *)rec_buff; - } - else - { + } + break; + + case GEM_TINYBLOB: + case GEM_BLOB: + case GEM_MEDIUMBLOB: + case GEM_LONGBLOB: + ((Field_blob*)key_part->field)->get_ptr((char**)&pos); + key_part_length = ((Field_blob*)key_part->field)->get_length( + (char*)record + key_part->offset); + break; + + default: pos = (unsigned char *)record + key_part->offset; + break; } isNull = record[key_part->null_offset] & key_part->null_bit; @@ -914,7 +1072,7 @@ int ha_gemini::createKeyString(const byte * record, KEY *pkeyinfo, *thereIsAnull = true; rc = gemFieldToIdxComponent(pos, - (unsigned long) key_part->length, + (unsigned long) key_part_length, fieldType, isNull , key_part->field->flags & UNSIGNED_FLAG, @@ -951,7 +1109,7 @@ int ha_gemini::update_row(const byte * old_record, byte * new_record) } for (uint keynr=0 ; keynr < table->keys ; keynr++) { - if(key_cmp(keynr,old_record, new_record)) + if(key_cmp(keynr,old_record, new_record,false)) { error = handleIndexEntry(old_record,lastRowid,KEY_DELETE,keynr); if(error) @@ -973,10 +1131,10 @@ int ha_gemini::update_row(const byte * old_record, byte * new_record) dsmRecord.table = tableNumber; dsmRecord.recid = lastRowid; - dsmRecord.maxLength = table->reclength; + dsmRecord.maxLength = table->rec_buff_length; if ((error=pack_row((byte **)&dsmRecord.pbuffer, (int *)&dsmRecord.recLength, - new_record))) + new_record, TRUE))) { DBUG_RETURN(error); } @@ -992,6 +1150,7 @@ int ha_gemini::delete_row(const byte * record) int error = 0; dsmRecord_t dsmRecord; THD *thd = current_thd; + dsmContext_t *pcontext = (dsmContext_t *)thd->gemini.context; DBUG_ENTER("delete_row"); statistic_increment(ha_delete_count,&LOCK_status); @@ -999,9 +1158,7 @@ int ha_gemini::delete_row(const byte * record) if(thd->gemini.needSavepoint) { thd->gemini.savepoint++; - error = dsmTransaction((dsmContext_t *)thd->gemini.context, - &thd->gemini.savepoint, - DSMTXN_SAVE, 0, 0); + error = dsmTransaction(pcontext, &thd->gemini.savepoint, DSMTXN_SAVE, 0, 0); if (error) DBUG_RETURN(error); thd->gemini.needSavepoint = 0; @@ -1013,8 +1170,27 @@ int ha_gemini::delete_row(const byte * record) error = handleIndexEntries(record, dsmRecord.recid,KEY_DELETE); if(!error) { - error = dsmRecordDelete((dsmContext_t *)thd->gemini.context, - &dsmRecord, 0, NULL); + error = dsmRecordDelete(pcontext, &dsmRecord, 0, NULL); + } + + /* Delete any blobs associated with this row */ + if (table->blob_fields) + { + dsmBlob_t gemBlob; + + gemBlob.areaType = DSMOBJECT_BLOB; + gemBlob.blobObjNo = tableNumber; + for (uint i = 0; i < table->blob_fields; i++) + { + if (pBlobDescs[i].blobId) + { + gemBlob.blobId = pBlobDescs[i].blobId; + my_free((char *)pBlobDescs[i].pBlob, MYF(MY_ALLOW_ZERO_PTR)); + dsmBlobStart(pcontext, &gemBlob); + dsmBlobDelete(pcontext, &gemBlob, NULL); + /* according to DSM doc, no need to call dsmBlobEnd() */ + } + } } DBUG_RETURN(error); @@ -1023,7 +1199,6 @@ int ha_gemini::delete_row(const byte * record) int ha_gemini::index_init(uint keynr) { int error = 0; - int keyStringLen; THD *thd; DBUG_ENTER("index_init"); thd = current_thd; @@ -1046,19 +1221,9 @@ int ha_gemini::index_init(uint keynr) } pbracketBase->index = 0; pbracketLimit->index = (dsmIndex_t)pindexNumbers[keynr]; - pbracketLimit->keycomps = 1; - keyStringLen = 0; - error = gemKeyHigh(pbracketLimit->keystr, &keyStringLen, - pbracketLimit->index); - - /* We have to subtract three here since cxKeyPrepare - expects that the three lead bytes of the header are - not counted in this length -- But cxKeyPrepare also - expects that these three bytes are present in the keystr */ - pbracketLimit->keyLen = (COUNT)keyStringLen - 3; - pbracketBase->descending_key = pbracketLimit->descending_key = 0; pbracketBase->ksubstr = pbracketLimit->ksubstr = 0; + pbracketLimit->keycomps = pbracketBase->keycomps = 1; pfoundKey = (dsmKey_t *)my_malloc(sizeof(dsmKey_t) + keyBufSize,MYF(MY_WME)); if(!pfoundKey) @@ -1130,6 +1295,7 @@ int ha_gemini::pack_key( uint keynr, dsmKey_t *pkey, { uint offset=0; unsigned char *pos; + uint key_part_length = key_part->length; int fieldType; if (key_part->null_bit) @@ -1141,7 +1307,7 @@ int ha_gemini::pack_key( uint keynr, dsmKey_t *pkey, key_ptr+= key_part->store_length; rc = gemFieldToIdxComponent( (unsigned char *)key_ptr + offset, - (unsigned long) key_part->length, + (unsigned long) key_part_length, 0, 1 , /* Tells it to build a null component */ key_part->field->flags & UNSIGNED_FLAG, @@ -1153,20 +1319,31 @@ int ha_gemini::pack_key( uint keynr, dsmKey_t *pkey, } } fieldType = gemDataType(key_part->field->type()); - if(fieldType == GEM_CHAR) + switch (fieldType) { - key_part->field->store(key_ptr + offset, key_part->length); - key_part->field->sort_string(rec_buff, key_part->length); + case GEM_CHAR: + key_part->field->store((char*)key_ptr + offset, key_part->length); + key_part->field->sort_string((char*)rec_buff, key_part->length); pos = (unsigned char *)rec_buff; - } - else - { + break; + + case GEM_TINYBLOB: + case GEM_BLOB: + case GEM_MEDIUMBLOB: + case GEM_LONGBLOB: + ((Field_blob*)key_part->field)->get_ptr((char**)&pos); + key_part_length = ((Field_blob*)key_part->field)->get_length( + (char*)key_ptr + offset); + break; + + default: pos = (unsigned char *)key_ptr + offset; + break; } rc = gemFieldToIdxComponent( pos, - (unsigned long) key_part->length, + (unsigned long) key_part_length, fieldType, 0 , key_part->field->flags & UNSIGNED_FLAG, @@ -1189,7 +1366,7 @@ void ha_gemini::unpack_key(char *record, dsmKey_t *key, uint index) int fieldIsNull, fieldType; int rc = 0; - char unsigned *pos= &key->keystr[7]; + char unsigned *pos= &key->keystr[FULLKEYHDRSZ+4/* 4 for the index number*/]; for ( ; key_part != end; key_part++) { @@ -1202,7 +1379,8 @@ void ha_gemini::unpack_key(char *record, dsmKey_t *key, uint index) } rc = gemIdxComponentToField(pos, fieldType, (unsigned char *)record + key_part->field->offset(), - key_part->field->field_length, + //key_part->field->field_length, + key_part->length, key_part->field->decimals(), &fieldIsNull); if(fieldIsNull) @@ -1266,12 +1444,12 @@ int ha_gemini::index_read(byte * buf, const byte * key, pbracketLimit->keyLen = componentLen; } - /* We have to subtract three here since cxKeyPrepare + /* We have to subtract the header size here since cxKeyPrepare expects that the three lead bytes of the header are not counted in this length -- But cxKeyPrepare also expects that these three bytes are present in the keystr */ - pbracketBase->keyLen -= 3; - pbracketLimit->keyLen -= 3; + pbracketBase->keyLen -= FULLKEYHDRSZ; + pbracketLimit->keyLen -= FULLKEYHDRSZ; thd = current_thd; @@ -1294,7 +1472,7 @@ int ha_gemini::index_next(byte * buf) dsmMask_t findMode; DBUG_ENTER("index_next"); - if(tableStatus) + if(tableStatus == HA_ERR_CRASHED) DBUG_RETURN(tableStatus); thd = current_thd; @@ -1304,9 +1482,12 @@ int ha_gemini::index_next(byte * buf) error = gemKeyLow(pbracketBase->keystr, &keyStringLen, pbracketLimit->index); - pbracketBase->keyLen = (COUNT)keyStringLen - 3; + pbracketBase->keyLen = (COUNT)keyStringLen - FULLKEYHDRSZ; pbracketBase->index = pbracketLimit->index; - pbracketBase->keycomps = 1; + error = gemKeyHigh(pbracketLimit->keystr, &keyStringLen, + pbracketLimit->index); + pbracketLimit->keyLen = (COUNT)keyStringLen - FULLKEYHDRSZ; + findMode = DSMFINDFIRST; } else @@ -1369,24 +1550,20 @@ int ha_gemini::index_last(byte * buf) error = gemKeyLow(pbracketBase->keystr, &keyStringLen, pbracketLimit->index); - if(error) - goto errorReturn; - pbracketBase->keyLen = (COUNT)keyStringLen - 3; + pbracketBase->keyLen = (COUNT)keyStringLen - FULLKEYHDRSZ; pbracketBase->index = pbracketLimit->index; - pbracketBase->keycomps = 1; + error = gemKeyHigh(pbracketLimit->keystr, &keyStringLen, + pbracketLimit->index); + pbracketLimit->keyLen = (COUNT)keyStringLen - FULLKEYHDRSZ; error = findRow(thd,DSMFINDLAST,buf); -errorReturn: if (error == DSM_S_ENDLOOP) error = HA_ERR_END_OF_FILE; table->status = error ? STATUS_NOT_FOUND : 0; DBUG_RETURN(error); - - table->status = error ? STATUS_NOT_FOUND : 0; - DBUG_RETURN(error); } int ha_gemini::rnd_init(bool scan) @@ -1414,7 +1591,7 @@ int ha_gemini::rnd_next(byte *buf) DBUG_ENTER("rnd_next"); - if(tableStatus) + if(tableStatus == HA_ERR_CRASHED) DBUG_RETURN(tableStatus); thd = current_thd; @@ -1429,7 +1606,7 @@ int ha_gemini::rnd_next(byte *buf) dsmRecord.recid = lastRowid; dsmRecord.pbuffer = (dsmBuffer_t *)rec_buff; dsmRecord.recLength = table->reclength; - dsmRecord.maxLength = table->reclength; + dsmRecord.maxLength = table->rec_buff_length; error = dsmTableScan((dsmContext_t *)thd->gemini.context, &dsmRecord, DSMFINDNEXT, lockMode, 0); @@ -1437,17 +1614,23 @@ int ha_gemini::rnd_next(byte *buf) if(!error) { lastRowid = dsmRecord.recid; - unpack_row((char *)buf,(char *)dsmRecord.pbuffer); + error = unpack_row((char *)buf,(char *)dsmRecord.pbuffer); } if(!error) ; - else if (error == DSM_S_ENDLOOP) - error = HA_ERR_END_OF_FILE; - else if (error == DSM_S_RQSTREJ) - error = HA_ERR_LOCK_WAIT_TIMEOUT; - else if (error == DSM_S_LKTBFULL) - error = HA_ERR_LOCK_TABLE_FULL; - + else + { + lastRowid = 0; + if (error == DSM_S_ENDLOOP) + error = HA_ERR_END_OF_FILE; + else if (error == DSM_S_RQSTREJ) + error = HA_ERR_LOCK_WAIT_TIMEOUT; + else if (error == DSM_S_LKTBFULL) + { + error = HA_ERR_LOCK_TABLE_FULL; + gemini_lock_table_overflow_error((dsmContext_t *)thd->gemini.context); + } + } table->status = error ? STATUS_NOT_FOUND : 0; DBUG_RETURN(error); } @@ -1500,14 +1683,14 @@ int ha_gemini::fetch_row(void *gemini_context,const byte *buf) dsmRecord.recid = lastRowid; dsmRecord.pbuffer = (dsmBuffer_t *)rec_buff; dsmRecord.recLength = table->reclength; - dsmRecord.maxLength = table->reclength; + dsmRecord.maxLength = table->rec_buff_length; rc = dsmRecordGet((dsmContext_t *)gemini_context, &dsmRecord, 0); if(!rc) { - unpack_row((char *)buf,(char *)dsmRecord.pbuffer); + rc = unpack_row((char *)buf,(char *)dsmRecord.pbuffer); } DBUG_RETURN(rc); @@ -1544,7 +1727,7 @@ int ha_gemini::findRow(THD *thd, dsmMask_t findMode, byte *buf) if(key_read) { - unpack_key(buf, pkey, active_index); + unpack_key((char*)buf, pkey, active_index); } if(!key_read) /* unpack_key may have turned off key_read */ { @@ -1554,10 +1737,17 @@ int ha_gemini::findRow(THD *thd, dsmMask_t findMode, byte *buf) errorReturn: if(!rc) ; - else if(rc == DSM_S_RQSTREJ) - rc = HA_ERR_LOCK_WAIT_TIMEOUT; - else if (rc == DSM_S_LKTBFULL) - rc = HA_ERR_LOCK_TABLE_FULL; + else + { + lastRowid = 0; + if(rc == DSM_S_RQSTREJ) + rc = HA_ERR_LOCK_WAIT_TIMEOUT; + else if (rc == DSM_S_LKTBFULL) + { + rc = HA_ERR_LOCK_TABLE_FULL; + gemini_lock_table_overflow_error((dsmContext_t *)thd->gemini.context); + } + } DBUG_RETURN(rc); } @@ -1578,25 +1768,47 @@ void ha_gemini::info(uint flag) dsmStatus_t error; ULONG64 rows; + if(thd->gemini.context == NULL) + { + /* Need to get this thread a connection into the database */ + error = gemini_connect(thd); + if(error) + DBUG_VOID_RETURN; + } + error = dsmRowCount((dsmContext_t *)thd->gemini.context,tableNumber,&rows); records = (ha_rows)rows; deleted = 0; } - else if ((flag & HA_STATUS_CONST)) + if ((flag & HA_STATUS_CONST)) { - ; + ha_rows *rec_per_key = share->rec_per_key; + for (uint i = 0; i < table->keys; i++) + for(uint k=0; + k < table->key_info[i].key_parts; k++,rec_per_key++) + table->key_info[i].rec_per_key[k] = *rec_per_key; } - else if ((flag & HA_STATUS_ERRKEY)) + if ((flag & HA_STATUS_ERRKEY)) { errkey=last_dup_key; } - else if ((flag & HA_STATUS_TIME)) + if ((flag & HA_STATUS_TIME)) { ; } - else if ((flag & HA_STATUS_AUTO)) + if ((flag & HA_STATUS_AUTO)) { - ; + THD *thd = current_thd; + dsmStatus_t error; + + error = dsmTableAutoIncrement((dsmContext_t *)thd->gemini.context, + tableNumber, + (ULONG64 *)&auto_increment_value, + 0); + /* Should return the next auto-increment value that + will be given -- so we need to increment the one dsm + currently reports. */ + auto_increment_value++; } DBUG_VOID_RETURN; @@ -1658,7 +1870,22 @@ int ha_gemini::external_lock(THD *thd, int lock_type) thd->gemini.lock_count = 1; thd->gemini.tx_isolation = thd->tx_isolation; } - + // lockMode has already been set in store_lock + // If the statement about to be executed calls for + // exclusive locks and we're running at read uncommitted + // isolation level then raise an error. + if(thd->gemini.tx_isolation == ISO_READ_UNCOMMITTED) + { + if(lockMode == DSM_LK_EXCL) + { + DBUG_RETURN(HA_ERR_READ_ONLY_TRANSACTION); + } + else + { + lockMode = DSM_LK_NOLOCK; + } + } + if(thd->gemini.context == NULL) { /* Need to get this thread a connection into the database */ @@ -1678,6 +1905,8 @@ int ha_gemini::external_lock(THD *thd, int lock_type) rc = dsmObjectLock((dsmContext_t *)thd->gemini.context, (dsmObject_t)tableNumber,DSMOBJECT_TABLE,0, lockMode, 1, 0); + if(rc == DSM_S_RQSTREJ) + rc = HA_ERR_LOCK_WAIT_TIMEOUT; } } else /* lock_type == F_UNLK */ @@ -1703,18 +1932,24 @@ THR_LOCK_DATA **ha_gemini::store_lock(THD *thd, THR_LOCK_DATA **to, !thd->in_lock_tables) lock_type = TL_WRITE_ALLOW_WRITE; lock.type=lock_type; - - if(thd->gemini.tx_isolation == ISO_READ_UNCOMMITTED) - lockMode = DSM_LK_NOLOCK; - else if(table->reginfo.lock_type > TL_WRITE_ALLOW_READ) - lockMode = DSM_LK_EXCL; - else - lockMode = DSM_LK_SHARE; } + if(table->reginfo.lock_type > TL_WRITE_ALLOW_READ) + lockMode = DSM_LK_EXCL; + else + lockMode = DSM_LK_SHARE; + *to++= &lock; return to; } +void ha_gemini::update_create_info(HA_CREATE_INFO *create_info) +{ + table->file->info(HA_STATUS_AUTO | HA_STATUS_CONST); + if (!(create_info->used_fields & HA_CREATE_USED_AUTO)) + { + create_info->auto_increment_value=auto_increment_value; + } +} int ha_gemini::create(const char *name, register TABLE *form, HA_CREATE_INFO *create_info) @@ -1777,7 +2012,7 @@ int ha_gemini::create(const char *name, register TABLE *form, (dsmText_t *)"gemini_data_area"); if( rc != 0 ) { - printf("dsmAreaNew failed %ld\n",rc); + gemini_msg(pcontext, "dsmAreaNew failed %l",rc); return(rc); } @@ -1787,7 +2022,7 @@ int ha_gemini::create(const char *name, register TABLE *form, (dsmText_t *)&name_buff[start_of_name]); if( rc != 0 ) { - printf("dsmExtentCreate failed %ld\n",rc); + gemini_msg(pcontext, "dsmExtentCreate failed %l",rc); return(rc); } @@ -1805,6 +2040,20 @@ int ha_gemini::create(const char *name, register TABLE *form, (dsmText_t *)&name_buff[start_of_name], &dummy,&dummy); + if (rc == 0 && table->blob_fields) + { + /* create a storage object record for blob fields */ + rc = dsmObjectCreate(pcontext, areaNumber, &tableNumber, + DSMOBJECT_BLOB,0,0,0, + (dsmText_t *)&name_buff[start_of_name], + &dummy,&dummy); + if( rc != 0 ) + { + gemini_msg(pcontext, "dsmObjectCreate for blob object failed %l",rc); + return(rc); + } + } + if(rc == 0 && form->keys) { fn_format(name_buff, name, "", ha_gemini_idx_ext, 2 | 4); @@ -1814,7 +2063,7 @@ int ha_gemini::create(const char *name, register TABLE *form, (dsmText_t *)"gemini_index_area"); if( rc != 0 ) { - printf("dsmAreaNew failed %ld\n",rc); + gemini_msg(pcontext, "dsmAreaNew failed %l",rc); return(rc); } /* Create an extent */ @@ -1823,7 +2072,7 @@ int ha_gemini::create(const char *name, register TABLE *form, (dsmText_t *)&name_buff[start_of_name]); if( rc != 0 ) { - printf("dsmExtentCreate failed %ld\n",rc); + gemini_msg(pcontext, "dsmExtentCreate failed %l",rc); return(rc); } @@ -1859,10 +2108,11 @@ int ha_gemini::create(const char *name, register TABLE *form, } } - rc = dsmTableAutoIncrementSet(pcontext,tableNumber, - create_info->auto_increment_value); - - + /* The auto_increment value is the next one to be given + out so give dsm one less than this value */ + if(create_info->auto_increment_value) + rc = dsmTableAutoIncrementSet(pcontext,tableNumber, + create_info->auto_increment_value-1); /* Get a table lock on this table in case this table is being created as part of an alter table statement. We don't want @@ -1950,26 +2200,25 @@ int ha_gemini::delete_table(const char *pname) (dsmObject_t *)&tableNum); if (rc) { - printf("Cound not find table number for %s with string %s, %ld\n", - pname,name_buff,rc); + gemini_msg(pcontext, "Unable to find table number for %s", name_buff); rc = gemini_rollback(thd); if (rc) { - printf("Error in rollback %ld\n",rc); + gemini_msg(pcontext, "Error in rollback %l",rc); } DBUG_RETURN(rc); } - rc = dsmObjectInfo(pcontext, tableNum, DSMOBJECT_MIXTABLE, &tableArea, - &objectAttr, &associate, &associateType, &block, &root); + rc = dsmObjectInfo(pcontext, tableNum, DSMOBJECT_MIXTABLE, tableNum, + &tableArea, &objectAttr, &associateType, &block, &root); if (rc) { - printf("Failed to get area number for table %d, %s, return %ld\n", + gemini_msg(pcontext, "Failed to get area number for table %d, %s, return %l", tableNum, pname, rc); rc = gemini_rollback(thd); if (rc) { - printf("Error in rollback %ld\n",rc); + gemini_msg(pcontext, "Error in rollback %l",rc); } } @@ -1979,14 +2228,14 @@ int ha_gemini::delete_table(const char *pname) rc = dsmObjectDeleteAssociate(pcontext, tableNum, &indexArea); if (rc) { - printf("Error deleting storage objects for table number %d, return %ld\n", + gemini_msg(pcontext, "Error deleting storage objects for table number %d, return %l", (int)tableNum, rc); /* roll back txn and return */ rc = gemini_rollback(thd); if (rc) { - printf("Error in rollback %ld\n",rc); + gemini_msg(pcontext, "Error in rollback %l",rc); } DBUG_RETURN(rc); } @@ -1994,33 +2243,33 @@ int ha_gemini::delete_table(const char *pname) if (indexArea != DSMAREA_INVALID) { /* Delete the extents for both Index and Table */ - rc = dsmExtentDelete(pcontext, indexArea, 0); + rc = dsmExtentDelete(pcontext, indexArea); rc = dsmAreaDelete(pcontext, indexArea); if (rc) { - printf("Error deleting Index Area %ld, return %ld\n", indexArea, rc); + gemini_msg(pcontext, "Error deleting Index Area %l, return %l", indexArea, rc); /* roll back txn and return */ rc = gemini_rollback(thd); if (rc) { - printf("Error in rollback %ld\n",rc); + gemini_msg(pcontext, "Error in rollback %l",rc); } DBUG_RETURN(rc); } } - rc = dsmExtentDelete(pcontext, tableArea, 0); + rc = dsmExtentDelete(pcontext, tableArea); rc = dsmAreaDelete(pcontext, tableArea); if (rc) { - printf("Error deleting table Area %ld, name %s, return %ld\n", + gemini_msg(pcontext, "Error deleting table Area %l, name %s, return %l", tableArea, pname, rc); /* roll back txn and return */ rc = gemini_rollback(thd); if (rc) { - printf("Error in rollback %ld\n",rc); + gemini_msg(pcontext, "Error in rollback %l",rc); } DBUG_RETURN(rc); } @@ -2030,7 +2279,7 @@ int ha_gemini::delete_table(const char *pname) rc = gemini_commit(thd); if (rc) { - printf("Failed to commit transaction %ld\n",rc); + gemini_msg(pcontext, "Failed to commit transaction %l",rc); } @@ -2047,7 +2296,6 @@ int ha_gemini::rename_table(const char *pfrom, const char *pto) THD *thd; dsmContext_t *pcontext; dsmStatus_t rc; - char tabname_buff[FN_REFLEN]; char dbname_buff[FN_REFLEN]; char name_buff[FN_REFLEN]; char newname_buff[FN_REFLEN]; @@ -2056,6 +2304,7 @@ int ha_gemini::rename_table(const char *pfrom, const char *pto) unsigned i, nameLen; dsmObject_t tableNum; dsmArea_t indexArea = 0; + dsmArea_t tableArea = 0; DBUG_ENTER("ha_gemini::rename_table"); @@ -2068,7 +2317,7 @@ int ha_gemini::rename_table(const char *pfrom, const char *pto) { if (gemini_is_vst(name_buff)) { - return 0; + return DSM_S_CANT_RENAME_VST; } } } @@ -2113,21 +2362,51 @@ int ha_gemini::rename_table(const char *pfrom, const char *pto) rc = dsmObjectNameToNum(pcontext, (dsmText_t *)name_buff, &tableNum); if (rc) + { + gemini_msg(pcontext, "Unable to file Table number for %s", name_buff); goto errorReturn; + } rc = dsmObjectRename(pcontext, tableNum, (dsmText_t *)newname_buff, (dsmText_t *)&newidxextname_buff[start_of_name], (dsmText_t *)&newextname_buff[start_of_name], - &indexArea); + &indexArea, &tableArea); if (rc) + { + gemini_msg(pcontext, "Failed to rename %s to %s",name_buff,newname_buff); goto errorReturn; + } + + /* Rename the physical table and index files (if necessary). + ** Close the file, rename it, and reopen it (have to do it this + ** way so rename works on Windows). + */ + if (!(rc = dsmAreaClose(pcontext, tableArea))) + { + if (!(rc = rename_file_ext(pfrom, pto, ha_gemini_ext))) + { + rc = dsmAreaOpen(pcontext, tableArea, 0); + if (rc) + { + gemini_msg(pcontext, "Failed to reopen area %d",tableArea); + } + } + } - /* rename the physical table and index files (if necessary) */ - rc = rename_file_ext(pfrom, pto, ha_gemini_ext); if (!rc && indexArea) { - rc = rename_file_ext(pfrom, pto, ha_gemini_idx_ext); + if (!(rc = dsmAreaClose(pcontext, indexArea))) + { + if (!(rc = rename_file_ext(pfrom, pto, ha_gemini_idx_ext))) + { + rc = dsmAreaOpen(pcontext, indexArea, 0); + if (rc) + { + gemini_msg(pcontext, "Failed to reopen area %d",tableArea); + } + } + } } errorReturn: @@ -2143,17 +2422,38 @@ errorReturn: double ha_gemini::scan_time() { - return records / (gemini_blocksize / table->reclength); + return (double)records / + (double)((gemini_blocksize / (double)table->reclength)); } -int ha_gemini::check(THD* thd, HA_CHECK_OPT* check_opt) +int ha_gemini::analyze(THD* thd, HA_CHECK_OPT* check_opt) { int error; + uint saveIsolation; + dsmMask_t saveLockMode; + + check_opt->quick = true; + check_opt->optimize = true; // Tells check not to get table lock + saveLockMode = lockMode; + saveIsolation = thd->gemini.tx_isolation; + thd->gemini.tx_isolation = ISO_READ_UNCOMMITTED; + lockMode = DSM_LK_NOLOCK; + error = check(thd,check_opt); + lockMode = saveLockMode; + thd->gemini.tx_isolation = saveIsolation; + return (error); +} + +int ha_gemini::check(THD* thd, HA_CHECK_OPT* check_opt) +{ + int error = 0; int checkStatus = HA_ADMIN_OK; ha_rows indexCount; - byte *buf = 0, *indexBuf = 0; + byte *buf = 0, *indexBuf = 0, *prevBuf = 0; int errorCount = 0; + info(HA_STATUS_VARIABLE); // Makes sure row count is up to date + /* Get a shared table lock */ if(thd->gemini.needSavepoint) { @@ -2167,23 +2467,33 @@ int ha_gemini::check(THD* thd, HA_CHECK_OPT* check_opt) return(error); thd->gemini.needSavepoint = 0; } - buf = my_malloc(table->rec_buff_length,MYF(MY_WME)); - indexBuf = my_malloc(table->rec_buff_length,MYF(MY_WME)); + buf = (byte*)my_malloc(table->rec_buff_length,MYF(MY_WME)); + indexBuf = (byte*)my_malloc(table->rec_buff_length,MYF(MY_WME)); + prevBuf = (byte*)my_malloc(table->rec_buff_length,MYF(MY_WME |MY_ZEROFILL )); + /* Lock the table */ - error = dsmObjectLock((dsmContext_t *)thd->gemini.context, - (dsmObject_t)tableNumber, - DSMOBJECT_TABLE,0, - DSM_LK_SHARE, 1, 0); + if (!check_opt->optimize) + error = dsmObjectLock((dsmContext_t *)thd->gemini.context, + (dsmObject_t)tableNumber, + DSMOBJECT_TABLE,0, + DSM_LK_SHARE, 1, 0); if(error) + { + gemini_msg((dsmContext_t *)thd->gemini.context, + "Failed to lock table %d, error %d",tableNumber, error); return error; + } - info(HA_STATUS_VARIABLE); - + ha_rows *rec_per_key = share->rec_per_key; /* If quick option just scan along index converting and counting entries */ for (uint i = 0; i < table->keys; i++) { - key_read = 1; + key_read = 1; // Causes data to be extracted from the keys indexCount = 0; + // Clear the cardinality stats for this index + memset(table->key_info[i].rec_per_key,0, + sizeof(table->key_info[0].rec_per_key[0]) * + table->key_info[i].key_parts); error = index_init(i); error = index_first(indexBuf); while(!error) @@ -2195,8 +2505,12 @@ int ha_gemini::check(THD* thd, HA_CHECK_OPT* check_opt) error = fetch_row(thd->gemini.context,buf); if(!error) { - if(key_cmp(i,buf,indexBuf)) + if(key_cmp(i,buf,indexBuf,false)) { + + gemini_msg((dsmContext_t *)thd->gemini.context, + "Check Error! Key does not match row for rowid %d for index %s", + lastRowid,table->key_info[i].name); print_msg(thd,table->real_name,"check","error", "Key does not match row for rowid %d for index %s", lastRowid,table->key_info[i].name); @@ -2209,6 +2523,9 @@ int ha_gemini::check(THD* thd, HA_CHECK_OPT* check_opt) { errorCount++; checkStatus = HA_ADMIN_CORRUPT; + gemini_msg((dsmContext_t *)thd->gemini.context, + "Check Error! Key does not have a valid row pointer %d for index %s", + lastRowid,table->key_info[i].name); print_msg(thd,table->real_name,"check","error", "Key does not have a valid row pointer %d for index %s", lastRowid,table->key_info[i].name); @@ -2218,10 +2535,27 @@ int ha_gemini::check(THD* thd, HA_CHECK_OPT* check_opt) } } } + + key_cmp(i,indexBuf,prevBuf,true); + bcopy((void *)indexBuf,(void *)prevBuf,table->rec_buff_length); + if(!error) error = index_next(indexBuf); } - + + for(uint j=1; j < table->key_info[i].key_parts; j++) + { + table->key_info[i].rec_per_key[j] += table->key_info[i].rec_per_key[j-1]; + } + for(uint k=0; k < table->key_info[i].key_parts; k++) + { + if (table->key_info[i].rec_per_key[k]) + table->key_info[i].rec_per_key[k] = + records / table->key_info[i].rec_per_key[k]; + *rec_per_key = table->key_info[i].rec_per_key[k]; + rec_per_key++; + } + if(error == HA_ERR_END_OF_FILE) { /* Check count of rows */ @@ -2231,6 +2565,10 @@ int ha_gemini::check(THD* thd, HA_CHECK_OPT* check_opt) /* Number of index entries does not agree with the number of rows in the index. */ checkStatus = HA_ADMIN_CORRUPT; + gemini_msg((dsmContext_t *)thd->gemini.context, + "Check Error! Total rows %d does not match total index entries %d for %s", + records, indexCount, + table->key_info[i].name); print_msg(thd,table->real_name,"check","error", "Total rows %d does not match total index entries %d for %s", records, indexCount, @@ -2248,23 +2586,61 @@ int ha_gemini::check(THD* thd, HA_CHECK_OPT* check_opt) { /* Now scan the table and for each row generate the keys and find them in the index */ - error = fullCheck(thd, buf);\ + error = fullCheck(thd, buf); if(error) checkStatus = error; } + // Store the key distribution information + error = saveKeyStats(thd); error_return: - my_free(buf,MYF(MY_ALLOW_ZERO_PTR)); + my_free((char*)buf,MYF(MY_ALLOW_ZERO_PTR)); + my_free((char*)indexBuf,MYF(MY_ALLOW_ZERO_PTR)); + my_free((char*)prevBuf,MYF(MY_ALLOW_ZERO_PTR)); + index_end(); key_read = 0; - error = dsmObjectUnlock((dsmContext_t *)thd->gemini.context, - (dsmObject_t)tableNumber, - DSMOBJECT_TABLE,0, - DSM_LK_SHARE,0); + if(!check_opt->optimize) + { + error = dsmObjectUnlock((dsmContext_t *)thd->gemini.context, + (dsmObject_t)tableNumber, + DSMOBJECT_TABLE,0, + DSM_LK_SHARE,0); + if (error) + { + gemini_msg((dsmContext_t *)thd->gemini.context, + "Unable to unlock table %d", tableNumber); + } + } return checkStatus; } +int ha_gemini::saveKeyStats(THD *thd) +{ + dsmStatus_t rc = 0; + + /* Insert a row in the indexStats table for each column of + each index of the table */ + + for(uint i = 0; i < table->keys; i++) + { + for (uint j = 0; j < table->key_info[i].key_parts && !rc ;j++) + { + rc = dsmIndexStatsPut((dsmContext_t *)thd->gemini.context, + tableNumber, pindexNumbers[i], + j, (LONG64)table->key_info[i].rec_per_key[j]); + if (rc) + { + gemini_msg((dsmContext_t *)thd->gemini.context, + "Failed to update index stats for table %d, index %d", + tableNumber, pindexNumbers[i]); + } + } + } + return rc; +} + int ha_gemini::fullCheck(THD *thd,byte *buf) { int error; @@ -2319,7 +2695,12 @@ int ha_gemini::repair(THD* thd, HA_CHECK_OPT* check_opt) &thd->gemini.savepoint, DSMTXN_SAVE, 0, 0); if (error) + { + gemini_msg((dsmContext_t *)thd->gemini.context, + "Error setting savepoint number %d, error %d", + thd->gemini.savepoint++, error); return(error); + } thd->gemini.needSavepoint = 0; } @@ -2330,7 +2711,11 @@ int ha_gemini::repair(THD* thd, HA_CHECK_OPT* check_opt) DSMOBJECT_TABLE,0, DSM_LK_EXCL, 1, 0); if(error) + { + gemini_msg((dsmContext_t *)thd->gemini.context, + "Failed to lock table %d, error %d",tableNumber, error); return error; + } error = dsmContextSetLong((dsmContext_t *)thd->gemini.context, DSM_TAGCONTEXT_NO_LOGGING,1); @@ -2338,13 +2723,18 @@ int ha_gemini::repair(THD* thd, HA_CHECK_OPT* check_opt) error = dsmTableReset((dsmContext_t *)thd->gemini.context, (dsmTable_t)tableNumber, table->keys, pindexNumbers); + if (error) + { + gemini_msg((dsmContext_t *)thd->gemini.context, + "dsmTableReset failed for table %d, error %d",tableNumber, error); + } - buf = my_malloc(table->rec_buff_length,MYF(MY_WME)); + buf = (byte*)my_malloc(table->rec_buff_length,MYF(MY_WME)); dsmRecord.table = tableNumber; dsmRecord.recid = 0; dsmRecord.pbuffer = (dsmBuffer_t *)rec_buff; dsmRecord.recLength = table->reclength; - dsmRecord.maxLength = table->reclength; + dsmRecord.maxLength = table->rec_buff_length; while(!error) { error = dsmTableScan((dsmContext_t *)thd->gemini.context, @@ -2352,13 +2742,15 @@ int ha_gemini::repair(THD* thd, HA_CHECK_OPT* check_opt) 1); if(!error) { - unpack_row((char *)buf,(char *)dsmRecord.pbuffer); - error = handleIndexEntries(buf,dsmRecord.recid,KEY_CREATE); - if(error == HA_ERR_FOUND_DUPP_KEY) + if (!(error = unpack_row((char *)buf,(char *)dsmRecord.pbuffer))) { - /* We don't want to stop on duplicate keys -- we're repairing - here so let's get as much repaired as possible. */ - error = 0; + error = handleIndexEntries(buf,dsmRecord.recid,KEY_CREATE); + if(error == HA_ERR_FOUND_DUPP_KEY) + { + /* We don't want to stop on duplicate keys -- we're repairing + here so let's get as much repaired as possible. */ + error = 0; + } } } } @@ -2366,7 +2758,13 @@ int ha_gemini::repair(THD* thd, HA_CHECK_OPT* check_opt) (dsmObject_t)tableNumber, DSMOBJECT_TABLE,0, DSM_LK_EXCL,0); - my_free(buf,MYF(MY_ALLOW_ZERO_PTR)); + if (error) + { + gemini_msg((dsmContext_t *)thd->gemini.context, + "Unable to unlock table %d", tableNumber); + } + + my_free((char*)buf,MYF(MY_ALLOW_ZERO_PTR)); error = dsmContextSetLong((dsmContext_t *)thd->gemini.context, DSM_TAGCONTEXT_NO_LOGGING,0); @@ -2374,6 +2772,313 @@ int ha_gemini::repair(THD* thd, HA_CHECK_OPT* check_opt) return error; } + +int ha_gemini::restore(THD* thd, HA_CHECK_OPT *check_opt) +{ + dsmContext_t *pcontext = (dsmContext_t *)thd->gemini.context; + char* backup_dir = thd->lex.backup_dir; + char src_path[FN_REFLEN], dst_path[FN_REFLEN]; + char* table_name = table->real_name; + int error = 0; + int errornum; + const char* errmsg = ""; + dsmArea_t tableArea = 0; + dsmObjectAttr_t objectAttr; + dsmObject_t associate; + dsmObjectType_t associateType; + dsmDbkey_t block, root; + dsmStatus_t rc; + + rc = dsmObjectInfo(pcontext, tableNumber, DSMOBJECT_MIXTABLE, tableNumber, + &tableArea, &objectAttr, &associateType, &block, &root); + if (rc) + { + error = HA_ADMIN_FAILED; + errmsg = "Failed in dsmObjectInfo (.gmd) (Error %d)"; + errornum = rc; + gemini_msg(pcontext, errmsg ,errornum); + goto err; + } + + rc = dsmAreaFlush(pcontext, tableArea, FLUSH_BUFFERS | FLUSH_SYNC); + if (rc) + { + error = HA_ADMIN_FAILED; + errmsg = "Failed in dsmAreaFlush (.gmd) (Error %d)"; + errornum = rc; + gemini_msg(pcontext, errmsg ,errornum); + goto err; + } + + rc = dsmAreaClose(pcontext, tableArea); + if (rc) + { + error = HA_ADMIN_FAILED; + errmsg = "Failed in dsmAreaClose (.gmd) (Error %d)"; + errornum = rc; + gemini_msg(pcontext, errmsg ,errornum); + goto err; + } + + /* Restore the data file */ + if (!fn_format(src_path, table_name, backup_dir, ha_gemini_ext, 4 + 64)) + { + return HA_ADMIN_INVALID; + } + + if (my_copy(src_path, fn_format(dst_path, table->path, "", + ha_gemini_ext, 4), MYF(MY_WME))) + { + error = HA_ADMIN_FAILED; + errmsg = "Failed in my_copy (.gmd) (Error %d)"; + errornum = errno; + gemini_msg(pcontext, errmsg ,errornum); + goto err; + } + + rc = dsmAreaFlush(pcontext, tableArea, FREE_BUFFERS); + if (rc) + { + error = HA_ADMIN_FAILED; + errmsg = "Failed in dsmAreaFlush (.gmd) (Error %d)"; + errornum = rc; + gemini_msg(pcontext, errmsg ,errornum); + goto err; + } + + rc = dsmAreaOpen(pcontext, tableArea, 1); + if (rc) + { + error = HA_ADMIN_FAILED; + errmsg = "Failed in dsmAreaOpen (.gmd) (Error %d)"; + errornum = rc; + gemini_msg(pcontext, errmsg ,errornum); + goto err; + } + +#ifdef GEMINI_BACKUP_IDX + dsmArea_t indexArea = 0; + + rc = dsmObjectInfo(pcontext, tableNumber, DSMOBJECT_MIXINDEX, &indexArea, + &objectAttr, &associate, &associateType, &block, &root); + if (rc) + { + error = HA_ADMIN_FAILED; + errmsg = "Failed in dsmObjectInfo (.gmi) (Error %d)"; + errornum = rc; + gemini_msg(pcontext, errmsg ,errornum); + goto err; + } + + rc = dsmAreaClose(pcontext, indexArea); + if (rc) + { + error = HA_ADMIN_FAILED; + errmsg = "Failed in dsmAreaClose (.gmi) (Error %d)"; + errornum = rc; + gemini_msg(pcontext, errmsg ,errornum); + goto err; + } + + /* Restore the index file */ + if (!fn_format(src_path, table_name, backup_dir, ha_gemini_idx_ext, 4 + 64)) + { + return HA_ADMIN_INVALID; + } + + if (my_copy(src_path, fn_format(dst_path, table->path, "", + ha_gemini_idx_ext, 4), MYF(MY_WME))) + { + error = HA_ADMIN_FAILED; + errmsg = "Failed in my_copy (.gmi) (Error %d)"; + errornum = errno; + gemini_msg(pcontext, errmsg ,errornum); + goto err; + } + + rc = dsmAreaOpen(pcontext, indexArea, 1); + if (rc) + { + error = HA_ADMIN_FAILED; + errmsg = "Failed in dsmAreaOpen (.gmi) (Error %d)"; + errornum = rc; + gemini_msg(pcontext, errmsg ,errornum); + goto err; + } + + return HA_ADMIN_OK; +#else /* #ifdef GEMINI_BACKUP_IDX */ + HA_CHECK_OPT tmp_check_opt; + tmp_check_opt.init(); + /* The following aren't currently implemented in ha_gemini::repair + ** tmp_check_opt.quick = 1; + ** tmp_check_opt.flags |= T_VERY_SILENT; + */ + return (repair(thd, &tmp_check_opt)); +#endif /* #ifdef GEMINI_BACKUP_IDX */ + + err: + { +#if 0 + /* mi_check_print_error is in ha_myisam.cc, so none of the informative + ** error messages above is currently being printed + */ + MI_CHECK param; + myisamchk_init(¶m); + param.thd = thd; + param.op_name = (char*)"restore"; + param.table_name = table->table_name; + param.testflag = 0; + mi_check_print_error(¶m,errmsg, errornum); +#endif + return error; + } +} + + +int ha_gemini::backup(THD* thd, HA_CHECK_OPT *check_opt) +{ + dsmContext_t *pcontext = (dsmContext_t *)thd->gemini.context; + char* backup_dir = thd->lex.backup_dir; + char src_path[FN_REFLEN], dst_path[FN_REFLEN]; + char* table_name = table->real_name; + int error = 0; + int errornum; + const char* errmsg = ""; + dsmArea_t tableArea = 0; + dsmObjectAttr_t objectAttr; + dsmObject_t associate; + dsmObjectType_t associateType; + dsmDbkey_t block, root; + dsmStatus_t rc; + + rc = dsmObjectInfo(pcontext, tableNumber, DSMOBJECT_MIXTABLE, tableNumber, + &tableArea, &objectAttr, &associateType, &block, &root); + if (rc) + { + error = HA_ADMIN_FAILED; + errmsg = "Failed in dsmObjectInfo (.gmd) (Error %d)"; + errornum = rc; + goto err; + } + + /* Flush the buffers before backing up the table */ + dsmAreaFlush((dsmContext_t *)thd->gemini.context, tableArea, + FLUSH_BUFFERS | FLUSH_SYNC); + if (rc) + { + error = HA_ADMIN_FAILED; + errmsg = "Failed in dsmAreaFlush (.gmd) (Error %d)"; + errornum = rc; + gemini_msg(pcontext, errmsg ,errornum); + goto err; + } + + /* Backup the .FRM file */ + if (!fn_format(dst_path, table_name, backup_dir, reg_ext, 4 + 64)) + { + errmsg = "Failed in fn_format() for .frm file: errno = %d"; + error = HA_ADMIN_INVALID; + errornum = errno; + gemini_msg(pcontext, errmsg ,errornum); + goto err; + } + + if (my_copy(fn_format(src_path, table->path,"", reg_ext, 4), + dst_path, + MYF(MY_WME | MY_HOLD_ORIGINAL_MODES ))) + { + error = HA_ADMIN_FAILED; + errmsg = "Failed copying .frm file: errno = %d"; + errornum = errno; + gemini_msg(pcontext, errmsg ,errornum); + goto err; + } + + /* Backup the data file */ + if (!fn_format(dst_path, table_name, backup_dir, ha_gemini_ext, 4 + 64)) + { + errmsg = "Failed in fn_format() for .GMD file: errno = %d"; + error = HA_ADMIN_INVALID; + errornum = errno; + gemini_msg(pcontext, errmsg ,errornum); + goto err; + } + + if (my_copy(fn_format(src_path, table->path,"", ha_gemini_ext, 4), + dst_path, + MYF(MY_WME | MY_HOLD_ORIGINAL_MODES )) ) + { + errmsg = "Failed copying .GMD file: errno = %d"; + error= HA_ADMIN_FAILED; + errornum = errno; + gemini_msg(pcontext, errmsg ,errornum); + goto err; + } + +#ifdef GEMINI_BACKUP_IDX + dsmArea_t indexArea = 0; + + rc = dsmObjectInfo(pcontext, tableNumber, DSMOBJECT_MIXINDEX, &indexArea, + &objectAttr, &associate, &associateType, &block, &root); + if (rc) + { + error = HA_ADMIN_FAILED; + errmsg = "Failed in dsmObjectInfo (.gmi) (Error %d)"; + errornum = rc; + gemini_msg(pcontext, errmsg ,errornum); + goto err; + } + + /* Backup the index file */ + if (!fn_format(dst_path, table_name, backup_dir, ha_gemini_idx_ext, 4 + 64)) + { + errmsg = "Failed in fn_format() for .GMI file: errno = %d"; + error = HA_ADMIN_INVALID; + errornum = errno; + gemini_msg(pcontext, errmsg ,errornum); + goto err; + } + + if (my_copy(fn_format(src_path, table->path,"", ha_gemini_idx_ext, 4), + dst_path, + MYF(MY_WME | MY_HOLD_ORIGINAL_MODES )) ) + { + errmsg = "Failed copying .GMI file: errno = %d"; + error= HA_ADMIN_FAILED; + errornum = errno; + gemini_msg(pcontext, errmsg ,errornum); + goto err; + } +#endif /* #ifdef GEMINI_BACKUP_IDX */ + + return HA_ADMIN_OK; + + err: + { +#if 0 + /* mi_check_print_error is in ha_myisam.cc, so none of the informative + ** error messages above is currently being printed + */ + MI_CHECK param; + myisamchk_init(¶m); + param.thd = thd; + param.op_name = (char*)"backup"; + param.table_name = table->table_name; + param.testflag = 0; + mi_check_print_error(¶m,errmsg, errornum); +#endif + return error; + } +} + + +int ha_gemini::optimize(THD* thd, HA_CHECK_OPT *check_opt) +{ + return HA_ADMIN_ALREADY_DONE; +} + + ha_rows ha_gemini::records_in_range(int keynr, const byte *start_key,uint start_key_len, enum ha_rkey_function start_search_flag, @@ -2412,7 +3117,7 @@ ha_rows ha_gemini::records_in_range(int keynr, pbracketBase->keyLen = componentLen; } - pbracketBase->keyLen -= 3; + pbracketBase->keyLen -= FULLKEYHDRSZ; if(end_key) { @@ -2431,9 +3136,10 @@ ha_rows ha_gemini::records_in_range(int keynr, pbracketLimit->keyLen = componentLen; } - pbracketLimit->keyLen -= 3; + pbracketLimit->keyLen -= FULLKEYHDRSZ; error = dsmIndexRowsInRange((dsmContext_t *)current_thd->gemini.context, pbracketBase,pbracketLimit, + tableNumber, &pctInrange); if(pctInrange >= 1) rows = (ha_rows)pctInrange; @@ -2457,32 +3163,82 @@ ha_rows ha_gemini::records_in_range(int keynr, may only happen in rows with blobs, as the default row length is pre-allocated. */ -int ha_gemini::pack_row(byte **pprow, int *ppackedLength, const byte *record) +int ha_gemini::pack_row(byte **pprow, int *ppackedLength, const byte *record, + bool update) { + THD *thd = current_thd; + dsmContext_t *pcontext = (dsmContext_t *)thd->gemini.context; + gemBlobDesc_t *pBlobDesc = pBlobDescs; + if (fixed_length_row) { *pprow = (byte *)record; *ppackedLength=(int)table->reclength; return 0; } - if (table->blob_fields) - { - return HA_ERR_WRONG_COMMAND; - } /* Copy null bits */ memcpy(rec_buff, record, table->null_bytes); byte *ptr=rec_buff + table->null_bytes; for (Field **field=table->field ; *field ; field++) - ptr=(byte*) (*field)->pack((char*) ptr,record + (*field)->offset()); + { +#ifdef GEMINI_TINYBLOB_IN_ROW + /* Tiny blobs (255 bytes or less) are stored in the row; larger + ** blobs are stored in a separate storage object (see ha_gemini::create). + */ + if ((*field)->type() == FIELD_TYPE_BLOB && + ((Field_blob*)*field)->blobtype() != FIELD_TYPE_TINY_BLOB) +#else + if ((*field)->type() == FIELD_TYPE_BLOB) +#endif + { + dsmBlob_t gemBlob; + char *blobptr; + + gemBlob.areaType = DSMOBJECT_BLOB; + gemBlob.blobObjNo = tableNumber; + gemBlob.blobId = 0; + gemBlob.totLength = gemBlob.segLength = + ((Field_blob*)*field)->get_length((char*)record + (*field)->offset()); + ((Field_blob*)*field)->get_ptr((char**) &blobptr); + gemBlob.pBuffer = (dsmBuffer_t *)blobptr; + gemBlob.blobContext.blobOffset = 0; + if (gemBlob.totLength) + { + dsmBlobStart(pcontext, &gemBlob); + if (update && pBlobDesc->blobId) + { + gemBlob.blobId = pBlobDesc->blobId; + dsmBlobUpdate(pcontext, &gemBlob, NULL); + } + else + { + dsmBlobPut(pcontext, &gemBlob, NULL); + } + dsmBlobEnd(pcontext, &gemBlob); + } + ptr = (byte*)((Field_blob*)*field)->pack_id((char*) ptr, + (char*)record + (*field)->offset(), (longlong)gemBlob.blobId); + + pBlobDesc++; + } + else + { + ptr=(byte*) (*field)->pack((char*) ptr, (char*)record + (*field)->offset()); + } + } *pprow=rec_buff; *ppackedLength= (ptr - rec_buff); return 0; } -void ha_gemini::unpack_row(char *record, char *prow) +int ha_gemini::unpack_row(char *record, char *prow) { + THD *thd = current_thd; + dsmContext_t *pcontext = (dsmContext_t *)thd->gemini.context; + gemBlobDesc_t *pBlobDesc = pBlobDescs; + if (fixed_length_row) { /* If the table is a VST, the row is in Gemini internal format. @@ -2568,38 +3324,129 @@ void ha_gemini::unpack_row(char *record, char *prow) const char *ptr= (const char*) prow; memcpy(record, ptr, table->null_bytes); ptr+=table->null_bytes; + for (Field **field=table->field ; *field ; field++) - ptr= (*field)->unpack(record + (*field)->offset(), ptr); + { +#ifdef GEMINI_TINYBLOB_IN_ROW + /* Tiny blobs (255 bytes or less) are stored in the row; larger + ** blobs are stored in a separate storage object (see ha_gemini::create). + */ + if ((*field)->type() == FIELD_TYPE_BLOB && + ((Field_blob*)*field)->blobtype() != FIELD_TYPE_TINY_BLOB) +#else + if ((*field)->type() == FIELD_TYPE_BLOB) +#endif + { + dsmBlob_t gemBlob; + + gemBlob.areaType = DSMOBJECT_BLOB; + gemBlob.blobObjNo = tableNumber; + gemBlob.blobId = (dsmBlobId_t)(((Field_blob*)*field)->get_id(ptr)); + if (gemBlob.blobId) + { + gemBlob.totLength = + gemBlob.segLength = ((Field_blob*)*field)->get_length(ptr); + /* Allocate memory to store the blob. This memory is freed + ** the next time unpack_row is called for this table. + */ + gemBlob.pBuffer = (dsmBuffer_t *)my_malloc(gemBlob.totLength, + MYF(0)); + if (!gemBlob.pBuffer) + { + return HA_ERR_OUT_OF_MEM; + } + gemBlob.blobContext.blobOffset = 0; + dsmBlobStart(pcontext, &gemBlob); + dsmBlobGet(pcontext, &gemBlob, NULL); + dsmBlobEnd(pcontext, &gemBlob); + } + else + { + gemBlob.pBuffer = 0; + } + ptr = ((Field_blob*)*field)->unpack_id(record + (*field)->offset(), + ptr, (char *)gemBlob.pBuffer); + pBlobDesc->blobId = gemBlob.blobId; + my_free((char*)pBlobDesc->pBlob, MYF(MY_ALLOW_ZERO_PTR)); + pBlobDesc->pBlob = gemBlob.pBuffer; + pBlobDesc++; + } + else + { + ptr= (*field)->unpack(record + (*field)->offset(), ptr); + } + } } + + return 0; } int ha_gemini::key_cmp(uint keynr, const byte * old_row, - const byte * new_row) + const byte * new_row, bool updateStats) { KEY_PART_INFO *key_part=table->key_info[keynr].key_part; KEY_PART_INFO *end=key_part+table->key_info[keynr].key_parts; - for ( ; key_part != end ; key_part++) + for ( uint i = 0 ; key_part != end ; key_part++, i++) { if (key_part->null_bit) { if ((old_row[key_part->null_offset] & key_part->null_bit) != (new_row[key_part->null_offset] & key_part->null_bit)) + { + if(updateStats) + table->key_info[keynr].rec_per_key[i]++; return 1; + } + else if((old_row[key_part->null_offset] & key_part->null_bit) && + (new_row[key_part->null_offset] & key_part->null_bit)) + /* Both are null */ + continue; } if (key_part->key_part_flag & (HA_BLOB_PART | HA_VAR_LENGTH)) { - - if (key_part->field->cmp_binary(old_row + key_part->offset, - new_row + key_part->offset, + if (key_part->field->cmp_binary((char*)(old_row + key_part->offset), + (char*)(new_row + key_part->offset), (ulong) key_part->length)) + { + if(updateStats) + table->key_info[keynr].rec_per_key[i]++; return 1; + } } else { if (memcmp(old_row+key_part->offset, new_row+key_part->offset, key_part->length)) + { + /* Check for special case of -0 which causes table check + to find an invalid key when comparing the the index + value of 0 to the -0 stored in the row */ + if(key_part->field->type() == FIELD_TYPE_DECIMAL) + { + double fieldValue; + char *ptr = key_part->field->ptr; + + key_part->field->ptr = (char *)old_row + key_part->offset; + fieldValue = key_part->field->val_real(); + if(fieldValue == 0) + { + key_part->field->ptr = (char *)new_row + key_part->offset; + fieldValue = key_part->field->val_real(); + if(fieldValue == 0) + { + key_part->field->ptr = ptr; + continue; + } + } + key_part->field->ptr = ptr; + } + if(updateStats) + { + table->key_info[keynr].rec_per_key[i]++; + } return 1; + } } } return 0; @@ -2612,13 +3459,13 @@ int gemini_parse_table_name(const char *fullname, char *dbname, char *tabname) /* separate out the name of the table and the database */ - namestart = strchr(fullname + start_of_name, '/'); + namestart = (char *)strchr(fullname + start_of_name, '/'); if (!namestart) { /* if on Windows, slashes go the other way */ - namestart = strchr(fullname + start_of_name, '\\'); + namestart = (char *)strchr(fullname + start_of_name, '\\'); } - nameend = strchr(fullname + start_of_name, '.'); + nameend = (char *)strchr(fullname + start_of_name, '.'); /* sometimes fullname has an extension, sometimes it doesn't */ if (!nameend) { @@ -2680,4 +3527,105 @@ static void print_msg(THD *thd, const char *table_name, const char *op_name, thd->killed=1; } +/* Load shared area with rows per key statistics */ +void +ha_gemini::get_index_stats(THD *thd) +{ + dsmStatus_t rc = 0; + ha_rows *rec_per_key = share->rec_per_key; + + for(uint i = 0; i < table->keys && !rc; i++) + { + for (uint j = 0; j < table->key_info[i].key_parts && !rc;j++) + { + LONG64 rows_per_key; + rc = dsmIndexStatsGet((dsmContext_t *)thd->gemini.context, + tableNumber, pindexNumbers[i],(int)j, + &rows_per_key); + if (rc) + { + gemini_msg((dsmContext_t *)thd->gemini.context, + "Index Statistics faild for table %d index %d, error %d", + tableNumber, pindexNumbers[i], rc); + } + *rec_per_key = (ha_rows)rows_per_key; + rec_per_key++; + } + } + return; +} + +/**************************************************************************** + Handling the shared GEM_SHARE structure that is needed to provide + a global in memory storage location of the rec_per_key stats used + by the optimizer. +****************************************************************************/ + +static byte* gem_get_key(GEM_SHARE *share,uint *length, + my_bool not_used __attribute__((unused))) +{ + *length=share->table_name_length; + return (byte*) share->table_name; +} + +static GEM_SHARE *get_share(const char *table_name, TABLE *table) +{ + GEM_SHARE *share; + + pthread_mutex_lock(&gem_mutex); + uint length=(uint) strlen(table_name); + if (!(share=(GEM_SHARE*) hash_search(&gem_open_tables, (byte*) table_name, + length))) + { + ha_rows *rec_per_key; + char *tmp_name; + + if ((share=(GEM_SHARE *) + my_multi_malloc(MYF(MY_WME | MY_ZEROFILL), + &share, sizeof(*share), + &rec_per_key, table->key_parts * sizeof(ha_rows), + &tmp_name, length+1, + NullS))) + { + share->rec_per_key = rec_per_key; + share->table_name = tmp_name; + share->table_name_length=length; + strcpy(share->table_name,table_name); + if (hash_insert(&gem_open_tables, (byte*) share)) + { + pthread_mutex_unlock(&gem_mutex); + my_free((gptr) share,0); + return 0; + } + thr_lock_init(&share->lock); + pthread_mutex_init(&share->mutex,NULL); + } + } + pthread_mutex_unlock(&gem_mutex); + return share; +} + +static int free_share(GEM_SHARE *share, bool mutex_is_locked) +{ + pthread_mutex_lock(&gem_mutex); + if (mutex_is_locked) + pthread_mutex_unlock(&share->mutex); + if (!--share->use_count) + { + hash_delete(&gem_open_tables, (byte*) share); + thr_lock_delete(&share->lock); + pthread_mutex_destroy(&share->mutex); + my_free((gptr) share, MYF(0)); + } + pthread_mutex_unlock(&gem_mutex); + return 0; +} + +static void gemini_lock_table_overflow_error(dsmContext_t *pcontext) +{ + gemini_msg(pcontext, "The total number of locks exceeds the lock table size"); + gemini_msg(pcontext, "Either increase gemini_lock_table_size or use a"); + gemini_msg(pcontext, "different transaction isolation level"); +} + #endif /* HAVE_GEMINI_DB */ diff --git a/sql/ha_gemini.h b/sql/ha_gemini.h index 7e6e8f26588..495dc2fd1c9 100644 --- a/sql/ha_gemini.h +++ b/sql/ha_gemini.h @@ -19,17 +19,26 @@ #pragma interface /* gcc class implementation */ #endif +#include "gem_global.h" #include "dstd.h" #include "dsmpub.h" /* class for the the gemini handler */ enum enum_key_string_options{KEY_CREATE,KEY_DELETE,KEY_CHECK}; - -#define READ_UNCOMMITED 0 -#define READ_COMMITED 1 -#define REPEATABLE_READ 2 -#define SERIALIZEABLE 3 +typedef struct st_gemini_share { + ha_rows *rec_per_key; + THR_LOCK lock; + pthread_mutex_t mutex; + char *table_name; + uint table_name_length,use_count; +} GEM_SHARE; + +typedef struct gemBlobDesc +{ + dsmBlobId_t blobId; + dsmBuffer_t *pBlob; +} gemBlobDesc_t; class ha_gemini: public handler { @@ -38,7 +47,7 @@ class ha_gemini: public handler uint int_option_flag; int tableNumber; dsmIndex_t *pindexNumbers; // dsm object numbers for the indexes on this table - unsigned long lastRowid; + dsmRecid_t lastRowid; uint last_dup_key; bool fixed_length_row, key_read, using_ignore; byte *rec_buff; @@ -46,10 +55,12 @@ class ha_gemini: public handler dsmKey_t *pbracketLimit; dsmKey_t *pfoundKey; dsmMask_t tableStatus; // Crashed/repair status + gemBlobDesc_t *pBlobDescs; int index_open(char *tableName); - int pack_row(byte **prow, int *ppackedLength, const byte *record); - void unpack_row(char *record, char *prow); + int pack_row(byte **prow, int *ppackedLength, const byte *record, + bool update); + int unpack_row(char *record, char *prow); int findRow(THD *thd, dsmMask_t findMode, byte *buf); int fetch_row(void *gemini_context, const byte *buf); int handleIndexEntries(const byte * record, dsmRecid_t recid, @@ -70,24 +81,28 @@ class ha_gemini: public handler void unpack_key(char *record, dsmKey_t *key, uint index); int key_cmp(uint keynr, const byte * old_row, - const byte * new_row); + const byte * new_row, bool updateStats); + int saveKeyStats(THD *thd); + void get_index_stats(THD *thd); short cursorId; /* cursorId of active index cursor if any */ dsmMask_t lockMode; /* Shared or exclusive */ /* FIXFIX Don't know why we need this because I don't know what store_lock method does but we core dump without this */ - THR_LOCK alock; THR_LOCK_DATA lock; + GEM_SHARE *share; + public: ha_gemini(TABLE *table): handler(table), file(0), int_option_flag(HA_READ_NEXT | HA_READ_PREV | HA_REC_NOT_IN_SEQ | HA_KEYPOS_TO_RNDPOS | HA_READ_ORDER | HA_LASTKEY_ORDER | HA_LONGLONG_KEYS | HA_NULL_KEY | HA_HAVE_KEY_READ_ONLY | - HA_NO_BLOBS | HA_NO_TEMP_TABLES | - /* HA_BLOB_KEY | */ /*HA_NOT_EXACT_COUNT | */ + HA_BLOB_KEY | + HA_NO_TEMP_TABLES | HA_NO_FULLTEXT_KEY | + /*HA_NOT_EXACT_COUNT | */ /*HA_KEY_READ_WRONG_STR |*/ HA_DROP_BEFORE_CREATE), pbracketBase(0),pbracketLimit(0),pfoundKey(0), cursorId(0) @@ -100,7 +115,7 @@ class ha_gemini: public handler uint max_record_length() const { return MAXRECSZ; } uint max_keys() const { return MAX_KEY-1; } uint max_key_parts() const { return MAX_REF_PARTS; } - uint max_key_length() const { return MAXKEYSZ; } + uint max_key_length() const { return MAXKEYSZ / 2; } bool fast_key_read() { return 1;} bool has_transactions() { return 1;} @@ -129,8 +144,12 @@ class ha_gemini: public handler void info(uint); int extra(enum ha_extra_function operation); int reset(void); + int analyze(THD* thd, HA_CHECK_OPT* check_opt); int check(THD* thd, HA_CHECK_OPT* check_opt); int repair(THD* thd, HA_CHECK_OPT* check_opt); + int restore(THD* thd, HA_CHECK_OPT* check_opt); + int backup(THD* thd, HA_CHECK_OPT* check_opt); + int optimize(THD* thd, HA_CHECK_OPT* check_opt); int external_lock(THD *thd, int lock_type); virtual longlong get_auto_increment(); void position(byte *record); @@ -139,7 +158,7 @@ class ha_gemini: public handler enum ha_rkey_function start_search_flag, const byte *end_key,uint end_key_len, enum ha_rkey_function end_search_flag); - + void update_create_info(HA_CREATE_INFO *create_info); int create(const char *name, register TABLE *form, HA_CREATE_INFO *create_info); int delete_table(const char *name); @@ -167,6 +186,7 @@ extern long gemini_locktablesize; extern long gemini_lock_wait_timeout; extern long gemini_spin_retries; extern long gemini_connection_limit; +extern char *gemini_basedir; extern TYPELIB gemini_recovery_typelib; extern ulong gemini_recovery_options; @@ -175,12 +195,13 @@ bool gemini_end(void); bool gemini_flush_logs(void); int gemini_commit(THD *thd); int gemini_rollback(THD *thd); +int gemini_recovery_logging(THD *thd, bool on); void gemini_disconnect(THD *thd); int gemini_rollback_to_savepoint(THD *thd); int gemini_parse_table_name(const char *fullname, char *dbname, char *tabname); int gemini_is_vst(const char *pname); int gemini_set_option_long(int optid, long optval); -const int gemini_blocksize = 8192; -const int gemini_recbits = 7; +const int gemini_blocksize = BLKSIZE; +const int gemini_recbits = DEFAULT_RECBITS; diff --git a/sql/handler.cc b/sql/handler.cc index 212fcea11ae..bac24a6dba7 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -694,6 +694,15 @@ void handler::print_error(int error, myf errflag) case HA_ERR_RECORD_FILE_FULL: textno=ER_RECORD_FILE_FULL; break; + case HA_ERR_LOCK_WAIT_TIMEOUT: + textno=ER_LOCK_WAIT_TIMEOUT; + break; + case HA_ERR_LOCK_TABLE_FULL: + textno=ER_LOCK_TABLE_FULL; + break; + case HA_ERR_READ_ONLY_TRANSACTION: + textno=ER_READ_ONLY_TRANSACTION; + break; default: { my_error(ER_GET_ERRNO,errflag,error); @@ -757,6 +766,25 @@ int ha_commit_rename(THD *thd) return error; } +/* Tell the handler to turn on or off logging to the handler's + recovery log +*/ +int ha_recovery_logging(THD *thd, bool on) +{ + int error=0; + + DBUG_ENTER("ha_recovery_logging"); +#ifdef USING_TRANSACTIONS + if (opt_using_transactions) + { +#ifdef HAVE_GEMINI_DB + error = gemini_recovery_logging(thd, on); +#endif + } +#endif + DBUG_RETURN(error); +} + int handler::index_next_same(byte *buf, const byte *key, uint keylen) { int error; diff --git a/sql/handler.h b/sql/handler.h index 076bf783f80..7a28dc07a81 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -74,6 +74,7 @@ #define HA_NOT_DELETE_WITH_CACHE (HA_NOT_READ_AFTER_KEY*2) #define HA_NO_TEMP_TABLES (HA_NOT_DELETE_WITH_CACHE*2) #define HA_NO_PREFIX_CHAR_KEYS (HA_NO_TEMP_TABLES*2) +#define HA_NO_FULLTEXT_KEY (HA_NO_PREFIX_CHAR_KEYS*2) /* Parameters for open() (in register form->filestat) */ /* HA_GET_INFO does a implicit HA_ABORT_IF_LOCKED */ @@ -353,3 +354,4 @@ int ha_autocommit_or_rollback(THD *thd, int error); void ha_set_spin_retries(uint retries); bool ha_flush_logs(void); int ha_commit_rename(THD *thd); +int ha_recovery_logging(THD *thd, bool on); diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index e7a6c52dfd9..373aede7b6b 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -487,7 +487,7 @@ Item_func_if::fix_length_and_dec() { maybe_null=args[1]->maybe_null || args[2]->maybe_null; max_length=max(args[1]->max_length,args[2]->max_length); - decimals=max(args[0]->decimals,args[1]->decimals); + decimals=max(args[1]->decimals,args[2]->decimals); enum Item_result arg1_type=args[1]->result_type(); enum Item_result arg2_type=args[2]->result_type(); if (arg1_type == STRING_RESULT || arg2_type == STRING_RESULT) diff --git a/sql/lock.cc b/sql/lock.cc index 23f81c9c164..1d9aca66e74 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -35,6 +35,7 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table,uint count, bool unlock, TABLE **write_locked); static int lock_external(TABLE **table,uint count); static int unlock_external(THD *thd, TABLE **table,uint count); +static void print_lock_error(int error); MYSQL_LOCK *mysql_lock_tables(THD *thd,TABLE **tables,uint count) @@ -154,7 +155,7 @@ static int lock_external(TABLE **tables,uint count) (*tables)->file->external_lock(thd, F_UNLCK); (*tables)->current_lock=F_UNLCK; } - my_error(ER_CANT_LOCK,MYF(ME_BELL+ME_OLDWIN+ME_WAITTANG),error); + print_lock_error(error); DBUG_RETURN(error); } else @@ -325,7 +326,7 @@ static int unlock_external(THD *thd, TABLE **table,uint count) } } if (error_code) - my_error(ER_CANT_LOCK,MYF(ME_BELL+ME_OLDWIN+ME_WAITTANG),error_code); + print_lock_error(error_code); DBUG_RETURN(error_code); } @@ -480,3 +481,24 @@ bool wait_for_locked_table_names(THD *thd, TABLE_LIST *table_list) } DBUG_RETURN(result); } + +static void print_lock_error(int error) +{ + int textno; + DBUG_ENTER("print_lock_error"); + + switch (error) { + case HA_ERR_LOCK_WAIT_TIMEOUT: + textno=ER_LOCK_WAIT_TIMEOUT; + break; + case HA_ERR_READ_ONLY_TRANSACTION: + textno=ER_READ_ONLY_TRANSACTION; + break; + default: + textno=ER_CANT_LOCK; + break; + } + my_error(textno,MYF(ME_BELL+ME_OLDWIN+ME_WAITTANG),error); + DBUG_VOID_RETURN; +} + diff --git a/sql/mysqlbinlog.cc b/sql/mysqlbinlog.cc index f0a9692cc2d..c234e2421bf 100644 --- a/sql/mysqlbinlog.cc +++ b/sql/mysqlbinlog.cc @@ -303,14 +303,12 @@ static void dump_remote_log_entries(const char* logname) uint len; NET* net = &mysql->net; if(!position) position = 4; // protect the innocent from spam - if(position < 4) - { - position = 4; - // warn the guity - fprintf(stderr, - "Warning: with the position so small you would hit the magic number\n\ -Unfortunately, no sweepstakes today, adjusted position to 4\n"); - } + if (position < 4) + { + position = 4; + // warn the guity + sql_print_error("Warning: The position in the binary log can't be less than 4.\nStarting from position 4\n"); + } int4store(buf, position); int2store(buf + 4, binlog_flags); len = (uint) strlen(logname); diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 1e4a96d2196..590e59f9a7d 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -3079,8 +3079,12 @@ static void usage(void) --console Don't remove the console window\n\ --install Install mysqld as a service (NT)\n\ --remove Remove mysqld from the service list (NT)\n\ - --standalone Dummy option to start as a standalone program (NT)\n\ + --standalone Dummy option to start as a standalone program (NT)\ "); +#ifdef USE_SYMDIR + puts("--use-symbolic-links Enable symbolic link support"); +#endif + puts(""); #endif #ifdef HAVE_BERKELEY_DB puts("\ diff --git a/sql/share/czech/errmsg.txt b/sql/share/czech/errmsg.txt index 666d70c957a..35a428273c7 100644 --- a/sql/share/czech/errmsg.txt +++ b/sql/share/czech/errmsg.txt @@ -215,3 +215,6 @@ "Could not create slave thread, check system resources", "User %-.64s has already more than 'max_user_connections' active connections", "You may only use constant expressions with SET", +"Lock wait timeout exceeded", +"The total number of locks exceeds the lock table size", +"Update locks cannot be acquired during a READ UNCOMMITTED transaction", diff --git a/sql/share/danish/errmsg.txt b/sql/share/danish/errmsg.txt index 9f1f6accc1f..b2fe6c4e800 100644 --- a/sql/share/danish/errmsg.txt +++ b/sql/share/danish/errmsg.txt @@ -209,3 +209,6 @@ "Could not create slave thread, check system resources", "User %-.64s has already more than 'max_user_connections' active connections", "You may only use constant expressions with SET", +"Lock wait timeout exceeded", +"The total number of locks exceeds the lock table size", +"Update locks cannot be acquired during a READ UNCOMMITTED transaction", diff --git a/sql/share/dutch/errmsg.txt b/sql/share/dutch/errmsg.txt index 8b44af7eb7b..616f832bee8 100644 --- a/sql/share/dutch/errmsg.txt +++ b/sql/share/dutch/errmsg.txt @@ -206,3 +206,6 @@ "Could not create slave thread, check system resources", "User %-.64s has already more than 'max_user_connections' active connections", "You may only use constant expressions with SET", +"Lock wait timeout exceeded", +"The total number of locks exceeds the lock table size", +"Update locks cannot be acquired during a READ UNCOMMITTED transaction", diff --git a/sql/share/english/errmsg.txt b/sql/share/english/errmsg.txt index 30e8894db08..6f423f879f8 100644 --- a/sql/share/english/errmsg.txt +++ b/sql/share/english/errmsg.txt @@ -206,8 +206,8 @@ "Could not create slave thread, check system resources", "User %-.64s has already more than 'max_user_connections' active connections", "You may only use constant expressions with SET", +"Lock wait timeout exceeded", +"The total number of locks exceeds the lock table size", +"Update locks cannot be acquired during a READ UNCOMMITTED transaction", "Error connecting to master: %-.128s", "Error running query on master: %-.128s", - - - diff --git a/sql/share/estonian/errmsg.txt b/sql/share/estonian/errmsg.txt index e807c8d4a64..032806f73f8 100644 --- a/sql/share/estonian/errmsg.txt +++ b/sql/share/estonian/errmsg.txt @@ -210,3 +210,6 @@ "Could not create slave thread, check system resources", "User %-.64s has already more than 'max_user_connections' active connections", "You may only use constant expressions with SET", +"Lock wait timeout exceeded", +"The total number of locks exceeds the lock table size", +"Update locks cannot be acquired during a READ UNCOMMITTED transaction", diff --git a/sql/share/french/errmsg.txt b/sql/share/french/errmsg.txt index 5cbcfe81b87..aadfecbc8a1 100644 --- a/sql/share/french/errmsg.txt +++ b/sql/share/french/errmsg.txt @@ -206,3 +206,6 @@ "Could not create slave thread, check system resources", "User %-.64s has already more than 'max_user_connections' active connections", "You may only use constant expressions with SET", +"Lock wait timeout exceeded", +"The total number of locks exceeds the lock table size", +"Update locks cannot be acquired during a READ UNCOMMITTED transaction", diff --git a/sql/share/german/errmsg.txt b/sql/share/german/errmsg.txt index 307ed7a00f4..7a86a4368e7 100644 --- a/sql/share/german/errmsg.txt +++ b/sql/share/german/errmsg.txt @@ -209,3 +209,6 @@ "Could not create slave thread, check system resources", "User %-.64s has already more than 'max_user_connections' active connections", "You may only use constant expressions with SET", +"Lock wait timeout exceeded", +"The total number of locks exceeds the lock table size", +"Update locks cannot be acquired during a READ UNCOMMITTED transaction", diff --git a/sql/share/greek/errmsg.txt b/sql/share/greek/errmsg.txt index 119de63b2a7..5022bb65792 100644 --- a/sql/share/greek/errmsg.txt +++ b/sql/share/greek/errmsg.txt @@ -206,3 +206,6 @@ "Could not create slave thread, check system resources", "User %-.64s has already more than 'max_user_connections' active connections", "You may only use constant expressions with SET", +"Lock wait timeout exceeded", +"The total number of locks exceeds the lock table size", +"Update locks cannot be acquired during a READ UNCOMMITTED transaction", diff --git a/sql/share/hungarian/errmsg.txt b/sql/share/hungarian/errmsg.txt index 7e9b9e6a3bf..cfdd4b7fe75 100644 --- a/sql/share/hungarian/errmsg.txt +++ b/sql/share/hungarian/errmsg.txt @@ -208,3 +208,6 @@ "Could not create slave thread, check system resources", "User %-.64s has already more than 'max_user_connections' active connections", "You may only use constant expressions with SET", +"Lock wait timeout exceeded", +"The total number of locks exceeds the lock table size", +"Update locks cannot be acquired during a READ UNCOMMITTED transaction", diff --git a/sql/share/italian/errmsg.txt b/sql/share/italian/errmsg.txt index d6c857d44a4..d1b17bc8f2e 100644 --- a/sql/share/italian/errmsg.txt +++ b/sql/share/italian/errmsg.txt @@ -206,3 +206,6 @@ "Could not create slave thread, check system resources", "User %-.64s has already more than 'max_user_connections' active connections", "You may only use constant expressions with SET", +"Lock wait timeout exceeded", +"The total number of locks exceeds the lock table size", +"Update locks cannot be acquired during a READ UNCOMMITTED transaction", diff --git a/sql/share/japanese/errmsg.txt b/sql/share/japanese/errmsg.txt index a62f22c253d..9dfe9bb3efb 100644 --- a/sql/share/japanese/errmsg.txt +++ b/sql/share/japanese/errmsg.txt @@ -208,3 +208,6 @@ "Could not create slave thread, check system resources", "User %-.64s has already more than 'max_user_connections' active connections", "You may only use constant expressions with SET", +"Lock wait timeout exceeded", +"The total number of locks exceeds the lock table size", +"Update locks cannot be acquired during a READ UNCOMMITTED transaction", diff --git a/sql/share/korean/errmsg.txt b/sql/share/korean/errmsg.txt index c476ad8fa3c..4f0f90f88ce 100644 --- a/sql/share/korean/errmsg.txt +++ b/sql/share/korean/errmsg.txt @@ -206,3 +206,6 @@ "Could not create slave thread, check system resources", "User %-.64s has already more than 'max_user_connections' active connections", "You may only use constant expressions with SET", +"Lock wait timeout exceeded", +"The total number of locks exceeds the lock table size", +"Update locks cannot be acquired during a READ UNCOMMITTED transaction", diff --git a/sql/share/norwegian-ny/errmsg.txt b/sql/share/norwegian-ny/errmsg.txt index 2a57c93cc84..99238d61e3e 100644 --- a/sql/share/norwegian-ny/errmsg.txt +++ b/sql/share/norwegian-ny/errmsg.txt @@ -208,3 +208,6 @@ "Could not create slave thread, check system resources", "User %-.64s has already more than 'max_user_connections' active connections", "You may only use constant expressions with SET", +"Lock wait timeout exceeded", +"The total number of locks exceeds the lock table size", +"Update locks cannot be acquired during a READ UNCOMMITTED transaction", diff --git a/sql/share/norwegian/errmsg.txt b/sql/share/norwegian/errmsg.txt index cf23991eefa..473d297b649 100644 --- a/sql/share/norwegian/errmsg.txt +++ b/sql/share/norwegian/errmsg.txt @@ -208,3 +208,6 @@ "Could not create slave thread, check system resources", "User %-.64s has already more than 'max_user_connections' active connections", "You may only use constant expressions with SET", +"Lock wait timeout exceeded", +"The total number of locks exceeds the lock table size", +"Update locks cannot be acquired during a READ UNCOMMITTED transaction", diff --git a/sql/share/polish/errmsg.txt b/sql/share/polish/errmsg.txt index 03e9d59dacd..253d4afd2b7 100644 --- a/sql/share/polish/errmsg.txt +++ b/sql/share/polish/errmsg.txt @@ -210,3 +210,6 @@ "Could not create slave thread, check system resources", "User %-.64s has already more than 'max_user_connections' active connections", "You may only use constant expressions with SET", +"Lock wait timeout exceeded", +"The total number of locks exceeds the lock table size", +"Update locks cannot be acquired during a READ UNCOMMITTED transaction", diff --git a/sql/share/portuguese/errmsg.txt b/sql/share/portuguese/errmsg.txt index 37f2bf9e7ac..ba010a20710 100644 --- a/sql/share/portuguese/errmsg.txt +++ b/sql/share/portuguese/errmsg.txt @@ -206,3 +206,6 @@ "Could not create slave thread, check system resources", "User %-.64s has already more than 'max_user_connections' active connections", "You may only use constant expressions with SET", +"Lock wait timeout exceeded", +"The total number of locks exceeds the lock table size", +"Update locks cannot be acquired during a READ UNCOMMITTED transaction", diff --git a/sql/share/romanian/errmsg.txt b/sql/share/romanian/errmsg.txt index 6bc2695bed5..384df0c864e 100644 --- a/sql/share/romanian/errmsg.txt +++ b/sql/share/romanian/errmsg.txt @@ -210,3 +210,6 @@ "Could not create slave thread, check system resources", "User %-.64s has already more than 'max_user_connections' active connections", "You may only use constant expressions with SET", +"Lock wait timeout exceeded", +"The total number of locks exceeds the lock table size", +"Update locks cannot be acquired during a READ UNCOMMITTED transaction", diff --git a/sql/share/russian/errmsg.txt b/sql/share/russian/errmsg.txt index 75d21dda888..7dd24c743bb 100644 --- a/sql/share/russian/errmsg.txt +++ b/sql/share/russian/errmsg.txt @@ -209,3 +209,6 @@ "îÅ ÍÏÇÕ ÓÏÚÄÁÔØ ÐÒÏÃÅÓÓ SLAVE, ÐÒÏ×ÅÒØÔÅ ÓÉÓÔÅÍÎÙÅ ÒÅÓÕÒÓÙ", "User %-.64s has already more than 'max_user_connections' active connections", "You may only use constant expressions with SET", +"Lock wait timeout exceeded", +"The total number of locks exceeds the lock table size", +"Update locks cannot be acquired during a READ UNCOMMITTED transaction", diff --git a/sql/share/slovak/errmsg.txt b/sql/share/slovak/errmsg.txt index 673499f5105..2a6063b6aee 100644 --- a/sql/share/slovak/errmsg.txt +++ b/sql/share/slovak/errmsg.txt @@ -214,3 +214,6 @@ "Could not create slave thread, check system resources", "User %-.64s has already more than 'max_user_connections' active connections", "You may only use constant expressions with SET", +"Lock wait timeout exceeded", +"The total number of locks exceeds the lock table size", +"Update locks cannot be acquired during a READ UNCOMMITTED transaction", diff --git a/sql/share/spanish/errmsg.txt b/sql/share/spanish/errmsg.txt index d470556fd58..dbf7caf585d 100644 --- a/sql/share/spanish/errmsg.txt +++ b/sql/share/spanish/errmsg.txt @@ -207,3 +207,6 @@ "Could not create slave thread, check system resources", "User %-.64s has already more than 'max_user_connections' active connections", "You may only use constant expressions with SET", +"Lock wait timeout exceeded", +"The total number of locks exceeds the lock table size", +"Update locks cannot be acquired during a READ UNCOMMITTED transaction", diff --git a/sql/share/swedish/errmsg.txt b/sql/share/swedish/errmsg.txt index 672ce97c575..fc26a08e9ee 100644 --- a/sql/share/swedish/errmsg.txt +++ b/sql/share/swedish/errmsg.txt @@ -206,3 +206,6 @@ "Kunde inte starta en tråd för replikering", "Användare '%-.64s' har redan 'max_user_connections' aktiva inloggningar", "Du kan endast använda konstant-uttryck med SET", +"Lock wait timeout exceeded", +"The total number of locks exceeds the lock table size", +"Update locks cannot be acquired during a READ UNCOMMITTED transaction", diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 077de271e04..83fd02c7ffe 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1375,11 +1375,6 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type) bool refresh; DBUG_ENTER("open_ltable"); -#ifdef __WIN__ - /* Win32 can't drop a file that is open */ - if (lock_type == TL_WRITE_ALLOW_READ) - lock_type= TL_WRITE; -#endif thd->proc_info="Opening table"; while (!(table=open_table(thd,table_list->db ? table_list->db : thd->db, table_list->real_name,table_list->name, @@ -1387,6 +1382,19 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type) if (table) { int error; + +#ifdef __WIN__ + /* Win32 can't drop a file that is open */ + if (lock_type == TL_WRITE_ALLOW_READ +#ifdef HAVE_GEMINI_DB + && table->db_type != DB_TYPE_GEMINI +#endif /* HAVE_GEMINI_DB */ + ) + { + lock_type= TL_WRITE; + } +#endif /* __WIN__ */ + table_list->table=table; table->grant= table_list->grant; if (thd->locked_tables) diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index fa5b599c2f8..74a3549149b 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -377,10 +377,9 @@ void mysql_binlog_send(THD* thd, char* log_ident, ulong pos, ushort flags) if ((file=open_binlog(&log, log_file_name, &errmsg)) < 0) goto err; - if(pos < 4) + if (pos < 4) { - errmsg = "Congratulations! You have hit the magic number and can win \ -sweepstakes if you report the bug"; + errmsg = "Client requested master to start repliction from impossible position.\n"; goto err; } diff --git a/sql/sql_select.h b/sql/sql_select.h index bb9bb374c76..0ec1854d641 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -138,8 +138,11 @@ class TMP_TABLE_PARAM { } inline void cleanup(void) { - delete [] copy_field; - copy_field=0; + if (copy_field) /* Fix for Intel compiler */ + { + delete [] copy_field; + copy_field=0; + } } }; diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 89699655ab9..c23c784ec14 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -423,6 +423,13 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, column->field_name); DBUG_RETURN(-1); } + if (key->type == Key::FULLTEXT && + (file->option_flag() & HA_NO_FULLTEXT_KEY)) + { + my_printf_error(ER_WRONG_KEY_COLUMN, ER(ER_WRONG_KEY_COLUMN), MYF(0), + column->field_name); + DBUG_RETURN(-1); + } if (f_is_blob(sql_field->pack_flag)) { if (!(file->option_flag() & HA_BLOB_KEY)) @@ -1690,6 +1697,16 @@ copy_data_between_tables(TABLE *from,TABLE *to, goto err; }; + /* Turn off recovery logging since rollback of an + alter table is to delete the new table so there + is no need to log the changes to it. */ + error = ha_recovery_logging(thd,false); + if(error) + { + error = 1; + goto err; + } + init_read_record(&info, thd, from, (SQL_SELECT *) 0, 1,1); if (handle_duplicates == DUP_IGNORE || handle_duplicates == DUP_REPLACE) @@ -1735,6 +1752,7 @@ copy_data_between_tables(TABLE *from,TABLE *to, if (to->file->activate_all_index(thd)) error=1; + tmp_error = ha_recovery_logging(thd,true); /* Ensure that the new table is saved properly to disk so that we can do a rename @@ -1746,6 +1764,7 @@ copy_data_between_tables(TABLE *from,TABLE *to, if (to->file->external_lock(thd,F_UNLCK)) error=1; err: + tmp_error = ha_recovery_logging(thd,true); free_io_cache(from); *copied= found_count; *deleted=delete_count; diff --git a/support-files/mysql-multi.server.sh b/support-files/mysql-multi.server.sh index af13009d038..6c940630427 100644 --- a/support-files/mysql-multi.server.sh +++ b/support-files/mysql-multi.server.sh @@ -65,7 +65,7 @@ parse_arguments() { done } -# Get arguments from the my.cfg file, groups [mysqld], [mysql_server], +# Get arguments from the my.cnf file, groups [mysqld], [mysql_server], # and mysql_multi_server if test -x ./bin/my_print_defaults then diff --git a/support-files/mysql.server.sh b/support-files/mysql.server.sh index 9307a2e3eb2..62381ccf0d3 100644 --- a/support-files/mysql.server.sh +++ b/support-files/mysql.server.sh @@ -53,7 +53,7 @@ parse_arguments() { done } -# Get arguments from the my.cfg file, groups [mysqld] and [mysql_server] +# Get arguments from the my.cnf file, groups [mysqld] and [mysql_server] if test -x ./bin/my_print_defaults then print_defaults="./bin/my_print_defaults" |