diff options
Diffstat (limited to 'sql')
164 files changed, 34961 insertions, 20793 deletions
diff --git a/sql/ChangeLog b/sql/ChangeLog deleted file mode 100644 index 2289765afad..00000000000 --- a/sql/ChangeLog +++ /dev/null @@ -1,3302 +0,0 @@ -2000-11-11 Jeremy Cole <jeremy@mysql.com> - -* Added ALTER TABLE ... ORDER BY ... - -2000-09-17 Michael Widenius <monty@mysql.com> - -* Added option QUICK to DELETE to tell MySQL not to balance the - trees on delete. - -2000-09-15 Michael Widenius <monty@mysql.com> - -* Added a thd argument to log::write() to get more speed. - -2000-09-01 Michael Widenius <monty@mysql.com> - -* Avoid allocation of "localhost" string. -* Changed that TIMESTAMP(X) is sometimes as string -* Release of 3.23.23 - -2000-08-21 Michael Widenius <monty@mysql.com> - -* Added RENAME TABLE. - -2000-08-20 Michael Widenius <monty@mysql.com> - -* Added memory as inline functions to THD to get them a bit faster -* Don't count entries with NULL in COUNT(DISTINCT ..) - -2000-08-08 Michael Widenius <monty@mysql.com> - -* Changed ALTER TABLE and LOAD DATA INFILE to create non unique, small keys - after all rows are inserted. -* Fixed use of UDF function with const arguments in WHERE clause. - -2000-07-11 Michael Widenius <monty@mysql.com> - -* Extended safe_mysqld; Patch by Christian Hammers - -2000-07-04 Michael Widenius <monty@mysql.com> - -* Changed the update log to take the query length argument; This - should make the update log \0 safe. - -2000-06-21 Michael Widenius <monty@mysql.com> - -* Added net_read_timeout and net_write_timeout as startup parameters to mysqld - -2000-06-19 Michael Widenius <monty@mysql.com> - -* Changed the copyright of MySQL to GPL and LGPL. -* Added myisampack and pack_isam to the MySQL distribution. - -2000-06-01 Michael Widenius <monty@mysql.com> - -* Added "FLUSH TABLES WITH READ LOCK" command - -2000-05-31 Michael Widenius <monty@mysql.com> - -* Added table locks to Berkeley DB. - -2000-05-28 Michael Widenius <monty@mysql.com> - -* Allow ON and USING with INNER JOIN. - -2000-05-23 Sasha - -* Fix that USE INDEX with 'PRIMARY' keys works. - -2000-05-20 Michael Widenius <monty@tik.pp.sci.fi> - -* Added symbolic links support for Win32. - -2000-05-19 Michael Widenius <monty@tik.pp.sci.fi> - -* Changed protocol to let client know if the server is in AUTOCOMMIT mode - and if there is a pending transaction. If there is a pending transaction - the client library will give an error before reconnecting to the server to - let the client know that the server did a rollback. - The protocol is still backward compatible with old clients -* 'kill' now works on a thread that is locked on a 'write' to a dead client. - -2000-05-18 Michael Widenius <monty@tik.pp.sci.fi> - -* unlock tables before sending last packet of SELECT ... result to client. - -2000-05-16 Michael Widenius <monty@mysql.com> - -* split Item_bool_func2::compare function into individual functions to get more - speed. -* Small optimizations of do_select() to get more speed. - -2000-05-15 Michael Widenius <monty@mysql.com> - -* CHECK will update the statistics for the keys. -* Fixed bug in REPAIR TABLE when the table was used by other threads. - -2000-05-11 Michael Widenius <monty@mysql.com> - -* put CREATE TEMPORARY TABLE commands in the update log. -* UPDATE IGNORE will not abort if an update gives a DUPLICATE_KEY error. -* Ensure that all fn_format() or unpack_filename() is called for all - generated filenames. - -2000-05-05 Michael Widenius <monty@mysql.com> - -* Added timzone variable to SHOW VARIABLES. - -2000-05-04 Michael Widenius <monty@mysql.com> - -* Don't write INSERT DELAYED to update log if SQL_LOG_UPDATE=0 - -2000-05-03 Michael Widenius <monty@mysql.com> - -* Fixed problem with REPLACE on HEAP tables. - -2000-04-26 Michael Widenius <monty@mysql.com> - -* Added 'Writing to net' and 'Reading from net' as 'status' to - mysqladmin processlist. - -2000-04-23 Michael Widenius <monty@mysql.com> - -* Added CREATE DATABASE and DROP DATABASE to the update log -* Fixed problem when doing a GROUP BY on an enum column with MANY - value combinations. -* Avoid sorting for some simple GROUP BY queries. - -2000-04-22 Michael Widenius <monty@mysql.com> - -* Fixed problems in update log when using LAST_INSERT_ID() to update - an table with an auto_increment key. -* New function: 'NULLIF(expr1,expr2)' - -2000-04-14 Michael Widenius <monty@tik.pp.sci.fi> - -* UPDATE and DELETE on UNIQUE keys, where the whole key is used in the WHERE - part, are now faster than before. - -* Added optimisation to skip ORDER BY parts where the order by column - is a constant expression in the WHERE part. ORDER BY will also - be skipped if ORDER BY matches a key where the key parts that are - missing in the ORDER BY is constants: - - In the following case the ORDER BY will be removed: - SELECT * FROM foo WHERE column=constant ORDER BY column; - - In the following case the first key will be used to solve the ORDER BY: - SELECT * FROM foo WHERE key_part1=const ORDER BY key_part2; - -2000-04-10 Michael Widenius <monty@mysql.com> - -* Changed mysql.server to wait until the pid file is deleted on stop -* Clients will automaticly be set to the same character set as the - server if one hasn't specified a character set with mysql_option() - or in the my.cnf files. - -2000-04-09 Michael Widenius <monty@mysql.com> - -* Release of 3.23.14 -* Fixed bug where complex CONCAT() use returned the wrong result. - -2000-04-07 Michael Widenius <monty@mysql.com> - -* Added some optimization to LIMIT when the used KEY matches almost all - rows in the table. (SELECT * from table where key_column > "a" LIMIT 1). -* REPLACE now honors the LOW_PRIORITY_UPDATES flag. - -2000-04-05 Michael Widenius <monty@mysql.com> - -* Fixed that DROP TABLE is logged in the update log. -* Fixed problem when searching on DECIMAL() key field - where the column data contained pre-zeros. - -2000-04-02 Michael Widenius <monty@mysql.com> - -* Fix bug in myisamchk when the auto_increment isn't the first key. - -2000-03-30 "Thimble Smith" <tim@mysql.com> - -* Allow DATETIME in ISO8601 format: 2000-03-12T12:00:00 - -2000-03-30 Michael Widenius <monty@mysql.com> - -* Added UMASK_DIR environment variable. -* Fixed problem with seek and RAID tables. - -2000-03-29 Michael Widenius <monty@mysql.com> - -* slow_queries log wasn't flushed on FLUSH LOGS. - -2000-03-28 Michael Widenius <monty@mysql.com> - -* Fix that DELETE FROM table_name; works on RAID tables. -* Fix that DROP DATABASE works correctly with RAID tables. - -2000-03-27 Michael Widenius <monty@mysql.com> - -* Added function CONNECTION_ID() - -2000-03-26 Michael Widenius <monty@mysql.com> - -* When using = on BLOB or VARCHAR BINARY keys where only a part of the column - was indexed, the whole column of the result row wasn't compared. - -2000-03-24 takeshi@SoftAgency.co.jp - -* Fix for sjis & order by - -2000-03-23 Michael Widenius <monty@mysql.com> - -* Added patches to allow client library to compile on BEOS. - -2000-03-16 Michael Widenius <monty@monty.pp.sci.fi> - -* Added STDCALL to some functions in libmysql that missed this. -* When running in ANSI mode, don't allow one to use columns that isn't in - the GROUP BY part. - -2000-03-14 Michael Widenius <monty@monty.pp.sci.fi> - -* Release of 3.23.13 -* Removed end space from double/float numbers in results from temporary - tables. - -2000-03-13 Michael Widenius <monty@monty.pp.sci.fi> - -* Fixed automatic removal of table locks if doing a DROP TABLE on the last - locked table. - -2000-03-13 Sasha - -* Added CHECK TABLE command. - -2000-03-12 Michael Widenius <monty@monty.pp.sci.fi> - -* Changed int2str and longlong2str to use the optimized - int10_to_str / longlong10_to_str functions. - -2000-03-09 Michael Widenius <monty@monty.pp.sci.fi> - -* Fixed bug so that mysqladmin shutdown will wait for the local server to close - down. -* Fixed a possible endless loop when calculating timestamp. - -2000-02-27 Michael Widenius <monty@monty.pp.sci.fi> - -* Release of 3.23.12 -* Changed that mysql_ping() doesn't increment the 'questions' status variable. -* Fixed that mysql -D database doesn't kill 'mysql'. - -2000-02-24 Michael Widenius <monty@monty.pp.sci.fi> - -* Only allow SHOW GRANTS if you have a privilege on the mysql - tables -* Fixed bug when storing 0 into a timestamp. - -2000-02-23 Michael Widenius <monty@monty.pp.sci.fi> - -* When doing mysqladmin shutdown on a local connection, mysqladmin now - waits until the pidfile is gone before doing an shutdown. -* Changed that the pid file is not removed until all threads have died. - -2000-02-23 Matt Wagner - -* When doing mysqladmin shutdown on a local connection, mysqladmin now - waits until the pidfile is gone before doing an shutdown. - -2000-02-23 Michael Widenius <monty@monty.pp.sci.fi> - -* Fixed problem with LEFT JOIN and key_field IS NULL. - -2000-02-23 Sasha - -* Fixed core dump with some COUNT(DISTINCT ...) queries. - -2000-02-21 Michael Widenius <monty@monty.pp.sci.fi> - -* Added options USE KEYS (key_list) and IGNORE KEYS (key_list) as - join parameters in SELECT. -* DROP of table is now done through the handler. - -2000-02-17 Michael Widenius <monty@monty.pp.sci.fi> - -* Added ANSI SQL syntax ALTER TABLE ADD (column_def1, column_def2 ...) - -2000-02-16 Michael Widenius <monty@monty.pp.sci.fi> - -* Fixed problem with optimizer that could sometimes use wrong keys - -2000-02-15 Michael Widenius <monty@monty.pp.sci.fi> - -* Fixed that GRANT/REVOKE ALL PRIVILEGES doesn't affect GRANT OPTION -* Removed extra ) from the output of SHOW GRANTS - -2000-02-13 Michael Widenius <monty@monty.pp.sci.fi> - -* Fixed problem when storing numbers in timestamps. -* Fix problem with timezones that has half hour offsets. -* Storage of numbers in timestamp columns are now faster. - -2000-02-11 Michael Widenius <monty@monty.pp.sci.fi> - -* Allow the syntax UNIQUE INDEX in CREATE statements. - -2000-02-10 Michael Widenius <monty@monty.pp.sci.fi> - -* Added options --i-am-a-dummy and --safe-updates to mysql.cc -* Added variables select_limit and max_join_size to mysql.cc -* Added sql variables: SQL_MAX_JOIN_SIZE and SQL_SAFE_UPDATES -* Added READ_LOCAL lock that doesn't lock the table for concurrent inserts -* Changed that LOCK TABLES .. READ doesn't anymore allow concurrent inserts -* Added option --skip-delay-key-write to mysqld. - -2000-02-09 Michael Widenius <monty@monty.pp.sci.fi> - -* Fixed security problem in the protocol regarding password checking. - -2000-02-03 Michael Widenius <monty@tik.pp.sci.fi> - -* Allow 'end' as a field name. -* Added _rowid as an alias for an auto_increment column. - -2000-01-28 Michael Widenius <monty@monty.pp.sci.fi> - -* Ignore empty queries in mysql when running in batch mode - (To be able to handle rows with double ';' chars). - -2000-01-27 Michael Widenius <monty@monty.pp.sci.fi> - -* Fixed problem with timestamps and INSERT DELAYED - -2000-01-26 Michael Widenius <monty@tik.pp.sci.fi> - -* Fixed problem that affected queries that did arithmetic on GROUP functions. - -2000-01-24 Michael Widenius <monty@tik.pp.sci.fi> - -* Don't give a unnecessary GRANT error when using tables from many - databases in the same query. - -2000-01-20 Michael Widenius <monty@tik.pp.sci.fi> - -* Fixed that 'date_column BETWEEN const_date AND const_date' works. - -2000-01-19 Michael Widenius <monty@monty.pp.sci.fi> - -* Fixed problem when only changing a 0 to NULL in a table with BLOB/TEXT - columns. -* Fixed bug in range optimizer when using many key parts and or on the middle - key parts: WHERE K1=1 and K3=2 and (K2=2 and K4=4 or K2=3 and K4=5) -* Added the virtual VIO interface to the mysql connection streams. - (This will make it possible to use SSL through the VIO classe interface) - -2000-01-14 Michael Widenius <monty@monty.pp.sci.fi> - -* Added command 'source' to mysql to allow reading of batch files inside - mysql. Original patch by Matthew Vanecek. - -2000-01-12 Michael Widenius <monty@monty.pp.sci.fi> - -* Fixed bug that a change of all VARCHAR columns to CHAR columns didn't change - row type from dynamic to fixed. - -2000-01-11 Michael Widenius <monty@monty.pp.sci.fi> - -* Added print of default arguments options to all clients. -* Added --log-slow-queries to mysqld to log all queries that takes a - long time to a separate log file with a time of how long the query took. -* Fixed critical problem with the WITH GRANT OPTION option. -* Added read-next-on-key to HEAP tables. This should fix all - problems with HEAP tables when using not UNIQUE keys. -* Disabled floating point exceptions for FreeBSD to fix core dump when - doing SELECT floor(pow(2,63)); - -2000-01-07 Michael Widenius <monty@monty.pp.sci.fi> - -* Fixed core dump when doing WHERE key_column=RAND(...) - (Note that this query can never use keys as the RAND() function must be - re-evaluated for each row) -2000-01-04 Michael Widenius <monty@monty.pp.sci.fi> - -* Fixed optimization bug in SELECT .. LEFT JOIN ... key_column IS NULL, when - key_column could contain NULL values. -* Fixed problem with 8 bit characters as separators in LOAD DATA INFILE. - -2000-01-02 Michael Widenius <monty@monty.pp.sci.fi> - -* Release of 3.23.8 - -1999-12-31 Michael Widenius <monty@monty.pp.sci.fi> - -* Fixed GRANT problem when doing 'CREATE TABLE ... SELECT' - -1999-12-30 Michael Widenius <monty@monty.pp.sci.fi> - -* Fixed problem with timezones that are < GMT -11 -* Fixed problem with ISAM when doing some ORDER BY .. DESC queries. - -1999-12-28 Michael Widenius <monty@monty.pp.sci.fi> - -* Fixed bug when doing a join on a text key which didn't covert the whole key. -* Option --delay-key-write didn't enable delayed key writing -* Fixed update of TEXT column which only involved case changes. - -1999-12-27 Michael Widenius <monty@monty.pp.sci.fi> - -* Fixed that INSERT DELAYED doesn't update timestamps that are given. - -1999-12-25 Michael Widenius <monty@monty.pp.sci.fi> - -* Added function yearweek() and options 'x', 'X', 'v' and 'V' to date_format() - -1999-12-22 Michael Widenius <monty@tik.pp.sci.fi> - -* Fixed problem with MAX(indexed_column) and HEAP tables. -* Fixed problem with BLOB NULL keys and LIKE "prefix%". - -1999-12-20 Michael Widenius <monty@monty.pp.sci.fi> - -* Fixed problem with MyISAM and fixed length rows < 5 bytes. - -1999-12-10 Michael Widenius <monty@monty.pp.sci.fi> - -* Added RAID support (patch from Tõnu Samuel) - -1999-12-02 Michael Widenius <monty@monty.pp.sci.fi> - -* Added -O interactive_timeout to mysqld. -* Changed the argument of mysql_data_seek to ulonglong from ulong. - -1999-11-30 Michael Widenius <monty@monty.pp.sci.fi> - -Fixed bug in replace() on Alpha. Patch by 'Tom Holroyd' -Fixed bug in LOAD DATA LOCAL INFILE on Alpha. Patch by 'Tom Holroyd' - -1999-11-29 Michael Widenius <monty@monty.pp.sci.fi> - -* Added detection of pthread_attr_stacksize() in configure. -* Added variable net_retry_count (needed for FreeBSD). - -Sun Nov 28 20:55:45 1999 Michael Widenius <monty@bitch.pp.sci.fi> - -* Added option '--verbose' to mysqladmin - -1999-11-28 Michael Widenius <monty@monty.pp.sci.fi> - -* Fixed problem when converting heap to myisam. -* Fixed bug in HEAP tables when doing insert + delete + insert + scan the - table. - -1999-11-23 Michael Widenius <monty@monty.pp.sci.fi> - -* Fix core dump when releasing a lock from a non existing table -* Remove locks on tables before starting to remove duplicates. -* Added patch to make most string functions multi-byte safe (by Wei He) -* Added Bytes_recieived/Bytes_sent statistics (by Sasha Pachev) -* Added optimization of read_next() with MyISAM -* Changed MyISAM to use concurrent inserts. - -1999-11-21 Michael Widenius <monty@monty.pp.sci.fi> - -* Inverted flag 'delayed_key_write' on 'show variables' - -1999-11-20 Michael Widenius <monty@monty.pp.sci.fi> - -* Added variable max_write_lock_count - -1999-11-13 Michael Widenius <monty@monty.pp.sci.fi> - -* Release of 3.23.6 -* Made floor() overflow safe on FREEBSD. -* Allow quoting of identifers with ` -* Temporary tables now start with #sql -* Added option --quote-names to mysqldump. -* Added option --ansi to change " to a identifier delimiter and || to - string concatenation. -* Fixed INTO DUMPFILE to give better error messages. NULL is now written - as an empty string. -* Changed Field_double and double->string to use snprintf() to avoid overflows. -* Fixed bug that one could make a part of a PRIMARY KEY not null. -* Fixed encrypt() to be thread safe and not reuse buffer. - -1999-11-11 Michael Widenius <monty@monty.pp.sci.fi> - -* Changed that FLOAT(X) where X <= 24 -> FLOAT and X <= 53 -> DOUBLE. -* Changed DECIMAL(X) to be DECIMAL(X,0) and DECIMAL to be DECIMAL(10,0) - -1999-11-09 Michael Widenius <monty@monty.pp.sci.fi> - -* Added mysqld option -O lower_case_table_names=[0|1] to force table - names to lower case. -* Added mysql_odbc_escape_string() function to support big5 characters in - MyOBC. - -1999-11-08 Michael Widenius <monty@monty.pp.sci.fi> - -* Added patch by Sasha for user defined variables. -* Changed that FLOAT and DOUBLE (without any length modifiers) are - not anymore fixed decimal point numbers. - -1999-10-22 Michael Widenius <monty@tik.pp.sci.fi> - -* Added option ROW_FORMAT=[default, dynamic, static, compressed] to - CREATE_TABLE - -1999-10-20 Michael Widenius <monty@monty.pp.sci.fi> - -* 'DELETE FROM table_name' didn't work on temporary tables - -1999-10-18 Michael Widenius <monty@monty.pp.sci.fi> - -* Release of MySQL 3.23.5 -* Fixed problem with LIKE "%const" - -1999-10-17 Michael Widenius <monty@monty.pp.sci.fi> - -* Added bit function ~ (neg) - -1999-10-14 Michael Widenius <monty@monty.pp.sci.fi> - -* Added support for the GBK Chinese character set (by Wei He) - -1999-10-13 Michael Widenius <monty@monty.pp.sci.fi> - -* Storage of date results is now much faster to date and datetime columns. - -1999-10-12 Michael Widenius <monty@monty.pp.sci.fi> - -* Fixed bug when using DISTINCT + ORDER BY RAND() -* new option --relative to mysqladmin. - -1999-10-11 Michael Widenius <monty@monty.pp.sci.fi> - -* Added some error messages in mysqld.cc -* Allow use of NATIONAL and NCHAR when defining character columns. - (They don't do anything) -* Don't allow NULL columns in PRIMARY KEY:s (only in UNIQUE keys) - -1999-10-10 Michael Widenius <monty@monty.pp.sci.fi> - -* Clear LAST_INSERT_ID if in uses this in ODBC context: - WHERE auto_increment_column IS NULL; -* 'SET SQL_AUTO_IS_NULL=0|1' now turns off/on the above handling of - auto_increment columns - -1999-10-09 Michael Widenius <monty@monty.pp.sci.fi> - -* Added parameter 'concurrency' for Solaris. - -1999-10-07 Michael Widenius <monty@monty.pp.sci.fi> - -* Fixed problem when using an auto_increment column in two keys - -1999-10-06 Michael Widenius <monty@monty.pp.sci.fi> - -* AS on fieldname with CREATE TABLE table_name SELECT ... didn't work. - -1999-10-01 Michael Widenius <monty@tik.pp.sci.fi> - -* LIKE with many % ("%xxx%yy%zz%") are now much faster. - -1999-09-24 Michael Widenius <monty@monty.pp.sci.fi> - -* Fix privilege check for LOAD DATA REPLACE . - -1999-09-22 Michael Widenius <monty@monty.pp.sci.fi> - -* Added SHOW GRANT FOR user (by Sinisa) -* Added date + INTERVALL # date_interval_type - -1999-09-19 Michael Widenius <monty@monty.pp.sci.fi> - -* Fixed problem with index optimzation with 'WHERE index is not null' - -* Allow creation of temporary tables with same name as original table. -* When granting user a grant option for a database, he couldn't grant - privileges to other users. - -1999-09-17 Michael Widenius <monty@monty.pp.sci.fi> - -* Inserting a DATETIME into a TIME column will not anymore try to store 'days' - in it. -* Fixed problem with storage of float/double on low endian machines. - -1999-09-08 Michael Widenius <monty@monty.pp.sci.fi> - -* Release of 3.23.3 -* Added limit to UPDATE -* Added client library function: mysql_change_user() - -1999-09-07 Michael Widenius <monty@monty.pp.sci.fi> - -* Added character set to 'show variables' -* Added support of '--[white-space]' as comment -* Allow 'INSERT into table_name VALUES ();' - -1999-09-03 Michael Widenius <monty@monty.pp.sci.fi> - -* Add mysqld option --delay-key-write to mysqld.cc - -1999-08-30 Michael Widenius <monty@monty.pp.sci.fi> - -* Fixed problem with COUNT(DISTINCT) and GROUP BY - -1999-08-29 Michael Widenius <monty@monty.pp.sci.fi> - -* Added /*!version */ -* Fixed core dump with empty blob to reverse() -* Fixed problem with year(now()) and year(curdate()). - -1999-08-28 Michael Widenius <monty@monty.pp.sci.fi> - -* Added construct: - - CASE value WHEN [compare-value] THEN result - [WHEN [compare-value] THEN result ...] - [ELSE result] - END - - CASE WHEN [condition] THEN result - [WHEN [condition] THEN result ...] - [ELSE result] - END -1999-08-19 Michael Widenius <monty@tik.pp.sci.fi> - -* Added check of arguments to acos() and asin(). -* unique_column IS NULL only returned the first row with NULL. - -1999-08-12 Michael Widenius <monty@tik.pp.sci.fi> - -* REGEXP(...,NULL) will not return an error anymore. - -* Removed ifdef mSQL_COMPLIANT when comparing NULL to NULL - -1999-08-05 Michael Widenius <monty@monty.pp.sci.fi> - -* Fix problem with LOCK TABLES and DELETE FROM table. - -1999-08-04 Michael Widenius <monty@monty.pp.sci.fi> - -* Don't pack all keys even if one key is packed when not using PACK_KEYS=1. - -1999-08-03 Michael Widenius <monty@monty.pp.sci.fi> - -* Fixed core-dump bug when inserting table or column grant on user name "" - -1999-08-02 Michael Widenius <monty@monty.pp.sci.fi> - -* Fix problem with LOCK TABLES when no database is selected. -* New functions: MD5(), (by Tõnu Samuel) and EXPORT_SET (by Sasha Pachev) -* Changed Socket to my_socket (to avoid conflicts) - -1999-07-29 Michael Widenius <monty@monty.pp.sci.fi> - -* Fixed problem with DISTINCT on BLOB column -* new keywords: CASE, THEN, WHEN, ELSE, END -* The CASE operator (by Tõnu Samuel) (not yet working) -* set SQL_LOW_PRIORITY_UPDATES=# didn't work -* Fixed range optimizer bug in - SELECT * FROM table_name WHERE key_part1 >= const AND (key_part2 = const OR key_part2 = const) - -1999-07-26 Michael Widenius <monty@tik.pp.sci.fi> - -* One can now update indexes columns that are used in the WHERE clause. - UPDATE tbl_name SET KEY=KEY+1 WHERE KEY > 100; - -1999-07-25 Michael Widenius <monty@tik.pp.sci.fi> - -* Added get_date() function to all item and fields. This makes date handling - a lot faster! -* Added handling of fuzzy dates (dates where day or month is 0) - -1999-07-21 Michael Widenius <monty@tik.pp.sci.fi> - -* Fixed optimization of SELECT ... WHERE key_part1=const1 and key_part_2=const2 and key_part1=const4 and key_part2=const4 - (indextype should be range instead of ref) - -1999-07-20 Michael Widenius <monty@tik.pp.sci.fi> - -* Changed handling of 'const_item' to allow handling of - ORDER BY RAND(). -* MyISAM tables now allows keys on NULL and BLOB columns. -* Optimize the following LEFT JOIN: - SELECT ... FROM t1 LEFT JOIN t2 ON ... WHERE t2.not_null_column IS NULL - -1999-07-19 Michael Widenius <monty@tik.pp.sci.fi> - -* Added ORDER BY and GROUP BY with functions -* Changed all handling of tables in sql_select.cc to use table_map instead - of table_nr. -* Added use of column_name = formula to use keys -* column_name = column_name2 don't anymore have to have identical packing -* field IS NULL can now use keys - -1999-07-16 Michael Widenius <monty@tik.pp.sci.fi> - -* Changed heap tables to be stored in low_byte_first order (to make it easy - to convert to MyISAM tables) -* Automatic change of HEAP temporary tables to MYISAM tables in case of - 'table is full' errors. -* Added option --init-file=file_name to mysqld - -1999-07-15 Michael Widenius <monty@tik.pp.sci.fi> - -* Added COUNT(DISTINCT value,[value,...]) - -1999-07-14 Michael Widenius <monty@tik.pp.sci.fi> - -* changed name of temporary table to include getpid(). -* Added full support for CREATE TEMPORARY - -1999-07-04 Michael Widenius <monty@monty.pp.sci.fi> - -* Added CREATE TABLE options 'CHECKSUM' and 'PACK_KEYS' -* Added mysqld option --default-table-type -* Added column 'packed' to 'show index' -* Added pack_keys and checksum to show table status - -1999-07-01 Michael Widenius <monty@monty.pp.sci.fi> - -* Release of 3.23.0 -* Show NULL as the default value for AUTO_INCREMENT columns. -* Fixed optimizer bug with tables with only one row. -* Fixed bug when using LOCK TABLES table_name READ; FLUSH TABLES; - -1999-06-30 Michael Widenius <monty@monty.pp.sci.fi> - -* Added use of LIBWRAP (by "Henning P . Schmiedehausen" <hps@tanstaafl.de>) - -1999-06-28 Michael Widenius <monty@monty.pp.sci.fi> - -* Don't allow AUTO_INCREMENT for other than numerical columns -* Using AUTO_INCREMENT will now automaticly make the column NOT NULL. - -1999-06-22 Michael Widenius <monty@tik.pp.sci.fi> - -* Added privilege column to 'show columns' - -1999-07-13 Michael Widenius <monty@tik.pp.sci.fi> - -* Added SQL_BIG_RESULT (SQL_SMALL_RESULT is now default) -* Use the MYISAM UNIQUE constraint to solve SELECT DISTINCT faster. - -1999-06-06 Michael Widenius <monty@monty.pp.sci.fi> - -* Changed most macros in the libmysql library to functions to avoid many - versions of shared libraries. - -1999-05-16 Michael Widenius <monty@tik.pp.sci.fi> - -* Added "Row_type" to SHOW TABLE STATUS. - -1999-05-15 Michael Widenius <monty@monty.pp.sci.fi> - -* Added option IF NOT EXISTS to CREATE TABLE -* Allow creation of CHAR(0) columns. - -1999-05-14 Michael Widenius <monty@monty.pp.sci.fi> - -* Added more error checking of errors from the table handler. - -1999-05-13 Michael Widenius <monty@monty.pp.sci.fi> - -* Added the '<=>' operator which will act as '=' but will return TRUE if both - arguments are NULL. - -1999-05-12 Michael Widenius <monty@monty.pp.sci.fi> - -* The default base for the log, update-log and pid-file name is now - 'hostname' with everything after the first '.' removed. - -1999-05-11 Michael Widenius <monty@monty.pp.sci.fi> - -* Require '%' before format characters in DATE_FORMAT(). -* Add logging of GRANT and SET PASSWORD in the update log. - -1999-05-10 Michael Widenius <monty@monty.pp.sci.fi> - -* Changed LIKE compare to behave as =; This means that 'e' LIKE 'é' is now - true. - -1999-05-10 Michael Widenius <monty@tik.pp.sci.fi> - -* REPLACE now used direct read to find dupplicate row instead of key read; - This makes REPLACE a lot faster. - -1999-05-05 Michael Widenius <monty@tik.pp.sci.fi> - -* New option: CREATE TABLE SELECT .... -* Added syntax for CREATE TEMPORARY TABLE (not yet implemented) - -1999-05-03 Michael Widenius <monty@tik.pp.sci.fi> - -@code{DESCRIBE TABLE} returns a lot of information about the tables - -1999-05-02 Michael Widenius <monty@monty.pp.sci.fi> - -* Added comments to tables -* Added UNIQUE, in CREATE TABLE table_name (col int not null UNIQUE); - -1999-04-29 Michael Widenius <monty@monty.pp.sci.fi> - -* Use auto_increment value provided by MYISAM -* Use key_part statistics if available - -1999-04-28 Michael Widenius <monty@monty.pp.sci.fi> - -* null bits are now stored at start of row instead at the end. - (Now we only need one bit to mark a row deleted) - -* DELAYED is now a reserved words. (because of conflicts from yacc) - -1999-04-20 Michael Widenius <monty@monty.pp.sci.fi> - -* Added patches for DEC_3_2 by "Andrea Suatoni" <and@itaca.it> - -1999-04-19 Jani Tolonen <jani@monty.pp.sci.fi> - -* Added load_file() function - -1999-04-15 Michael Widenius <monty@monty.pp.sci.fi> - -* Added option --skip-show-databases to mysqld. - -1999-04-10 Michael Widenius <monty@monty.pp.sci.fi> - -* set start time when connection. - -1999-04-03 Michael Widenius <monty@monty.pp.sci.fi> - -* Fixed problem with 'DELETE FROM TABLE' when table was locked by another - thread. - -1999-04-03 Michael Widenius <monty@monty.pp.sci.fi> - -* Check if rows has changed now also works with BLOB/TEXT. -* Added the INNER JOIN syntax; This made 'INNER' a reserved word. - -1999-04-02 Michael Widenius <monty@monty.pp.sci.fi> - -* Fixed problem with 'Host '..' is not allowed to connect to this MySQL - server' after one had inserted a new MySQL user with a GRANT command. - -1999-04-01 Michael Widenius <monty@monty.pp.sci.fi> - -* Added support for netmasks to the hostname in the MySQL tables. - -1999-03-31 Michael Widenius <monty@monty.pp.sci.fi> - -* Changed net.cc to use TCP_NODELAY also on Linux. -* If one compares a NOT NULL DATE/DATETIME column with IS NULL, this - is changed to a compare against 0 to satisfy some ODBC applications. - (By shreeve@uci.edu) - -1999-03-30 Michael Widenius <monty@monty.pp.sci.fi> - -* NULL IN (...) now returns NULL instead of 0. - This will ensure that 'null_column NOT IN (...)' doesn't match NULL values. -* Changed the mysql.db entry to char(60). - -1999-03-16 Michael Widenius <monty@monty.pp.sci.fi> - -* Fix storage of floating point values in TIME columns -* Changed parsing of TIME strings to be more strict. Now the fractional - second part is detected (and currently skipped) - The following formats are supported - [[[DAYS] [H]H:]MM:]SS[.fraction] and [[[[H]H]H]H]MM]SS[.fraction] -* Detect (and ignore) second fraction part from DATETIME - -1999-03-10 Michael Widenius <monty@monty.pp.sci.fi> - -* On Win32 detect --basedir automaticly from path to mysqld. -* Added option --skip-column-names to mysql. -* Added some memory checks in sql_string.cc - -1999-03-08 Michael Widenius <monty@monty.pp.sci.fi> - -* Added the ODBC 3.0 EXTRACT(interval FROM datetime) function -* Added lots of 'out of memory' checks for SELECT statements. - -1999-03-05 Michael Widenius <monty@monty.pp.sci.fi> - -* Fixed std() for big tables when result should be 0. - -1999-03-04 Michael Widenius <monty@monty.pp.sci.fi> - -* INSERT DELAYED had some garbage at end in the update log. - -1999-03-03 Michael Widenius <monty@monty.pp.sci.fi> - -* Added a lot of casts in filesort.cc to make it more portable. - -1999-02-28 Michael Widenius <monty@monty.pp.sci.fi> - -* Changed default size of key_buffer to 4M -* Fixed problem with queries that needed temporary tables with blobs. - -1999-02-26 Michael Widenius <monty@monty.pp.sci.fi> - -* Added LOAD DATA [LOW_PRIORITY] INFILE. -* Added option 'flush-time' to force MySQL-Win32 version to flush - the tables once in a while. -* On Linux all process didn't die on shutdown. - -1999-02-18 Michael Widenius <monty@monty.pp.sci.fi> - -* Fixed a core dump problem when using --log-update and connecting - without a default database. -* Added more error check if one get an error writing to the log files. - -1999-02-16 Michael Widenius <monty@monty.pp.sci.fi> - -* Fixed some configure errors -* If one used @code{LEFT JOIN} on tables that had circular dependencies this - caused mysqld to hang forever. - -1999-02-05 Michael Widenius <monty@monty.pp.sci.fi> - -* Release of 3.22.16 -* mysqladmin processlist could core dump mysqld if a new user logged in. -* DELETE FROM table_name WHERE key_column=column_name didn't find any matching - rows. -* The default index name is now using the same case as the used column name. - -1999-02-03 Michael Widenius <monty@monty.pp.sci.fi> - -* Added the MODIFY attribute to ALTER TABLE (to be compatible with some other - SQL databases) -* Added LIKE to 'show status' - -1999-01-31 Michael Widenius <monty@monty.pp.sci.fi> - -* DATE_ADD(column,...) didn't work. -* INSERT DELAYED could deadlock with status 'upgrading lock' - -1999-01-30 Michael Widenius <monty@monty.pp.sci.fi> - -* Extended item_encrypt() to take longer salt strings than 2 characters. - (for FreeBSD) - -1999-01-26 Michael Widenius <monty@monty.pp.sci.fi> - -* Release of 3.22.15 -* LIKE on binary strings didn't work if one used a multi-byte character set. -* mysqladmin -w will now wait for the server to come up if it's killed. - -Tue Jan 26 00:06:10 1999 Michael Widenius <monty@bitch.pp.sci.fi> - -* Fixed problem with ORDER BY whith 'only index' optimzation when there - where multiple key definitions for an used column. -* GRANT with password didn't update in memory GRANT table before - 'mysqladmin flush' - -1999-01-20 Michael Widenius <monty@monty.pp.sci.fi> - -* Updating BLOB/TEXT through formulas didn't work for short (< 256 char) - strings. -* Changed option --extended_insert-insert to --extended-insert in mysqldump - -1999-01-19 Michael Widenius <monty@monty.pp.sci.fi> - -* Lots of changes to support INSERT DELAYED. - -1999-01-17 Michael Widenius <monty@monty.pp.sci.fi> - -* Changed unpacking of DATE and DATETIME; These are now about 5 times faster. - -1999-01-16 Michael Widenius <monty@monty.pp.sci.fi> - -* DATE_ADD with now() or curdate() reused the same string. -* Added BENCHMARK(loop-count,expression) function to time expressions. - -1999-01-14 Michael Widenius <monty@monty.pp.sci.fi> - -* LEFT JOIN USING (col1,col2) gave an error if one used it with tables - from 2 different databases. -* LOAD DATA LOCAL INFILE didn't work in the unix version because of a missing - file in the sql directory -* Fixed problems with VARCHAR/BLOB on very short rows (< 4 bytes); One - could get error 127 when deleting rows. - -1999-01-13 Michael Widenius <monty@monty.pp.sci.fi> - -* Nicer error messages for table types. -* Changed default number of connections to 100 - -1999-01-11 Michael Widenius <monty@monty.pp.sci.fi> - -* When one did a GRANT on a new host mysqld could die on the first connect - from this host. -* Use as default bigger buffers when using 'load data infile'. - -1999-01-06 Michael Widenius <monty@monty.pp.sci.fi> - -* All blob pointers have now reserved 8 bytes in the .frm files; This makes - the .frm files portable to 64 bit architectures. - -* DECIMAL(x,y) now works according to ANSI SQL. - -1998-12-30 Michael Widenius <monty@monty.pp.sci.fi> - -* If one used ORDER BY on column name that was the same name as an alias, - the ORDER BY was done on the alias. - -1998-12-29 Michael Widenius <monty@monty.pp.sci.fi> - -* Added aggregate UDF functions. Thanks to - Andreas F. Bobak <bobak@relog.ch> for this ! - -1998-12-28 Michael Widenius <monty@monty.pp.sci.fi> - -* Changed sql_crypt() a bit to make it a bit more secure; This will make old - string stored with the old decrypt() function unreadable! - -1998-12-27 Michael Widenius <monty@monty.pp.sci.fi> - -* Allow empty arguments to mysqld to make it easier to start it - from shell scripts! - -1998-12-23 Michael Widenius <monty@monty.pp.sci.fi> - -* last_insert_id() is now updated for INSERT INTO ... SELECT -* Setting a TIMESTAMP column to NULL didn't record the timestamp - value in the update log. - -1998-12-21 Michael Widenius <monty@monty.pp.sci.fi> - -* Fixed lock handler bug when one did: - INSERT INTO TABLE ... SELECT ... GROUP BY. -* Added a patch for localtime_r() on Win32 that it will not crash anymore - if your date is > 2039, but instead it will return a time of all zero. -* UDF function names are not longer case sensitive. -* Added escape of '^Z' to \Z as ^Z doesn't work with pipes on Win32 -* Changed optimizer to not use 'range search' in some cases. -* Changed optimizer to use result form 'check_range' in optimization of - searching of part keys. - -1998-12-16 Michael Widenius <monty@monty.pp.sci.fi> - -* SELECT COUNT(*) didn't work on LEFT JOIN queries with only had expressions - in the ON part and there where no WHERE clause. -* Added optional support for crypted frm files. - -1998-12-13 Michael Widenius <monty@monty.pp.sci.fi> - -* Saving NOW(), CURDATE() or CURTIME() directly in a column didn't work. -* SELECT COUNT(*) didn't work on LEFT JOIN queries with only had expressions - in the ON part and no WHERE clause. - -1998-12-09 Michael Widenius <monty@monty.pp.sci.fi> - -* Fixed a bug in sql_list.h that made ALTER TABLE dump a core in some context. - -1998-12-08 Michael Widenius <monty@monty.pp.sci.fi> - -* Allow use of negative real numbers without a decimal point. -* day number is now adjusted to max days in month if the resulting month - after DATE_ADD/DATE_SUB() doesn't have enough days. - -1998-12-07 Michael Widenius <monty@monty.pp.sci.fi> - -* Fix that GRANT compares columns case insensitive. -* Add / to TMPDIR if needed. - -1998-12-06 Michael Widenius <monty@monty.pp.sci.fi> - -* Allow GLOBAL as a table or database name. - -Thu Dec 3 10:29:11 1998 Michael Widenius <monty@tik> - -* Added option SQL_SMALL_RESULT to SELECT to force use of fast temporary - tables when one knows that the result set will be small! - -1998-11-27 Michael Widenius <monty@monty.pp.sci.fi> - -* The hostname in user@hostname can now include '.' and '-' without quotes. - -1998-11-26 Michael Widenius <monty@monty.pp.sci.fi> - -* Fixed problem when using DATE_ADD()/DATE_SUB() in a WHERE clause. -* One can now set the password for a user with the - GRANT ... user IDENTIFIED BY 'password' syntax. -* Fixed bug in GRANT checking with SELECT on many tables. -* Removed some 'no Database selected' errors. -* Release of 3.22.11 - -1998-11-21 Michael Widenius <monty@monty.pp.sci.fi> - -* Changed USER() to return user@host -* New command: FLUSH STATUS ; to reset most status variables. -* New status variables: aborted_threads and aborted_connects. - -1998-11-20 Michael Widenius <monty@monty.pp.sci.fi> - -* Fixed bug in ORDER BY on FIELD() -* New function make_set() (70% by Jani Tolonen) - -1998-11-18 Michael Widenius <monty@monty.pp.sci.fi> - -* Added functions encrypt() and decrypt(). Because of endspace stripping of - CHAR() and VARCHAR() these should only be used with fixed size strings or - with BLOB/TEXT columns. - -1998-11-17 Michael Widenius <monty@monty.pp.sci.fi> - -* Silently remove DEFAULT values from AUTO_INCREMENT columns. -* Added new variable to mysqld: connection_timeout - -1998-11-13 Michael Widenius <monty@monty.pp.sci.fi> - -* Better error message when table doesn't exists. - -1998-11-12 Michael Widenius <monty@monty.pp.sci.fi> - -* Added option SET SQL_WARNINGS=1 to get a warning count also for simple - inserts. -* IS NULL on a AUTO_INCREMENT column in a LEFT JOIN didn't work. -* MySQL now uses SIGTERM instead of SIGQUIT with shutdown to work better - on FreeBSD. -* Added option \G (print vertically) to mysql -* SELECT HIGH_PRIORITY ... killed mysqld. - -1998-11-11 Michael Widenius <monty@monty.pp.sci.fi> - -* Added grant checking to 'show tables' -* Large changes to get grants integrated with the current privilege system. - -1998-11-10 Michael Widenius <monty@monty.pp.sci.fi> - -* SELECT .. WHERE t1.id1=t2.id2 could fail if t1.id1 and t1.id2 was keys - and of radically differently types. -* Changed get_password to use getpass function. (Patch by Jaromir - Dolecek <dolecek@ics.muni.cz>) - -1998-11-04 Michael Widenius <monty@analytik> - -* Release of 3.22.10 -* Changed +, - (sign and minus), *, /, % and ABS() to be BIGINT aware. -* ALTER TABLE and UPDATE now writes out the values of any duplicated keys. - -1998-11-03 Michael Widenius <monty@monty.pp.sci.fi> - -* ADABASD like INSERT statement: - INSERT INTO table_name SET column=value,column=value... -* The client flag option 'CLIENT_IGNORE_SPACE' didn't work properly. -* Fixed bug in ALTER TABLE that caused a core dump. -* Added optimization of SELECT ... FROM table ORDER BY key_part1 LIMIT ... - This query will now use indexes instead of sorting the table. - -Mon Nov 2 20:52:15 1998 Jani Tolonen <jani@bitch.pp.sci.fi> - -* Added more variables to SHOW STATUS and changed format of output -* Added command extended-status to mysqladmin which will show the - new status - -1998-10-30 Michael Widenius <monty@monty.pp.sci.fi> - -* columns of type date, date_time and 'set' are now stored a little - more efficient if they are 0, NULL or ''. - -1998-10-26 Michael Widenius <monty@monty.pp.sci.fi> - -* Most errors are now printed through sql_write_error() which will add - date, time and thread id to the .err log. - -1998-10-25 Michael Widenius <monty@monty.pp.sci.fi> - -* Added option MYSQL_INIT_COMMAND to mysql_options() to make a query - on connect or reconnect. -* Added option MYSQL_READ_DEFAULT_FILE and MYSQL_READ_DEFAULT_GROUP to - mysql_option() to read the following parameters from the my.cnf file: - "port", "socket", "compress", "password", "pipe", "timeout", "user", - "init-command", "host" and "database" - -* Added maybe_null to the UDF structure - -1998-10-22 Michael Widenius <monty@monty.pp.sci.fi> - -* Added IGNORE to INSERT with many rows. -* Added SQL GRANT commands -* Release of 3.22.9 - -1998-10-18 Michael Widenius <monty@monty.pp.sci.fi> - -* One can new set the last_insert_id() value in an update with - LAST_INSERT_ID(expr). This makes it possible to return a value for things - like: - UPDATE table SET COUNT=LAST_INSERT_ID(COUNT+1) WHERE primary_key_col=# -* display key name used by 'range' in the 'key' column in 'show processlist' -* new SQL command: FLUSH [ TABLES | HOSTS | LOGS | PRIVILEGES ] [, ...] -* new SQL command: KILL thread_id - -Thu Oct 15 18:57:15 1998 Michael Widenius <monty@tik> - -* Reuse memory for identical set and enum fields. - -1998-10-14 Michael Widenius <monty@analytik> - -* Added open file information to mysqladmin debug -* Fixed conversion problem when using ALTER TABLE from a INT to a short CHAR() - column. -* Added 'SELECT HIGH_PRIORITY'; This will get a lock for the SELECT even if - there is a thread waiting another SELECT to get a WRITE LOCK. - NOTE: This makes HIGH_PRIORITY a reserved word - -1998-10-12 Michael Widenius <monty@analytik> - -* Moved wild_compare to string class to be able to use LIKE on BLOB/TEXT columns with \0 -* Added ESCAPE option to LIKE - -1998-10-08 Michael Widenius <monty@monty.pp.sci.fi> - -* Update for AIX: Added a cast to all bzero() calls and changed to use - my_gethostbyname_r instead of gethostbyname_r. - -1998-10-03 Michael Widenius <monty@monty.pp.sci.fi> - -* Release of 3.22.8 -* Added an extra thread signal loop on shutdown to avoid some error messages - from the client. -* MySQL now uses the next available number as extension for the update - log file. - -1998-09-25 Michael Widenius <monty@monty.pp.sci.fi> - -* MySQL clients on NT will now by default first try to connect with named pipes - and after this with TCP/IP. - -1998-09-24 Michael Widenius <monty@monty.pp.sci.fi> - -* Fixed problems with TIME columns and negative strings. -* Added a new column 'state' to 'mysqladmin proc' that gives some - information what the thread is doing. - -* DATE_ADD() and DATE_SUB() didn't work with group functions. - -1998-09-23 Michael Widenius <monty@monty.pp.sci.fi> - -* 'mysql' will now also try to reconnect on 'use database' commands. - -* Fix problem with ORDER BY and LEFT JOIN and const tables. - -1998-09-22 Michael Widenius <monty@monty.pp.sci.fi> - -* Fixed problem with ORDER BY if the first ORDER BY column was a key and - the rest wasn't. - -1998-09-17 Michael Widenius <monty@monty.pp.sci.fi> - -* Release of 3.22.7 -* OPTIMIZE TABLE table_name can now be used to reclaim disk space - after many deletes. This uses currently ALTER TABLE to re-generate - the table, but in the future it will use an integrated isamchk - for more speed. - -1998-09-16 Michael Widenius <monty@monty.pp.sci.fi> - -* Added functions for perfect hashing of symbols. Moved some other things - to the LEX structure for faster setup. -* Changed libmysql.dll on Win32 to use TLS to get better multi-threading - -1998-09-15 Michael Widenius <monty@monty.pp.sci.fi> -* Added --where to mysqldump (patch by Jim Faucette). -* Fixed slow UPDATE/DELETE when using DATETIME or DATE keys. - -1998-09-14 Michael Widenius <monty@monty.pp.sci.fi> - -* Changed some optimizations parameters to make better joins. -* Anyone can now use 'mysqladmin proc' to check ones own - threads. Only users with the 'Process_priv' privilege can get - information about all threads. -* Fixed very unlikely optimizer bug in the range optimizer - (bug introduced in 3.22.6) -* Added handling of formats YYMMDD, YYYYMMDD, YYMMDDHHMMSS to - DATETIME/TIMESTAMP when using numbers. (Before these formats only worked - with strings). - -1998-09-06 Michael Widenius <monty@monty.pp.sci.fi> - -* Added connect option CLIENT_IGNORE_SPACE to allow one to use - space after the function name and before '(' (Powerbuilder requires this). - This will make all function names reserved words. -* comments that start with /*! are now interpreted as commands. This feature - allows one to use MySQL extensions like: - 'SELECT /*! STRAIGHT_JOIN */ * from table1,table1' - in a portable manor. - -1998-09-04 Michael Widenius <monty@analytik> - -* Added SET OPTION INSERT_ID=# to force use of specific INSERT_ID. This is - usable with new --log-long-format option. - -1998-08-31 Michael Widenius <monty@monty.pp.sci.fi> - -* Fixed problem when INSERTING into TIME fields. - -1998-08-29 Michael Widenius <monty@monty.pp.sci.fi> - -* Added key cache and handler statistic to 'mysqladmin debug'. -* changed UPDATE and DELETE to not use 'index only' range detection. - (Fixed slow update_key in the benchmarks) -* Changed the insert benchmark because it was impossible to use it with - postgreSQL (to slow). - -Thu Aug 27 15:38:23 1998 Michael Widenius <monty@bitch.pp.sci.fi> - -* mysqldump will automaticly use LOAD DATA LOCAL INFILE if one uses - an TCP/IP connection. - -1998-08-27 Michael Widenius <monty@monty.pp.sci.fi> - -* Added support of local files with LOAD DATA LOCAL INFILE .. -* Save history if one kills mysql with ^C. Save history in MYSQL_HISTFILE. - Modfied patch by Tommy Larsen <tommy@mix.hive.no> - -1998-08-26 Michael Widenius <monty@monty.pp.sci.fi> - -* Fixed a possible problem with mysql_refresh(). - -Tue Aug 18 14:07:53 1998 Michael Widenius <monty@bitch.pp.sci.fi> - -* Give an error for queries that mix GROUP columns and fields when there - is no GROUP BY specification. - -1998-08-17 Michael Widenius <monty@monty.pp.sci.fi> - -* Changed sql_yacc.yy to allow field attributes in any order. - -1998-08-15 Michael Widenius <monty@monty.pp.sci.fi> - -* Increased max_allowed_packed to 1M as default. -* LOAD DATA INFILE didn't copy single field separators in some case: - "Hello"Atif"!" - -1998-08-13 Michael Widenius <monty@monty.pp.sci.fi> - -* Fixed fatal bug in lpad(). - -Thu Aug 13 01:00:44 1998 Michael Widenius <monty@bitch.pp.sci.fi> - -* REGEXP can now take a expression as the second argument. - -1998-08-12 Michael Widenius <monty@monty.pp.sci.fi> - -* Changed LIKE to be faster in some cases with many '%': LIKE '%c%ompiler%' - -1998-08-11 Michael Widenius <monty@monty.pp.sci.fi> - -* All table lock handing is changed to avoid some very subtitle - deadlocks when using DROP TABLE, ALTER TABLE, DELETE FROM TABLE and - mysqladmin flush-tables under heavy usage. - -1998-08-10 Michael Widenius <monty@monty.pp.sci.fi> - -* Allow one to use the syntax 'CONSTRAINT symbol' before FOREIGN KEY. -* new mysqld option '--low-priority-insert' to give inserts lower priority - than selects. -* One can now use {INSERT | REPLACE} LOW_PRIORITY INTO ... - One side effect is that LOW_PRIORITY is now a reserved word :( -* Changed locking code to get better handling of locks of different types. - -1998-08-09 Michael Widenius <monty@monty.pp.sci.fi> - -* mysqld will now ignore trailing ';' characters in queries. This is to - make it easier to emigrate from some other SQL servers that require the - end ';' -* One can now use a LIMIT value with DELETE to make it return after deleting - a given number of rows. -* Fix for corrupted output of fixed format and SELECT INTO OUTFILE: - select * from test into outfile "/tmp/test.txt" fields terminated by '' enclosed by '' - -1998-08-04 Michael Widenius <monty@monty.pp.sci.fi> - -* new mysqld option: '-O max_connect_errors=#'. - Connect errors are now reset for each correct connection. -* Add support for INSERT INTO table ... VALUES(...),(...),(...) - -1998-08-03 Michael Widenius <monty@monty.pp.sci.fi> - -* Added Oracle GREATEST() and LEAST() functions. One must now use - these instead if the MAX() and MIN() functions to get the biggest/smallest - value from a list of values. These can now handle real, bigint and - string values. -* The following query now uses indexing instead of sorting the table: - SELECT ... FROM table ORDER BY key_part1 desc,key_part2 desc,... -* Added check that the error message file has enough error messages. -* DAYOFWEEK() had offset 0 for Sunday. Changed the offset to 1. - -1998-08-02 Michael Widenius <monty@monty.pp.sci.fi> - -* new option to mysql: '--vertical' to print results in vertical mode. -* All count structures in the client (affected_rows, insert_id...) are now of - type BIGINT to allow one to use 64 bit values. - This required a minor change in the MySQL protocol which may affect - old clients when using tables with auto_increment values > 24M. -* The return type of mysql_fetch_lengths() has changed from uint * - to ulong *. This may give a warning for old clients but should work - on most machines. - -Thu Jul 30 15:29:05 1998 Michael Widenius <monty@tik> - -* COUNT(), STD() and AVG() are extended to handle more than 4G rows. - -Wed Jul 29 10:36:05 1998 Michael Widenius <monty@tik> - -* Added new option: - SET OPTION SQL_LOG_UPDATE=[0,1] to allow users with process_priv - privilege to bypass the update log. - (Modified patch from Sergey A Mukhin <violet@rosnet.net>) - -Thu Jul 23 15:58:13 1998 Michael Widenius <monty@tik> - -* Initialize line buffer in mysql.cc to make blob readings from pipes safer. - -Tue Jul 21 22:04:43 1998 Michael Widenius <monty@tik> - -* One can now store -838:59:59 <= x <= 838:59:59 in a TIME column. -* TIME_TO_SEC() and SEC_TO_TIME() can now handle negative times and hours - up to 32767. - -Mon Jul 20 20:34:33 1998 Michael Widenius <monty@tik> - -* Change mysys/dbug to allocate all thread varibles in one struct. - This makes it easier to make a threaded libmysql.dll - -Sun Jul 19 12:54:45 1998 Michael Widenius <monty@tik> - -* Changed ALTER TABLE to make it more multi-thread safe. -* normal INSERT INTO TABLE are now also cached when used with - LOCK TABLES. (previously only INSERT ... SELECT and LOAD DATA INFILE - was cached) - -Fri Jul 17 20:53:23 1998 Michael Widenius <monty@tik> - -* Allow group functions with HAVING: - SELECT col FROM table GROUP BY col HAVING COUNT(*)>0; - -Tue Jul 14 15:11:52 1998 Michael Widenius <monty@tik> - -* Use the result from 'gethostname' as the name for pid files - (instead of uname()). - -Sun Jul 12 12:38:45 1998 Michael Widenius <monty@tik> - -* Index only optimization; Some queries are now resolved using - only indexes. Until MySQL 4.0 this works only for number columns. - - SELECT key_part1,key_part2 FROM table WHERE key_part1=# - SELECT COUNT(*) FROM table WHERE key_part1=# and key_part2=# - SELECT key_part2 FROM table GROUP BY key_part1; - SELECT * FROM table ORDER BY key_part2; - -1998-07-07 Michael Widenius <monty@monty.pp.sci.fi> - -* Added function DATE_ADD() and DATE_SUB() - -1998-07-06 Michael Widenius <monty@monty.pp.sci.fi> - -* Added function SUBSTRING() with 2 arguments. - -1998-07-05 Michael Widenius <monty@monty.pp.sci.fi> - -* Added optimization to remove const reference tables from ORDER BY and - GROUP BY. -* Allow '$' in table and column names. - -1998-07-04 Michael Widenius <monty@monty.pp.sci.fi> - -* new option --tmpdir for mysqld. - -1998-07-03 Michael Widenius <monty@monty.pp.sci.fi> - -* MySQL now automaticly changes a query from an ODBC client: - SELECT ... from table WHERE auto_increment_column IS NULL - to - SELECT ... from table WHERE auto_increment_column == LAST_INSERT_ID(). - This allows some ODBC programs (Delphi, Access) to retrieve the newly - inserted row to fetch the auto_increment id. -* Drop table now waits for all users to free a table before deleting it - -1998-07-02 Michael Widenius <monty@monty.pp.sci.fi> - -* New functions: BIN(), HEX() and CONV() for converting between different - number bases. - -1998-07-01 Michael Widenius <monty@monty.pp.sci.fi> - -* If one created a table with smaller record length than 5, one couldn't - delete rows from this table -* mysqld now automaticly disables system locking on Linux, Win32 and if - one uses MIT-threads. One can force the use of locking by doing: - --enable-locking. -* Added new mysqld option --console, to force a console window (for error - messages) when using Win32. -* Removed a useless check in the ISAM delete code; Delete should now be - a bit faster. - -1998-06-28 Michael Widenius <monty@monty.pp.sci.fi> - -* Release of MySQL 3.22.3 -* New flag to mysqld: --one-thread for debugging with linuxthreads (or glibc) - -1998-06-27 Michael Widenius <monty@monty.pp.sci.fi> - -* Added the LEX structure to THD to get a bit more speed. -* Added DROP TABLE IF EXISTS to not get an error if the table doesn't exists. -* IF and EXISTS are now reserved words (they would have to be sooner or later) - -1998-06-26 Michael Widenius <monty@monty.pp.sci.fi> - -* Added lots of new options to mysqldump. - -Wed Jun 24 23:33:35 1998 Michael Widenius <monty@tik> - -* Server error messages are now in mysqld_errror.h -* Added compression server/client protocol. (By Sinisa). - -1998-06-22 Michael Widenius <monty@monty.pp.sci.fi> - -* New functions: <<, >>, rpad() and lpad(). -* Fixed a core-dump bug in the range optimizer. - -Fri Jun 19 01:51:09 1998 Michael Widenius <monty@tik> - -* One can now save default options (like passwords) in a config file (my.cnf). - -1998-06-17 Michael Widenius <monty@monty.pp.sci.fi> - -* searching on multiple constant keys that matched > 30 % of the rows didn't - always use the best possible key. - -1998-06-16 Michael Widenius <monty@monty.pp.sci.fi> - -* Lot's of small changes to get ORDER BY to work when no records are found - when using fields that are not in GROUP BY (MySQL extension) -* Added new option --chroot to mysqld to start mysqld in a chroot environment - (by Nikki Chumakov <nikkic@cityline.ru>) - -1998-06-15 Michael Widenius <monty@monty.pp.sci.fi> - -* Add option --one-database to mysql to only update one database - from a update log. - -1998-06-13 Michael Widenius <monty@monty.pp.sci.fi> - -* end space is now ignored when comparing case sensitive strings; - This should fix some problems with ODBC! -* mysql_free_result() now automaticly handles a mysql_use_result() set that - is not completely read. - -1998-06-10 Michael Widenius <monty@monty.pp.sci.fi> - -* Release of MySQL 3.22.1 -* Fixed problems with date_format() and wrong dates. -* enum() and set() columns was compared binary; Changed to be case insensitive. - -1998-06-08 Michael Widenius <monty@monty.pp.sci.fi> - -* Added new API functions: mysql_init() and mysql_options(). - One MUST now call mysql_init() before one calls mysql_real_connect(). - One doesn't have to call mysql_init if one only calls mysql_connect(). -* LEFT JOIN core dumped if the second table is used with a constant - WHERE/ON expression with uniquely identifies one record. - -1998-06-07 Michael Widenius <monty@monty.pp.sci.fi> - -* Range optimizer is not used anymore when comparing a string column - to a number. This will make such compares slower but safer. - -Sun Jun 7 04:47:14 1998 Michael Widenius <monty@tik> - -* UPDATE now returns a update information about how many rows was - matched, updated and if one got any 'warnings' when doing the update. - -Sat Jun 6 22:58:02 1998 Michael Widenius <monty@tik> - -* Fixed wrong result from 'format(-100,2)'. - -1998-06-06 Michael Widenius <monty@monty.pp.sci.fi> - -* Added new C-API function: mysql_ping(). -* Added options AFTER column and FIRST to ALTER TABLE ... ADD columns. - This makes is possible to add a new column at some specific location - in an old table. -* Fixed problem with find_in_set(). - -1998-05-18 Michael Widenius <monty@analytik> - -* Added new API function: mysql_ping(). - -1998-05-15 Michael Widenius <monty@monty.pp.sci.fi> - -* WEEK() now takes an optional argument to allow handling of weeks when the - first day of the week = Sunday (default or 0) or Monday ( extra argument is - 1). WEEK() now returns the week number in the range 0-53 for the used - year. - -1998-05-13 Michael Widenius <monty@monty.pp.sci.fi> - -* Added flag -T32 to mysqld for running all queries under the main thread. - This makes it possible to debug mysqld under Linux with gdb! - (This is now called --one-thread) - -1998-05-12 Michael Widenius <monty@monty.pp.sci.fi> - -* Added optimization of 'not_null_column IS NULL' (needed for some Access - queries) -* Made all time functions 'more streamlined'. - -1998-05-09 Michael Widenius <monty@monty.pp.sci.fi> - -* Allow one to use STRAIGHT_JOIN between two tables to force the optimizer - to join them in a specific order. - -Fri May 8 02:35:00 1998 Michael Widenius <monty@bitch.pp.sci.fi> - -* Added SET OPTION PASSWORD='new_crypted_password' and - SET OPTION PASSWORD= 'host' : 'user' : 'new_password'. The last version - only works for users with write access to the mysql database. - One can also use: SET OPTION PASSWORD=PASSWORD("new_password"); - -Tue May 5 14:41:47 1998 Michael Widenius <monty@bitch.pp.sci.fi> - -* String functions now return VARCHAR() instead of CHAR() and - the column type is now VARCHAR() for fields saved as VARCHAR(). - This should make the MyODBC driver better, but may break some old - MySQL clients that doesn't handle FIELD_TYPE_VARCHAR identical as - FIELD_TYPE_CHAR. - -Mon May 4 00:33:27 1998 Michael Widenius <monty@bitch.pp.sci.fi> - -* Added BOOL as a synonym for BIT and DISTINCTROW as a synonym for DISTINCT. -* CREATE INDEX and DROP INDEX are now implemented trough ALTER TABLE. - CREATE TABLE is still the recommended (fast) way to create indexes. -* Added option SET OPTION PASSWORD='new_password'. - mysqladmin can now be used by not anonymous users to change their - password. - -Sun May 3 18:47:24 1998 Michael Widenius <monty@bitch.pp.sci.fi> - -* Added option wait_timeout to mysqld. - -Sat Apr 18 14:14:23 1998 Michael Widenius <monty@bitch.pp.sci.fi> - -* Added hashing of fieldnames for tables with many fields. -* The most frequently used string functions are now in assembler (Linux-intel). - -Thu Apr 16 16:14:14 1998 Michael Widenius <monty@bitch.pp.sci.fi> - -* Added quick checking if ok host. -* Changed the interface for field->val_str() to better use stack buffers. - -Thu Apr 9 20:02:26 1998 Michael Widenius <monty@bitch.pp.sci.fi> - -* One can now reference to tables in different databases with: - table@database or database.table -* Added cacheing of users & access rights (for faster access rights checking) - -1998-04-08 Michael Widenius <monty@monty.pp.sci.fi> - -* Save of command line history to file in mysql client. - by Tommy Larsen <tommy@mix.hive.no> - -1998-04-07 Michael Widenius <monty@monty.pp.sci.fi> - -* Added time column to 'mysqladmin processes' to show how long a query - has taken or how long a thread has sleeped. - -1998-04-06 Michael Widenius <monty@monty.pp.sci.fi> - -* 'show variables' now gives the correct path for 'datadir'. -* Added logging and update_log to "show variables" - -1998-03-29 Michael Widenius <monty@analytik> - -* Added new type: YEAR. YEAR is stored on 1 byte with range 0, 1901-2155. -* Added new DATE type that is stored on 3 bytes instead of 4. All new - tables will created with the new date type if one doesn't use - --old-protocol. -* Fixed bug in record caches; One could get 'Error from table handler: #' - on some OS from some queries. - -1998-03-27 Michael Widenius <monty@monty.pp.sci.fi> - -* mysql (the command line tool) striped start space from new rows. - -1998-03-25 Michael Widenius <monty@monty.pp.sci.fi> - -* Added user level locks: GET_LOCK(string,timeout), RELEASE_LOCK(string) -* Fixed bug in range optimizer when using: - WHERE key_part_1 >= something and key_part_2 <= something_else -* Changed connect timeout to 3 seconds to make it somewhat harder - for crackers to kill mysqld trough telnet + TCP/IP. - -1998-03-24 Michael Widenius <monty@monty.pp.sci.fi> - -* new mysqld option --big-selects: - Allow big result sets by saving all temporary sets on file. - (Solves most 'table full' errors) - -1998-03-21 Michael Widenius <monty@monty.pp.sci.fi> - -* WHERE with string-column-key = constant-string didn't always find all rows - if the column had many values differing only with characters of the same sort - value (like e and é). -* Added opened_tables to 'show status'. -* Strings keys looked up with 'ref' was not compared case sensitively. -* Added flag '--big-selects' to avoid 'Table is full' errors. - Using this will slow down some queries thought. -* Added umask() to make log_files non-readable for normal users. -* Fixed some odd cases with queries that uses group functions where - the WHERE or HAVING didn't match anything. -* Ignore users with old password (8 byte) on startup if not using - --old-protocol. -* Changed name of the sql_memory allocation system and moved this to - the mysys library. - -1998-03-17 Michael Widenius <monty@monty.pp.sci.fi> - -* Added use of current_date, current_time and current_timestamp functions - without (). This automaticly made these reservered words :( - -Tue Mar 10 12:34:50 1998 Michael Widenius <monty@monty.pp.sci.fi> - -* Changed mysql_real_connect() to include possible db for faster connection - to a new db. -* select which matched all key fields returned the values in the same - case as the matched values instead of the found values. -* Release of 3.21.26 -* In DATE_FORMAT() PM and AM was swapped for hours 00 and 12. - -Mon Mar 9 14:15:00 1998 Michael Widenius <monty@monty.pp.sci.fi> - -* Added some tests to the table order optimizer to get some cases with - 'SELECT ... FROM many_tables' much faster. -* Added a retry loop around accept() to possible fix some problems on some - Linux machines. - -Fri Mar 6 01:18:47 1998 Michael Widenius <monty@monty.pp.sci.fi> - -* from_days(0) now returns "0000-00-00" -* Enchanted mysql_connect protocol to allow one to specify database - on connection. This will make MySQL twice as fast to connect to a database - for new clients. - -Thu Mar 5 18:09:45 1998 Michael Widenius <monty@monty.pp.sci.fi> - -* Updated record_cache code to be aligned for more speed. -* New tests to crash-me -* Extended the default max key size to 256. - -Wed Mar 4 00:02:16 1998 Michael Widenius <monty@monty.pp.sci.fi> - -* Fixed bug when using BLOB/TEXT in GROUP BY with many tables. - -Mon Mar 2 18:58:10 1998 Michael Widenius <monty@monty.pp.sci.fi> - -* A enum field that is not declared NOT NULL has NULL as default value. - (Before the default value was the first enum option) - -Tue Feb 24 20:11:30 1998 Michael Widenius <monty@monty.pp.sci.fi> - -* Fixed bug in the join optimizer code when using many part keys - on the same key: INDEX (Organisation,Surname(35),Initials(35)). - -Mon Feb 23 16:15:39 1998 Michael Widenius <monty@monty.pp.sci.fi> - -* One can now kill threads that are waiting for 'disk full'. -* Fixed some problems with UDF functions. -* ALTER TABLE + IGNORE now returns right number of affected rows. -* Fixed a bug when using 8 bytes long (alpha); filesort() didn't work. - Affects DISTINCT, ORDER BY and GROUP BY on 64 bit processors. - -Sat Feb 21 15:36:48 1998 Michael Widenius <monty@monty.pp.sci.fi> - -* Changed typedef string to my_string because of C++ new string class. -* now one can kill threads that's are waiting on 'disk full'. - -Fri Feb 13 23:19:23 1998 Michael Widenius <monty@monty.pp.sci.fi> - -* Release of MySQL 3.21.24 -* Fixed problem with LEFT JOIN and constant expressions in the ON part. - -Thu Feb 12 02:54:57 1998 Michael Widenius <monty@monty.pp.sci.fi> - -* Added much more descriptive error messages to mysqladmin if connect failed. -* Dynamic loadable functions. Based on source from: - Alexis Mikhailov <root@medinf.chuvashia.su> - -Thu Feb 5 15:19:14 1998 <monty@monty.pp.sci.fi> - -* One couldn't delete from a table if no one had done a select on the table. -* Fixed problem with range optimizer which many OR's on key parts inside - each other. - -Tue Feb 3 14:34:32 1998 <monty@monty.pp.sci.fi> - -* Changed default umask for new files from 0664 to 0660. - -Fri Jan 30 23:58:19 1998 <monty@monty.pp.sci.fi> - -* Release of MySQL 3.21.23 -* Changed ALTER TABLE to work with WIN32 (Win32 can't rename open files) - -Thu Jan 29 20:37:50 1998 <monty@monty.pp.sci.fi> - -* Fixed that the following symbols are not reserved words: - TIME DATE TIMESTAMP TEXT BIT ENUM NO ACTION CHECK YEAR MONTH DAY HOUR - MINUTE SECOND STATUS VARIABLES. -* Changed string handling in sql_yacc.yy and sql_lex.cc to be faster. -* Setting a TIMSTAMP to NULL in LOAD DATA INFILE... didn't set the current - time for the TIMESTAMP. -* Fixed that key conversions are tested in the WHERE clause -* LOAD DATA INFILE .... REPLACE INTO ... had wrong 'skipped' count - -Tue Jan 27 15:24:50 1998 <monty@monty.pp.sci.fi> - -* Added switch --skip-thread-prior for systems where mysqld's thread - scheduling doesn't work properly. At least BSDI 3.1 works better with - this! -* Added ODBC functions DAYNAME() and MONTHNAME(). -* Fixed unlikely(?) key optimizer bug when using ORs inside ANDs. - -Sat Jan 24 03:35:46 1998 <monty@monty.pp.sci.fi> - -* Release of 3.21.22 -* Added support of 'long constant strings' from ANSI SQL: - select 'first ' 'second'; -> 'first second'; - -Mon Jan 19 17:59:49 1998 <monty@monty.pp.sci.fi> - -* Fixed problem with Russian character set and LIKE. -* Fixed bug in ORDER BY on string formula with possible NULL values. -* Added functions DAYOFYEAR(), DAYOFMONTH(), MONTH(), YEAR(), WEEK(), - QUARTER(), HOUR(), MINUTE(), SECOND() and FIND_IN_SET(). -* Changed weighting, when using many key parts, in join optimizer to avoid - full joins for a couple of cases. - -Sun Jan 18 21:16:06 1998 <monty@monty.pp.sci.fi> - -* Removed that NULL = NULL is true. Now one must use IS NULL or IS NOT NULL - to test if a value is NULL. (This is according to ANSI SQL but may break - old applications that are ported from mSQL) - One can get the old behaviour by compiling with -DmSQL_COMPLIANT -* Fix of count(*) problems when the WHERE clause didn't match any records. -* Added function DAYOFMONTH() - -1998-01-14 Michael Widenius <monty@analytik> - -* Fixed mysqladmin.c to display only the used socket or TCP/IP port. - -Mon Jan 12 19:32:31 1998 <monty@monty.pp.sci.fi> - -* Changed SHOW FIELDS to return NULL as default value for TIMESTAMP - (This removes the DEFAULT "" entry for timestamps in mysqldump) -* Release of MySQL 3.21.21 -* Added commands SHOW STATUS and SHOW VARIABLES. -* Fixed optimizer bug when using - 'WHERE data_field=date_field2 and date_field2=constant' - -Sun Jan 11 05:07:59 1998 <monty@monty.pp.sci.fi> - -* Release of MySQL 3.21.20 -* Added long comments to MySQL /* */ -* Changed lex parsing to be a bit faster in some cases. - -Sat Jan 10 15:17:44 1998 <monty@monty.pp.sci.fi> - -* Fixed bug when using SELECT DISTINCT + NULL values. - -Fri Jan 9 16:45:26 1998 <monty@monty.pp.sci.fi> - -* Changed maximum table name and column name lengths from 32 to 64. -* Aliases can now be of 'any' length. - -Thu Jan 8 02:28:11 1998 <monty@monty.pp.sci.fi> - -* Now one gets an error if one tries to create an INDEX or UNIQUE - on a column that allows NULL values. (Before the column was silently - made NOT NULL). - -Wed Jan 7 23:19:11 1998 <monty@monty.pp.sci.fi> - -* Changed protocol (downward compatible) to mark if a column - is auto_increment or a timestamp. This is needed for the - new java driver. -* One can now in the clients check if a column is a automatic - TIMESTAMP or a AUTO_INCREMENT field. - -Sun Jan 4 20:10:21 1998 <monty@monty.pp.sci.fi> - -* Added update of big5 by jou@pdlc.ieo.nctu.edu.tw -* Added hebrew sorting order by Zeev Suraski. - -Thu Jan 1 12:57:04 1998 <monty@monty.pp.sci.fi> - -* Release of 3.21.19 -* unique key fields was not marked as unique keys in mysqlshow. -* Added function REVERSE() (by Zeev Suraski) -* Changed ni_range() to fixed a case of slow range searching. - -Wed Dec 31 15:46:25 1997 <monty@monty.pp.sci.fi> - -* Release of 3.21.18a -* Fixed problem with new filesort code from 3.21.18 on Linux - -Mon Dec 29 10:02:24 1997 <monty@monty.pp.sci.fi> - -* Added CROSS JOIN syntax. CROSS is now a reserved word -* USE database was not always written to output log. -* mysqladmin command 'status' doesn't increment 'Questions' anymore. - -Sun Dec 28 13:20:20 1997 <monty@monty.pp.sci.fi> - -* Recoded yacc/bison stack allocation to be even safer and allow MysQL - to handle even bigger expressions. - -Sat Dec 27 15:28:39 1997 <monty@monty.pp.sci.fi> - -* last_insert_id and used timestamp is now written to update log file. - timestamps, calculations with time and LAST_INSERT_ID() will now work - correctly when updating from the update log. - -Fri Dec 26 17:03:14 1997 <monty@monty.pp.sci.fi> - -* Give error message if client C functions are called in wrong order. -* Added automatic reconnect of clients for some cases. - -Mon Dec 22 00:25:34 1997 <monty@monty.pp.sci.fi> - -* Range optimizer didn't solve ranges of type: - key_part1= x AND key_part2 > y. This forced some ORDER BY queries to - do a full table scan when used with where like above. -* Small sort sets doesn't use temporary files anymore. - -Fri Dec 19 16:30:24 1997 <monty@monty.pp.sci.fi> - -* Release of MySQL 3.21.17a -* Fixed problem with compare of binary strings and blobs with ASCII - characters over 127. - -Thu Dec 18 00:33:25 1997 <monty@monty.pp.sci.fi> - -* Fixed core dump in first() when chaning some very specific AND and OR - key columns. -* Fixed lock problem: When freeing a read lock on a table with multiple - read locks, a thread waiting for write lock would have given the lock. - This shouldn't affect data integrity, but could possible make mysqld - to restart if one thread was reading data that another thread modified. -* LIMIT offset,count didn't work in INSERT ... SELECT. - -Wed Dec 17 12:35:11 1997 <monty@monty.pp.sci.fi> - -* optimized key block caching. This will be quicker than the old one when - using bigger key caches. - -Tue Dec 16 23:33:24 1997 <monty@monty.pp.sci.fi> - -* Changed bool to my_bool in some item structures to use less memory. -* Changed optimizer to use array references. This made the code 'nicer' - -Mon Dec 15 17:03:43 1997 <monty@monty.pp.sci.fi> - -* Release of Mysql 3.21.17 -* mysql: Added ouput of line number on errors when running batch. -* SELECT column,SUM(expr) now returns NULL for column when there is no - matching rows. - -Sun Dec 14 14:59:46 1997 <monty@monty.pp.sci.fi> - -* Fixed create problem with fixed length records of exactly 256 bytes. - (One couldn't insert more than 1 record in such a table). - -Fri Dec 12 18:31:32 1997 <monty@monty.pp.sci.fi> - -* Added ODBC and ANSI SQL style LEFT OUTER JOIN. - The following are new reserved words: LEFT, NATURAL, USING -* Changed use of table bits and key bits to use typedefs to make it easy - to extend join tables and keys to 64. -* The client library is now using the environment variable MYSQL_HOST as - the default host if it's defined. - -Wed Dec 10 01:29:11 1997 <monty@monty.pp.sci.fi> - -* Release of 3.21.16a -* Field type SET with 33-55 elements didn't work. -* Release of 3.21.16 -* Fixed bug in ALTER TABLE when copying from DATETIME to TIMESTAMP. - (All TIMESTAMP where set to current time). - -Tue Dec 9 14:53:15 1997 <monty@monty.pp.sci.fi> - -* Added function TIME_TO_SEC() - -Mon Dec 8 09:56:44 1997 <monty@monty.pp.sci.fi> - -* Allow empty strings as default values for BLOB and TEXT to be compatible with - mysqldump. -* Added ODBC 2.0 & 3.0 functions: POWER(), SPACE(), COT(), DEGREES(), RADIANS(), - ROUND(2 arg) and TRUNCATE(). -* Added optional (ignored) argument to CURRENT_TIME() and CURRENT_TIMESTAMP(). -* LOCATE() parameters where swapped according to ODBC standard. Fixed. -* Added detection of all ODBC 3.0 functions to crash-me -* In some cases default values was not used for NOT NULL fields. -* Timestamp wasn't updated in UPDATE SET... if the timestamp was used as - a value or in the WHERE clause. - -Sun Nov 30 04:06:31 1997 <monty@monty.pp.sci.fi> - -* Renamed version.h to mysql_version.h - -Sat Nov 29 10:50:28 1997 <monty@monty.pp.sci.fi> - -* Added dayofweek() for ODBC. -* Allow DATE '1997-01-01', TIME '12:10:10' and TIMESTAMP '1997-01-01 12:10:10' - formats required by ANSI SQL. - This has the unfortunate side-effect that one can't have columns named - DATE, TIME or TIMESTAMP anymore :( -* Changed net_write() to my_net_write() because of name conflict with sybase. -* Added --no-auto-rehash option to mysql. -* Added VARBINARY as synonym for VARCHAR BINARY - -Wed Nov 26 12:53:41 1997 <monty@monty.pp.sci.fi> - -* Added framework for multiple character sorting - -Tue Nov 25 04:03:29 1997 <monty@monty.pp.sci.fi> - -* Added extra client flag to mysql_real_connect to be compatible with - MyODBC. -* Zeev fixed bug in DATE_FORMAT: It forgot to reset the null marker. - -Mon Nov 24 20:19:18 1997 <monty@monty.pp.sci.fi> - -* Fixed problem with wrong result order when using all of - DISTINCT + JOIN + ORDER BY + LIMIT. - -Sun Nov 23 14:29:54 1997 <monty@monty.pp.sci.fi> - -* mysql: edit command now allows one to edit last query in a editor; - (patch by Zeev Suraski) -* Recoded all delete item to avoid use of stack space for deletes. - (For crash-me) -* Added command: SET SQL_LOG_OFF=1 to not log commands to standard log. - This will only affect users with process list privileges. - -Sat Nov 22 13:08:55 1997 <monty@monty.pp.sci.fi> - -* Added stack checking for crash-me :) - The following failed before: select 1+1+1+1+1+.... (687 times) - -Fri Nov 21 01:50:34 1997 <monty@monty.pp.sci.fi> - -* Added new patch for Chinese Big5 code. -* Change that blobs returns the max length for a blob instead of 8192 - to the client as field_length. - -Thu Nov 20 15:37:05 1997 <monty@monty.pp.sci.fi> - -* fixed bug in range-optimizer that crashed mysql on some queries. -* table and column name completion for mysql by - Zeev Suraski and Andi Gutmans -* Fixed problem with queries that didn't find any records: This happens only - when using multiple part keys where the first part is a number and some - other part is a char or varchar. -* Removed some wrong warning messages from range optimizer - -Wed Nov 19 16:41:14 1997 <monty@monty.pp.sci.fi> - -* Added new command REPLACE, which works like INSERT but replaces conflicting - records with the new record. REPLACE INTO TABLE ... SELECT ... works also. -* Added new commands: CREATE DATABASE db_name and DROP DATABASE db_name -* Added RENAME option to ALTER TABLE: ALTER TABLE name RENAME AS new_name - -Sun Nov 16 21:41:32 1997 <monty@monty.pp.sci.fi> - -* The thread stack was overwritten if one tried to create a table with too many - fields (more than 1000). -- Table scanning was a bit slower when using LOCK TABLE xxx WRITE. Fixed. - -Thu Nov 13 03:12:54 1997 <monty@monty.pp.sci.fi> - -* ALTER TABLE forgot BINARY attribute for strings and BLOBS -* Change comparision of strings to integer to compare as floats instead - of as integers. -* Added printing of Access denied errors to log. -* Fixed some not 100% portable typedefs in mysql_com.h -* Added Luuk de Boers defines for interval handling. - This isn't compleat yet. - -Wed Nov 12 00:28:58 1997 <monty@monty.pp.sci.fi> - -* Added automatic removal of 'ODBC function conversions': {fn now() } -* Added new function DATE_FORMAT(date_expr,format). The format string is the - same one that was previously integrated with from_unix_timestamp(). -* Changed from_unix_timestamp() to call function DATE_FORMAT() with format - element. - -Mon Nov 10 18:17:39 1997 <monty@monty.pp.sci.fi> - -* Added flag for stupid ODBC applications (like access) that wants found_rows - instead of affected_rows. -* Added basic functions for handling av ANSI INTERVAL. - -Sat Nov 8 01:20:03 1997 <monty@monty.pp.sci.fi> - -* compare with DATE and TIME with NULL didn't work. (IS NULL worked) -* Added many changes from the Win32 port. -* new function: DATE_ADD_MM(). - -Wed Nov 5 13:10:10 1997 Michael Widenius <monty@analytik> - -* SORTING on calculated DOUBLE values sorted on integer results instead. -* SELECT ... WHERE KEY=constant ORDER BY ... didn't use key to retrieve - records. This was slow because everything was sorted.. -* CHECK isn't a reserved word anymore. - -Mon Nov 3 07:55:47 1997 Michael Widenius <monty@monty.pp.sci.fi> - -* Allow start of mysqld.cc without a current database. -* Changed server version to include -debug and -log if compiled with debugging - and to show that one has a logging enabled. - -Sat Nov 1 13:08:00 1997 <monty@monty.pp.sci.fi> - -* Added missing expression 'NOT IN' -* Changed the place where HAVING should be. According to ANSI it should be - after GROUP BY but before ORDER BY. MySQL 3.20 had it wrongly last. -* Added Sybase command: USE DATABASE to start using another database. -* Fixed core dump when one had a wrong password in the password column. -* Added automatic adjusting of number of connections and table cache size - if the maximum number of files that can be opened are less than needed. - This should fix that mysqld doesn't crash even if one hasn't done a - ulimit -n 256 before starting mysqld. -* Added limit checks for create table. -* Added more checks of different errors from net_read for SCO port. - -Tue Oct 28 14:30:31 1997 <monty@monty.pp.sci.fi> - -* Fixed problem when using big table_caches; MySQL could previously only - open 256 files. - -Mon Oct 27 10:02:19 1997 <monty@monty.pp.sci.fi> - -* Added options LINE STARTING WITH to LOAD DATA INFILE and - SELECT ... into outfile. Now one can do: - - LOAD DATA INFILE '/tmp/syslog.sql' INTO TABLE uptime - FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED by "'" - LINES STARTING WITH 'VALUES (' LINES TERMINATED by ');\n' ignore 100 lines - -and - SELECT * from uptime into outfile '/tmp/syslog2.sql' - FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED by "'" - LINES STARTING WITH 'INSERT INTO uptime VALUES (' LINES TERMINATED by ');\n' - -* Added IGNORE # LINES to LOAD DATA INFILE. - -* Allow \N as a shorthand of NULL in SQL statements. - -Sun Oct 26 11:34:38 1997 <monty@monty.pp.sci.fi> - -* More memory checking. -* Fixed grouping of functions with many from tables. - -Sat Oct 25 01:46:27 1997 <monty@monty.pp.sci.fi> - -* New error message so one can check if the connection was lost while - the command was running or if the connection was down from the start. -* The mysql command tool now does a automatic reconnect if the connection - was lost when it does a query. -* new command: 'mysqladmin debug'. This forces the server to dump out some - useful information to stdout. Currently it prints all lock information. -* Rewrite lexer to be faster and more easy to extend. - -Fri Oct 24 13:57:06 1997 <monty@monty.pp.sci.fi> - -* Fixed bug when like on number key. -* Added --table option to mysql to print in table format. - Moved time and row information after query result. -* Added != as an alias for <>. - -Thu Oct 23 16:00:22 1997 <monty@monty.pp.sci.fi> - -* Added function VERSION() to make easier logs. - -Tue Oct 21 00:09:13 1997 <monty@monty.pp.sci.fi> - -* Relase of 3.21.12 -* Added memory checks to all string functions to return NULL if some - string gets bigger than max_allowed_packet. This is to make MySQL more - secure. -* Fixed core dump bug on range optimizer. -* In some cases doing a join + group + INTO OUTFILE, the result wasn't - grouped. -* Now SQL_BIG_TABLES + DISTINCT is also optimized. -* Changed the syntax of ALTER TABLE ... ALTER COLUMN ident SET DEFAULT ... - (The DEFAULT keyword wasn't allowed or required before). -* Added russian error messages. - -Mon Oct 20 04:10:53 1997 <monty@monty.pp.sci.fi> - -* LIKE with '_' as last character didn't work. Fixed -* Added many error checks for 'end of memory' -* Added ENCRYPT() function by Zeev Suraski. -* Fixed better FOREIGN KEY syntax skipping. - New reserved words: MATCH, FULL, PARTIAL - -Sun Oct 19 23:13:50 1997 <monty@monty.pp.sci.fi> - -* Force .log to logfile-name if one uses hostname as logfile. -* mysqld now allows ip and hostname to the --bind-address option. - -Sat Oct 18 22:02:36 1997 <monty@monty.pp.sci.fi> - -* Added "SET OPTION CHARACTER SET cp1251_koi8" to enable conversions off - data to/from different character sets. Currently cp1251_koi8 is the only - one, but it's now trivial to add others. - Conversions: strings in the query -> intern set - fields and items in result -> terminal set - One can get back to the old one with: - SET OPTION CHARACTER SET DEFAULT -* Lots of changes for Win95 port - -Fri Oct 17 15:29:44 1997 <monty@monty.pp.sci.fi> - -* Changed the create column syntax off NOT NULL to be after the DEFAULT value - as specified in the ANSI SQL standard. This will make mysqldump with - NOT NULL and default values incompatible with MySQL 3.20. -* New reserved words are: BOTH, FOR, LEADING and TRAILING -* Added a lot of function name alias so one can use the functions with - ODBC or ANSI SQL92 syntax. -* Fixed ALTER TABLE person ALTER COLUMN phone SET DEFAULT NULL syntax. -* Added CHAR and BIT as a synonyms for CHAR(1) -* Changed the name if the INTERVAL type to ENUM, because INTERVAL is used in - ANSI SQL. -* Added extended ANSI SQL TRIM() function. -* Added CURTIME(). - -Thu Oct 16 17:26:48 1997 <monty@monty.pp.sci.fi> - -* Fixed core dump when updating as user with only select privilige. - -Wed Oct 15 04:25:47 1997 <monty@monty.pp.sci.fi> - -* INSERT ... SELECT ... GROUP BY didn't work in some cases. On got - 'Invalid use of group function' -* When using LIMIT, SELECT now always uses keys instead of record scan. - This will give better performance on SELECT and a WHERE that matches many - rows. -* Added function last_insert_id() to retreive last auto_increment value. - This is for clients to ODBC that can't use the mysql_insert_id API function. -* Added option '--flush-logs' to mysqladmin. -* Added command 'status' to mysql. -* Moved some messages from libmysql.c to errmsg.c - -Mon Oct 13 18:38:01 1997 <monty@monty.pp.sci.fi> - -* Tested on BSDI 3.0 with the newest pthread library. -* Added new group functions: BIT_OR() and BIT_AND(). -* Added compatibility functions: CHECK, REFERENCES. -* Added BIT as a synonym for CHAR(1) to get better compatibility. -* Added option ALL to GRANT for better compatibility. (GRANT is still - a dummy fuction. -* CHECK is now a reserved word. - -Fri Oct 10 17:01:25 1997 <monty@monty.pp.sci.fi> - -* Added partly translated dutch messages. -* Fixed bug in ORDER BY and GROUP BY with NULL columns - -Thu Oct 9 10:26:47 1997 <monty@monty.pp.sci.fi> - -* Added test of create of table without columns. -* Release of 3.21.10 -* Fixed a couple of bugs in the range optimizer. Now test-select works. - -Tue Sep 30 02:40:42 1997 <monty@monty.pp.sci.fi> - -* Added new function: REPEAT(string,count). -* Added patch of support of Chinese(BIG5). -* Fixed awful slowdown of libmysql.c when configuring using '--with-debug=yes' - This affected all clients that got large results from the server. - (This didn't affect using --quick or mysql_use_result). - -Sun Sep 28 20:59:41 1997 <monty@monty.pp.sci.fi> - -* Made a new function: mysql_real_connect, that takes two extra arguments: - port and socket to use on connection. -* Added text types: TINYTEXT, TEXT, MIDDLETEXT and LONGTEXT. - These are actually blobs, but all searching is done text independent. - All old BLOB fields are now TEXT fields. -* LONG VARCHAR is a synonym for TEXT. LONG BINARY is a synonym for BLOB. -* 'LONG' is now a reserved word. - -Fri Sep 26 16:11:34 1997 <monty@monty.pp.sci.fi> - -* Release of 3.21.9 -* Fixed a couple of portable problems with include files. -* Fixed bug in range calculation that could return empty - set when searching on multiple key with only one entry (very rare). - -Wed Sep 24 15:51:37 1997 <monty@monty.pp.sci.fi> - -* Changed thread scope from PROCESS to SYSTEM. This should give better - scheduling (performance) on Solaris. -* Fixed duplicated free bug in sql_base with io_cache. - -Tue Sep 23 13:05:23 1997 <monty@monty.pp.sci.fi> - -* Allow also the old SELECT ... INTO OUTFILE syntax. -* Fixed bug with group by and select on key with many values. - -Mon Sep 22 14:54:00 1997 <monty@monty.pp.sci.fi> - -* mysql_fetch_lengths() returned sometimes wrong lengths when one used - mysql_use_result(). This affected at least some cases of mysqldump --quick. - -Sun Sep 21 20:50:07 1997 <monty@monty.pp.sci.fi> - -* Fixed memory leak bug in range optimizer. - -Sat Sep 20 00:03:51 1997 <monty@monty.pp.sci.fi> - -* Allow TIME, DATE and TIMESTAMP as column names. -* Fixed bug in optimization of WHERE const op field. - -Fri Sep 19 12:06:37 1997 <monty@monty.pp.sci.fi> - -* Fixed problem when sorting on NULL fields. -* Added handling of calculation of sum() functions. -* Added handling of trigometric functions: PI(), ACOS(), ASIN(), ATAN(), - COS(), SIN() and TAN(). -* Fixed sorting of big tables for 64 bit integers (Alpha). - -Fri Aug 29 13:06:32 1997 Michael Widenius <monty@analytik> - -* Added option --pid-file=# to mysqld -* Added date formating to from_unixtime(), originally by Zeev Suraski. - -Wed Aug 27 01:35:18 1997 <monty@monty.pp.sci.fi> - -* Fixed bug when using 'unsigned long' on Alpa. - -Tue Aug 26 03:23:28 1997 <monty@monty.pp.sci.fi> - -* Added option --bind-address to mysqld. -* Changed 'Access denied' to return username and password usage. -* Changed 'Access to database denied' to return username and database. - -Sun Aug 24 22:55:24 1997 Michael Widenius <monty@monty.pp.sci.fi> - -* Changed password crypt from 31 bits to 62 bits to make passwords more - secure. -* Changed protocol to allow for passing of mysql_errno to client. - -Fri Aug 22 18:14:00 1997 <monty@monty.pp.sci.fi> - -* Fixed bug in BETWEEN in range optimizer (Did only test = of the first - argument). - -Thu Aug 21 16:40:21 1997 <monty@monty.pp.sci.fi> - -* Version 3.21.6 -* Enabled range optimizer for update and delete. Now update and delete can - use keys again. -* Fixed bug when using unknown field in group clause. - -Tue Aug 19 00:49:13 1997 <monty@monty.pp.sci.fi> - -* The range optimizer is now enabled as default. Use msyqld --skip-new - to disable it. -* numerous small fixes to the range optimzer and a couple if fixes to - group and where handling. -* Added patch from JOERG_HENNE@Non-HP-Germany-om88.om.hp.com to allow - mit-threads to work on HPUX10. (This patch is regarded alpha) - -Fri Aug 15 02:29:21 1997 <monty@monty.pp.sci.fi> - -* Remove reverse lookup of hostnames because this takes 2 seconds - one some machines for every connection! - This can be enabled with the --secure option to mysqld. - -Thu Aug 14 22:40:15 1997 <monty@monty.pp.sci.fi> - -* remove HAVING -> WHERE optimization. To fix this one has to change - all Item_ref fields to Item_fields. -* Added patch for fast TCP/IP on FreeBSD. - -Wed Aug 13 17:14:50 1997 <monty@monty.pp.sci.fi> - -* Add optimizing of SELECT DISTINCT .... LIMIT # when there is no - GROUP or ORDER BY. -* Changed mysql to only print time information if not silent or if -vvv. -* Added polish error messages - -Sun Aug 10 11:31:10 1997 <monty@monty.pp.sci.fi> - -* new function: substring_index(), originally by Zeev Suraski. -* Added new option to mysqld: -O tmp_table_size=# -* Removed all use of PTHREAD_MUTEX_INIT and PTHREAD_COND_INIT for - porting to FreeBSD 3.0 and HPUX. - -Thu Aug 7 01:24:50 1997 <monty@monty.pp.sci.fi> - -* New function from_unixtime(timestamp) which returns a date string in - YYYY-MM-DD HH:MM:DD format. -* New function sec_to_time(seconds) which returns a string in H:MM:SS format. - -Sat Aug 2 17:18:22 1997 <monty@monty.pp.sci.fi> - -* Fixed some porting issues for OSF1 and for Alpha. - Now MySQL is know to configure on OSF1 with the Dec compiler, - after changeing one line in config.h: - #define SOCKET_SIZE_TYPE int - -Wed Jul 30 11:05:39 1997 <monty@monty.pp.sci.fi> - -* Added reverse check lookup of hostnames to get better security. -* Fixed some possible buffer overflows if one uses too long filenames. -* mysqld doesn't accept hostnames that starts with digits followed by a '.' - because the hostname may look like a IP. -* Added option --skip-networking to only allow socket connections. - (This will not work with MIT threads!) - -Tue Jul 29 10:38:55 1997 Michael Widenius <monty@monty.pp.sci.fi> - -* Removed wrong free() that killed the server on 'create/drop database'. -* Changed the name of some mysqld -O options to better names. -* Added option '-O join_cache_size=#'. -* Added option '-O max_join_size=#' to be able to set a limit how big queries - (in this case big = slow) one should be able to handle without specifying - 'SQL_OPTION OPTION_BIG_SELECTS=1'. - A # = is about 10 examined records. The default is 'unlimited'. -* When comparing a TIME, DATE, DATETIME or TIMESTAMP column to a - constant the constant is converted to a time value before comparing. - This will make it easier to get ODBC and particularly Access97 to work with - the above types. It should also make dates easier to use and the compares - should be quicker than before. -* added check of too long table names for alias. - -Mon Jul 21 16:09:47 1997 Michael Widenius <monty@monty.pp.sci.fi> - -* Applied patch from Jochen Wiedmann that fixes that query() in mysqlperl now - can take queries with \0 in it. - -Sat Jul 19 01:11:38 1997 Michael Widenius <monty@monty.pp.sci.fi> - -* Store of timestamp with 2 digit year YYMMDD didn't work. -* Fix that timestamp isn't automaticly updated if set in a update clause. -* Now the automatic timestamp field is the FIRST timestamp field. - -Thu Jul 3 13:34:26 1997 <monty@monty.pp.sci.fi> - -* Added check if database name is okey. -* Addded check if too long table names. -* SELECT * INTO OUTFILE, which didn't correctly if the outfile already existed. -* 'mysql' now shows thread id when starting or doing a reconnect. -* Changed the default sort buffer size from 2M to 1M. - -Mon Jun 30 10:18:39 1997 <monty@monty.pp.sci.fi> - -* mysqladmin: One can now do 'mysqladmin kill 5,6,7,8' -* Fixed 'Packets out of order' message. This error come sometimes when the - server was out of threads/memory. Now the correct message is retrieved by - the client. -* Added more checks with thread create for 'out of memory' errors. -* Added more checks if threads is killed to get faster kill. -* Changed the default record cache from 512K to 128K to get less problem on - systems with little memory. - -Sat Jun 28 00:18:02 1997 <monty@monty.pp.sci.fi> - -* When the max connection limit is reached, one extra connection by a user with - the PROCESS_ACL privilege is granted. - -Fri Jun 27 22:03:24 1997 <monty@monty.pp.sci.fi> - -* Added new mysqld option: -O backlog=# - -Tue Jun 24 22:08:58 1997 <monty@monty.pp.sci.fi> - -* Fixed SELECT DISTINCT when using 'hidden group'. For example: - SELECT DISTINCT MOD(some_field,10) FROM test GROUP BY some_field; -* Increased max packet size from 512K to 1024K for client. -* Removed a lot of unused functions - -Mon Jun 23 22:58:07 1997 <monty@monty.pp.sci.fi> - -* Changed key_parts to have own field for shortened keys. This gives much - nicer code in select. - -Thu Jun 19 13:09:14 1997 <monty@monty.pp.sci.fi> - -* ALTER TABLE now returns warnings from field conversions. - Changed all numerical fields to check for correct number and - increment warning counts if the value is wrong. - -Wed Jun 18 22:14:36 1997 <monty@monty.pp.sci.fi> - -* Fixed buffer overflow when retrieving big packets. - -Tue Jun 17 03:26:27 1997 <monty@monty.pp.sci.fi> - -* Port changed to 3306 (got it reserved from ISI). - -Mon Jun 16 15:46:42 1997 <monty@monty.pp.sci.fi> - -* All double are now rounded before storad as integer values. -* Fixed bug when using: SELECT WHERE A=const1 OR A=const2 OR A=const3, - and const1 = const3. In this case a key over A=const1 was wrongly used and - A=const2 wasn't used. -* Added a fix for Visual Fox Base so that any schema name from a table - specification is automaticly removed. - -Sun Jun 15 12:47:23 1997 <monty@monty.pp.sci.fi> - -* The thr_alarm array is now initialized based on number of connections -* Changed some memcpy() to bmove() to get rid of some warnings from purify. -* Changed the sql_yacc.c to drop schema name from table name. This is a crude - patch to get VFP - -Sat Jun 14 12:04:59 1997 <monty@monty.pp.sci.fi> - -* fixed missed ptr variable in filesort. -* Fixed wrong tablename and record count EXPLAIN. -* Changed the 'key use' test to prefere keys even more over full join. -* Fixed LIKE to work for binary strings. - -Fri Jun 13 13:38:14 1997 <monty@monty.pp.sci.fi> - -* New function char(num,....). - -Wed Jun 11 14:53:17 1997 <monty@monty.pp.sci.fi> - -* All field types tested with extrema values. date_time and timestamp - now require at least year, month and day on insert. - -Mon Jun 9 01:23:36 1997 <monty@monty.pp.sci.fi> - -* Added French error messages (by Therrien, Gilbert). English is still default. -* Added option '--skip-name-resolve' to get mysqld to use only IP's to - autenticate a host. 'localhost' will still be used for local UNIX sockets. -* Removed the between() function. On should use the 'col BETWEEN a AND b' - syntax instead. - -Sun Jun 8 11:05:46 1997 <monty@monty.pp.sci.fi> - -* New function ASCII. - -Sat May 31 01:00:18 1997 <monty@monty.pp.sci.fi> - -* host names are now compared case insensitive. - -Wed May 28 13:04:00 1997 <monty@monty.pp.sci.fi> - -* HAVING is added to WHERE if there is no grouping. -* MySQL now doesn't anymore have to use a extra temporary table when sorting - on functions or SUM functions. - -Tue May 27 00:54:51 1997 <monty@monty.pp.sci.fi> - -* Added function to print a item for debugging purposes. -* Fixed bug that one couldn't use 'table_name.field_name' in UPDATE. -* Removed init code with reset some of the mysqld -O variables to default after - they where set by start options. - -Thu May 22 14:52:26 1997 <monty@monty.pp.sci.fi> - -* Added varbinary syntax: 0x###### which can be used as a string (default) or a - number. - -Sun May 18 22:00:58 1997 <monty@bitch.sci.fi> - -* mysqldump: added options to lock tables and specify many tables to dump -* Add support of NULL fields in filesort -* SELECT with COUNT(),MIN() .... with no matching rows now returns 1 row. - -Sat May 17 22:06:29 1997 <monty@bitch.sci.fi> - -* New operator IN. This uses a binary search to find a match. -* Added 'SET OPTION SQL_BIG_TABLES= (0 | 1). Setting this to 1 will force - all temporary tables to disk. This will allow one to do big selects that - ordinary would give a 'table full' error. - -Fri May 16 18:53:21 1997 <monty@bitch.sci.fi> - -* New command LOCK TABLES table_name [alias] (READ | WRITE), .... - -Wed May 14 14:33:07 1997 <monty@bitch.sci.fi> - -* Renamed FIELD_TYPE_CHAR to FIELD_TYPE_TINY - -Mon May 12 09:54:24 1997 <monty@bitch.sci.fi> - -* Added command --log-update to get a update log for incremental backups. -* log file rotation for mysqld. -* log file for incremental backups -* new command: DESCRIBE SELECT .... -* Removed mysql_reload() and added mysql_refresh() instead. - Left a define to get old source to compile. - -Fri May 9 10:41:36 1997 <monty@bitch.sci.fi> - -* All functions now regards a binary type as 'sticky'. -* The time is now only requested once at start of each query. -* Splitted item_func.cc in two tables to get around that gcc uses too - much memory compiling it. -* Changed MIN() and MAX() to return the original type. -* Fixed bug in acl with anonymous user: Now if one gets accepted by the user - table as a empty user name, the user name is set to '' when checking against - the 'db' and 'host' tables. -* calculate all const expressions in the first optimizer pass. - -Tue May 6 19:16:56 1997 <monty@bitch.sci.fi> - -* Fixed ORDER BY bug when selecting on very small tables that made the - optimizer use a full join. - -Mon May 5 00:15:52 1997 <monty@bitch.sci.fi> - -* Added use of table alias in insert, delete and update. -* Removed FIELD_TYPE_TINY_BLOB, FIELD_TYPE_MEDIUM_BLOB, FIELD_TYPE_LONG_BLOB, - FIELD_TYPE_VAR_STRING from client code. -* Change syntax of SELECT .. WHERE ... INTO OUTFILE .. to the more standard - SELECT .. INTO OUTFILE 'name' WHERE ... - -Thu May 1 23:16:14 1997 <monty@bitch.sci.fi> - -* Added new API functions: - mysql_row_seek(),mysql_row_tell() and mysql_field_tell(). - mysql_field_seek() now returns old offset. -* Added expr BETWEEN expr2 AND expr3. - -Sun Apr 27 16:16:17 1997 <monty@bitch.sci.fi> - -* Changed range() detection to get queries on prefix to works faster. - Now SELECT name FROM table WHERE name="prefix" is quick even if there - are lots of rows where name starts with prefix. -* Fixed crash with shutdown and --log-isam -* Added group function STD() (standard derivation). -* mysql.cc: Fixed that NULL columns are always at least 4 wide for nicer output - of NULL values. -* Fixed that calculations that are not in GROUP BY works as expected. - (ANSI SQL extension) - Example: SELECT id,id+1 FROM table GROUP BY id - -Thu Apr 24 13:41:01 1997 Michael Widenius TcX DataKonsulter AB <monty@analytik> - -* Fixed convert bug which got mysqld to core dump with Aritmetic error on - Sparc-386 - -Wed Apr 23 12:11:05 1997 Michael Widenius TcX DataKonsulter AB <monty@analytik> - -* Added tty password to mysqlshow.c - -Tue Apr 22 15:44:11 1997 Michael Widenius TcX DataKonsulter AB <monty@analytik> - -* The test of using MYSQL_PWD was reversed. Now MYSQL_PWD is enabled as default - in the default release - -Sun Apr 20 14:36:39 1997 <monty@bitch.sci.fi> - -* Now one usually only have to give --basedir to mysqld. All other paths - are relative in a normal installation. -* BLOBs contained sometimes garbage when used with a SELECT on more than - one table and ORDER BY. -* Added option --unbuffered to mysql. (For new mysqlaccess) -* 'select *' without tables crashed server. -* When using overlapping (unnecessary keys) and join over many tables - the optimizer could get confused and return 0 records. -* Changed safe_mysqld to allow one to move installed releases. - -Sun Apr 13 10:40:50 1997 <monty@bitch.sci.fi> - -* Release 3.20.17 -* Added new function unix_timestamp([timestamp_column]) - -Sat Apr 12 11:27:57 1997 <monty@bitch.sci.fi> - -* Fixed memory over run bug when using selects with many brace levels. -* Change from_days() and weekday() to also take a full timestamp or - a datetime as argument. Before they only took a number of type YYYYMMDD or - YYMMDD. - -Wed Apr 9 13:22:24 1997 Michael Widenius <monty@bitch.sci.fi> - -* Changed stack usage to use less memory. -* All communication packages and row buffers are now alloced on demand. - The default communication buffers are now smaller than before. -* count(field) where field could have a NULL value didn't work. -* IS NULL and IS NOT NULL now work in the WHERE. -* BLOBs now work in the WHERE. -* Remove pre-space from numbers when writing decimal() coulmns to file. -* INSERT INTO ... SELECT .. WHERE could give the error 'Dupplicated field' - -Tue Apr 8 16:14:54 1997 Michael Widenius <monty@bitch.sci.fi> - -* Added commands SET OPTION SQL_SELECT_LIMIT=# to provide framework - for options and to get some ODBC things to work. -* Fixed bug in SELECT ... two tables ... GROUP BY -* Fixed bug in INSERT ... SELECT ... GROUP BY -* Fixed bug in acl: To use FILE_PRIV one also had to have SELECT PRIV - in the user grant table. -* Fixed fatal bug in ranged querie with OR when one part of the query didn't - have any matching records. - -Mon Apr 7 16:03:00 1997 Michael Widenius <monty@bitch.sci.fi> - -* Now connections are allowed even if hostname isn't found. - In this case all hostname checks are done on IP. -* When doing insert on timestamps, the timestamp was set to the - current time even if updated by a value. -* Fixed LOAD DATA.. that if one has COLUMN TERMINATED BY to be same as - LINE TERMINATED BY, then LINE TERMINATED BY is set to a empty string. - This wasn't a bug, but a common mistake when reading columns separated - with newlines. - -Sun Apr 6 22:37:53 1997 Michael Widenius <monty@bitch.sci.fi> - -* Changed definition of function ELT as it should have been: - ELT(index,element,element,element....) now returns the index:s - element in the list. The first element has index 1. - FIELD(find,string,string,string) searches after the 'find' string - in the string list and returns a index to the found string. - The strings are compared case insensitive. -* Added some tests to safe_mysqld to make it 'safer' - -Fri Apr 4 02:17:40 1997 Michael Widenius <monty@bitch.sci.fi> - -* When sorting the db grant table, host wasn't sorted. - -Wed Apr 2 03:00:14 1997 Michael Widenius <monty@bitch.sci.fi> - -* Fixed case 'WHERE key_num_column = "string"' -* LIKE was case sensitive in some places and case insensitive in other. - Now LIKE is always case insensitive. -* Fixed bug in select optimizer when using many tables with the same - column used as key to different tables. - -Sun Mar 30 21:22:39 1997 Michael Widenius <monty@bitch.sci.fi> - -* mysql.cc; Allow '#' anywhere on the line. - -Thu Mar 27 02:42:12 1997 Michael Widenius <monty@bitch.sci.fi> - -* Added new latin2 and Russian KOI8 character tables. -* Added support for a dummy GRANT command satisfy Powerbuilder. - -Wed Mar 26 03:03:07 1997 Michael Widenius <monty@bitch.sci.fi> - -* Release of 3.20.15 -* Removed possible loop when thread waits for command from client - and fcntl() fails. Thanks to Mike Bretz for finding this bug - -Tue Mar 25 18:03:15 1997 Michael Widenius <monty@bitch.sci.fi> - -* Changed alarm loop in mysqld.cc because shutdown didn't always - succeed in Linux. -* Removed use of termbits from mysql.cc This conflicted with glibc 2.0 -* Fixed syntax error in get_password.c (for BSD). Added flush of line. -* Added test if 'linux' style gethostbyaddr_r in mysqld.cc -* Fixed bug when doing a select as superuser without a database. -* Fixed bug when doing SELECT with group calculation to outfile. - -Mon Mar 24 16:03:01 1997 Michael Widenius <monty@bitch.sci.fi> - -* Release of 3.20.14 -* Added new function SOUNDEX() -* If one gives '-p' or -password to mysql or mysqladmin without an argument, - the password will be asked from the tty. - -Sun Mar 23 00:19:42 1997 Michael Widenius <monty@bitch.sci.fi> - -* Sometimes when doing a reconnect on a down connection this succeded - first on second try. Fixed by removing handling of SIGPIPE in client. -* When adding a auto_increment key with ALTER_TABLE on got the error: - 'Can't write, duplicate key'. - -Sat Mar 22 22:55:12 1997 Michael Widenius <monty@bitch.sci.fi> - -* AVG() gave too small value on some selects with GROUP BY and ORDER BY. - -Fri Mar 21 12:27:32 1997 Michael Widenius <monty@bitch.sci.fi> - -* Added new DATETIME type (by Giovanni Maruzzelli <maruzz@matrice.it>) -* Fixed that define 'DONT_USE_DEFAULT_FIELDS' works -* Added default password from MYSQL_PWD. (by Elmar Haneke) -* Changed C++ code to be compatible with Sun Workshop - -Thu Mar 20 12:28:06 1997 Michael Widenius TcX DataKonsulter AB <monty@analytik> - -* Changed to use a thread to handle alarms instead of signals on Solaris to - avoid race conditions. -* Fixed default length of signed numbers. (George Harvey <georgeh@pinacl.co.uk>) -* Added commando 'kill' to mysqladmin to kill a specific mysql thread. - -Wed Mar 19 12:21:33 1997 Michael Widenius TcX DataKonsulter AB <monty@analytik> - -* sql_base.cc: Allow anything for CREATE INDEX. - -Mon Mar 17 19:54:11 1997 Michael Widenius TcX DataKonsulter AB <monty@analytik> - -* Add prezeros when packing numbers to DATE, TIME and TIMESTAMP. -* Fixed the OR bug for good. - -Fri Mar 14 11:46:54 1997 Michael Widenius <monty@bitch.sci.fi> - -* Release 3.20.13 -* Added changes by bonis@kiss.de to allow WHERE const op field -* Fixed bug in mysql.c when reading long commands from batch. -* mysqldump.c - Changed newlines, return and ASCII 0 to "\n", "\r" and "\0", - to allow restoring of columns with these. - -Thu Mar 13 20:02:53 1997 Michael Widenius <monty@bitch.sci.fi> - -* Fixed bug in select with and-or levels. - -Mon Mar 10 04:04:03 1997 Michael Widenius <monty@bitch.sci.fi> - -* Added support for Slovenian characters. -* Fixed bug with limit and order by. -* Allow order and group on items that isn't in the select list. - -Sun Mar 9 00:21:36 1997 Michael Widenius <monty@bitch.sci.fi> - -* Added ANSISQL94 DATE and TIME types. Changed TIMESTAMP fields to work better - when updateing it with a number. - -Sat Mar 8 20:19:21 1997 Michael Widenius <monty@bitch.sci.fi> - -* Allow setting of timestamp values in INSERT. -* Fixed bug with SELECT ... WHERE ... = NULL. -* Added changes for glibc 2.0 - -Fri Mar 7 07:53:01 1997 Michael Widenius <monty@bitch.sci.fi> - -* Fixed bug in alter table when changeing a not null field to allow NULLs. -* Added HAVE_READDIR_R as a define which can be removed if one has - a broken readdir_r implementation (Sparc/Linux). - -Thu Mar 6 21:06:02 1997 Michael Widenius <monty@bitch.sci.fi> - -* Added some ANS92 synonyms as field types to CREATE TABLE. - CREATE TABLE now allows FLOAT(4) and FLOAT(8) to mean FLOAT and DOUBLE. - -Wed Mar 5 00:41:29 1997 Michael Widenius <monty@bitch.sci.fi> - -* Release 3.20.11 -* Added sync of records count in sql_update. This fixed slow updates on first - connection. (Thanks to Vaclav Bittner for the test) -* Changed temporary file prefix from UN to MY. -* When using SELECT .... INTO OUTFILE all temporary tables are ISAM instead of - HEAP to allow big dumps. -* Changed date functions to be 'string functions'. This fixed some 'funny' - side effects when sorting on dates. - -Tue Mar 4 23:07:03 1997 Michael Widenius <monty@bitch.sci.fi> - -* Changed FOREIGN KEY to not create a key. Now it's only for compability. -* Extended ALTER TABLE according to SQL92. -* Some minor compability changes. -* Added --port and --socket to all utility programs and mysqld. - -Sat Feb 15 01:27:51 1997 Michael Widenius <monty@bitch.sci.fi> - -* Added Oracle command DESCRIBE (DESC) as a synomym for some SHOW commands: - DESC table_name <==> SHOW FIELDS FROM table_name - DESC table_name column <==> SHOW FIELDS FROM table_name LIKE 'column' - DESC table_name 'column' <==> SHOW FIELDS FROM table_name LIKE 'column' - mysql.cc thought that tinyblob, mediumblob and longblob was numerical. - (Was right adjusted) - -Thu Feb 13 00:49:29 1997 Michael Widenius <monty@bitch.sci.fi> - -* mediumblob didn't work. -* Fixed safe_mysqld and make_binary_distribution to work better. -* ALTER TABLE and changeing a BLOB to a CHAR() added some garabage at - string end. - -Wed Feb 12 16:10:49 1997 Michael Widenius <monty@bitch.sci.fi> - -* new insert type: INSERT INTO ... SELECT ....; - -Tue Feb 11 12:58:36 1997 Michael Widenius <monty@bitch.sci.fi> - -* Fixed some defines to get mysql to compile on freebsd 2.0 (intel) -* Removed all _A() from prototype declaration. -* Removed use of ulong in mysql.h and mysql_com.h -* Changed mysqldump to dump keynames. -* SELECT ... INTO OUTFILE 'test' create the file in the base directory - instead in database directory (if one didn't give a full path) -* A primary key is now defined as a key with name PRIMARY or the first - unique key if there doesn't exist a key with name PRIMARY. - -Mon Feb 10 00:40:48 1997 Michael Widenius <monty@bitch.sci.fi> - -* Fixed leak bug when using LOAD DATA into a blob with is sometimes NULL. -* DROP TABLE can now take a list of tables. -* If a databas was crashed, in some cases a read of the wrong record - was used as a 'end of file', instead of returning an error. - -Sat Feb 8 00:16:07 1997 Michael Widenius <monty@bitch.sci.fi> - -* merged structs field_t and FIELD to FIELD. - -Fri Feb 7 12:49:01 1997 Michael Widenius <monty@bitch.sci.fi> - -* version 3.20.9 -* Alter table didn't copy null bit. This resulted that NULL fields where - always NULL. -* CREATE didn't take numbers as DEFAULT. - -Wed Feb 5 13:28:19 1997 Michael Widenius <monty@bitch.sci.fi> - -* New scripts 'add_file_priv' which add the new field 'file_priv' - to the user table. This scripts must be executed if one wants to - use the new SELECT ... INTO and LOAD DATA INFILE... commands - with a version of mysql less than 3.20.7. -* Found bug in locking code when another thread got a table opened - by another thread. This could make a thread block forever wating - for a write lock. - -Tue Feb 4 00:57:24 1997 Michael Widenius <monty@bitch.sci.fi> - -* Changed select_test.c and insert_test.c to include config.h - -Mon Feb 3 00:42:08 1997 Michael Widenius <monty@bitch.sci.fi> - -* Add an optional keyname for all key declarators. - -Sat Feb 1 19:02:43 1997 Michael Widenius <monty@bitch.sci.fi> - -* Added command 'status' to mysqladmin for short logging. -* Increased max keys to 16 and max key parts to 15. - -Fri Jan 31 00:05:23 1997 Michael Widenius <monty@bitch.sci.fi> - -* Added ANSI92 extended ALTER TABLE statement. -* Changed all locking code to detect ALTER table after one got a lock. - Tables are automaticly reopened if ALTERed. -* Changed some structs to classes to get better code when using CREATE TABLE. - -Thu Jan 30 01:12:13 1997 Michael Widenius <monty@bitch.sci.fi> - -* Added new privilege to the user grant table: file_priv -* Added some compitibility changes to mysql.cc -* Added new syntax for creating keys with is a sub part of some field. -* Did a lot of changes to get around bug when comparing fields of - different lengths. Hope I didn't break something else :) -* Added long options to mysqldump. -* Added new function NOW(). - -Wed Jan 29 15:51:22 1997 Michael Widenius <monty@bitch.sci.fi> - -* Added option -k for mysqlshow to get key info for table. -* Changed some definitions from int to uint in mysql.h to get fewer warning - with prolint. - -Mon Jan 27 02:01:29 1997 Michael Widenius <monty@bitch.sci.fi> - -* Added sql command 'load data infile...' for export from textfiles. -* Added new API function mysql->info to pass info to client. -* Added INTO OUTFILE as option to select to get result to file. - -Fri Jan 24 14:56:19 1997 Michael Widenius <monty@bitch.sci.fi> - -* Relase 3.20.5-beta -* Got first version to work which MIT-threads. -* Added long options to mysqld -* mysqld now starts without system locking if compiled with MIT threads. -* Added new sql function RAND([init]) -* Changed sql_lex to handle \0 unquoted, but the client can't send - the query through the C api, because it takes a str pointer. - one have to use mysql_real_query() to send the query. - -Thu Jan 23 00:33:26 1997 Michael Widenius <monty@bitch.sci.fi> - -* Added API function: mysql_get_client_info -* mysqld now uses the N_MAX_KEY_LENGTH from nisam.h as the max allowed key - length. -* The following now works: "select filter_nr,filter_nr from filter order by - filter_nr" - Before you got the error: "Column: 'filter_nr' in order clause is ambiguous" - -Wed Jan 22 14:48:58 1997 Michael Widenius <monty@bitch.sci.fi> - -* Changed fctnl flag O_NDELAY to O_NONBLOCK (Posix, and to get MIT threads - to work) - -Tue Jan 21 12:31:17 1997 Michael Widenius <monty@bitch.sci.fi> - -* mysql now outputs \0 \t \n and \\ when writing tab separated output. - when encountering ascii 0, tab, newline or \. This is to allow printing of - binary data in a portable format. - To get old behavior use -r (or --raw). -* Added long options to mysqladmin, mysql and mysqlshow. -* Added german error messages (60 of 80 error messages translated) -* Added new api function: mysql_fetch_lengths(MYSQL_RES *) which - returns a array of of column lengths (of type uint). - -Sat Jan 18 23:59:53 1997 Michael Widenius <monty@bitch.sci.fi> - -* Fixed bug with IS NULL in where clause. - -Fri Jan 17 12:14:38 1997 Michael Widenius <monty@bitch.sci.fi> - -* Changed the optimizer a little to get better results when searching on a key - part. -* Added select option STRAIGHT_JOIN to tell the optimizer that it should join - tables in the given order. - -Thu Jan 16 00:55:41 1997 Michael Widenius <monty@bitch.sci.fi> - -* Added support of comment starting with '--' in mysql.cc (Postgres syntax) -* You can now have select_expressions and table columns in a select which - are not used in the group part. This makes it efficient to implement lookups. - If the not used column is not a constant for the group the column value - is unspecified. - Example: SELECT id,lookup.text,sum(*) FROM test,lookup - WHERE test.id=lookup.id group by id; - -* Fixed bug in sum(function) (Could make core dump) -* Changed auto_increment according to SQL_SYNTAX: - INSERT into table (auto_field) values (0) inserted 0, but the SQL_SYNTAX - statied it should insert a auto_incremnt value. - -Wed Jan 15 10:42:09 1997 Michael Widenius <monty@bitch.sci.fi> - -* mysqlshow.c: Added number of records in table. Had to change the client code a - little to fix this. -* mysql now allows double '' or "" in strings for embedded ' or ". -* Changed copyright text in mysqlshow and mysqladmin. - -Mon Jan 13 02:33:09 1997 Michael Widenius <monty@bitch.sci.fi> - -* Relase 3.20.3 -* Using the new readline library from bash. -* Updated a lot of text files. -* safe_mysqld and mysql.server changed to be more compatible between the - source and the binary releases. - -Sun Jan 12 18:23:30 1997 Michael Widenius <monty@bitch.sci.fi> - -* LIMIT takes now one or two numerical arguments. - If one argument the argument indicates the maximum number of rows in a result. - If two arguments the first arguments says the offset to the first row to return, - the second is the maximum number of rows. - With this it's easy to do a poor mans next page/previous page www application. -* Changed name of SQL function FIELDS to ELT. -* Made SHOW COLUMNS a synonym for SHOW FIELDS. - Added compatibility syntax FRIEND KEY to create table. This creates in mysql - a non unique key on the given columns. -* Added CREATE INDEX and DROP INDEX as compatibility functions. In mysql - CREATE INDEX only checks if the index exists and gives an error if it doesn't - exists. DROP INDEX always succeeds. - -Sat Jan 11 00:44:29 1997 Michael Widenius <monty@bitch.sci.fi> - -* mysqladmin.c: Added client version to version info. - -Fri Jan 10 20:30:04 1997 Michael Widenius <monty@bitch.sci.fi> - -* Fixed core dump bug in sql_acl (core on new connection). -* Removed host,user and db tables from database test in the distribution. -* FIELD_TYPE_CHAR can now be signed (-128 - 127) or unsigned (0 - 255) - Before it was always unsigned. - -Thu Jan 9 00:02:03 1997 Michael Widenius <monty@bitch.sci.fi> - -* Changed name from mysqllib to mysqlclient for mysql client lib. -* The following failed: concat(1,concat(2),2). - Could not call a variable argument function in a variable argument count - function. Fixed. - -Wed Jan 8 15:58:49 1997 Michael Widenius <monty@bitch.sci.fi> - -* weekday() returned wrong day 6 of 7 times. - -Mon Jan 6 23:49:31 1997 Michael Widenius <monty@bitch.sci.fi> - -* changed a lot of source to get mysqld to be compiled with SUNPRO compiler. -* sql functions must now have a '(' directly after the function name. - user '(' is now regarders as an identifier and a '(' - -Fri Jan 3 12:18:14 1997 Michael Widenius <monty@bitch.sci.fi> - -* Fixed possible bug when sorting with float and double. - Changed static sort_length to thread variable. This may have caused some - big sorts to fail when running two simultaneous sorts. -* Changed sql function INTERVALL() to INTERVAL(). - -Wed Jan 1 16:18:30 1997 Michael Widenius <monty@bitch.sci.fi> - -* Added some portability files for testing with RTS threads. -* Lot of changes for configure. - -Sun Dec 29 13:26:52 1996 Michael Widenius <monty@bitch.sci.fi> - -* Remove Makefile-linux-pl and Makefile-solaris-pl from the binary distribution. - Now only Makefile.PL is needed. - -Sat Dec 28 22:41:09 1996 Michael Widenius <monty@bitch.sci.fi> - -* Fixed that insert with a timestamp set to NULL works. (This is for a cleaner - syntax) - -Fri Dec 27 01:28:02 1996 Michael Widenius <monty@bitch.sci.fi> - -* mysqld now has english & swedish error messages. -* unireg files moved to sql directory changed to c++. - -Thu Dec 26 11:57:57 1996 Michael Widenius <monty@bitch.sci.fi> - -* mysqld: Added option 'b' for mysql basedir. All given directories is - prefixed with this if not given with hard path. - added option '-L' (language). Default is 'english/' - Moved all unireg files to sql directory. - -Fri Dec 20 11:05:37 1996 Michael Widenius TcX DataKonsulter AB <monty@ozelot> - -* Changed lex to allow a database name, table name and field name to start with - number or '_'. - -* mysqldump should now be able to dump all field types. - Changed 'show fields from table' to be fully compatible with create. -* Some bugs when parsing 'create table' fixed. (Blobs and timestamps was effected) -* Fixed one possible dead lock bug when using many tables. -* Changed a lot for configure - -Sun Dec 15 02:29:53 1996 Michael Widenius <monty@bitch.sci.fi> - -* Added new functions: INSERT(),RTRIM(),LTRIM(),FORMAT(). - -* New relase 3.19.5 -* Added functions DATABASE(),USER(),POW(),LOG10() (needed for ODBC). - -Sat Dec 14 10:10:42 1996 Michael Widenius <monty@bitch.sci.fi> - -* In a WHERE with a ORDER BY on fields from only one table the table is - now preferred as first table in a multi-join. -* HAVING and IS NULL or IS NOT NULL now works. -* a group on one column and a sort on a group function (SUM,AVG...) didn't - work together. Fixed. - -Fri Dec 13 07:20:47 1996 Michael Widenius <monty@bitch.sci.fi> - -* mysqldump: Didn't send password to server. - -* New relase 3.19.4 -* Fixed horrible locking bug when inserting in one thread and reading - on another thread. -* Fixed one-off decimal bug. 1.00 was outputed as 1.0 -* Added attribute 'Locked' to process list as info if a query is - locked by another query. -* Fixed full magic timestamp. Timestamp length may now be 14,12,10,8,6,4 or 2. - -Thu Dec 12 18:14:57 1996 Michael Widenius <monty@bitch.sci.fi> - -* sort on some number functions could be sorted wrong on last number. -* if(arg,syntax_error,syntax_error) crashed. -* added functions ceiling() and round(), exp(), log() and sqrt() -* enchanted BETWEEN to handle strings. - -Wed Dec 11 09:09:02 1996 Michael Widenius <monty@bitch.sci.fi> - -* MYODBC: Sometimes password test failed because of faulty charactermap in - windows. - -Mon Dec 9 12:50:56 1996 Michael Widenius <monty@bitch.sci.fi> - -* new relase 3.19.3 -* Fixed that select with grouping on blob's doesn't return wrong blob info. - grouping, sorting and distinct on blobs will not yet work as expected - (Probably it will group/sort by the first 7 characters in the blob) - Groping on formulas with a fixed string size (use mid on blob) should work. -* When doing a full join (no direct keys) on multiple tables with blob fields, - the blob was garbage on output. -* Fixed distinct with calculated columns. - -Sun Dec 8 19:53:24 1996 Michael Widenius <monty@bitch.sci.fi> - -* Fixed bug when allocation string for group -* new release 3.19.2 -* mysqldump.c: Didn't output ' around blobs. - -Sat Dec 7 13:00:43 1996 Michael Widenius <monty@bitch.sci.fi> - -* Added user flag to mysqldump & mysqlshow. -* ODBC: Added full support of SQLGetInfo(). Fixed limit bug (from 1.0.3). - myodbc-1.0.4 released - -Fri Dec 6 01:35:22 1996 Michael Widenius <monty@bitch.sci.fi> - -* ODBC: Added more support SetStmtOptions(). Added more debugging code - myodbc-1.0.3 released - -Tue Dec 3 22:12:30 1996 Michael Widenius <monty@bitch.sci.fi> - -* Added 'max_connections' and 'table_cache' as start variables to mysqld. -* Changed weights in join optimizer: Now prefers to use keys even more: - Before the optimizer would prefer to do a full join on small tables - (< 300 records), even if there was a usable key. - -Mon Dec 2 00:17:42 1996 Michael Widenius <monty@bitch.sci.fi> - -* new release 3.19.1 -* Fixed bug when joining tables without keys and null fields and varchars. - (mysqld hang) -* Fixed output of 'mysql show'. All fields was 'unsigned zerofill'. - -* new release 3.19.0 -* Added new column specifier AUTO_INCREMENT. -* Changed format of sql command 'show fields'. -* Changed mysqlshow to use sql command 'show fields' to get more info. -* Added synonym RLIKE for REGEXP to be compatible with mSQL - -Sun Dec 1 12:53:05 1996 Michael Widenius <monty@bitch.sci.fi> - -* item_func.cc (fix_fields): Fixed new bug when calculation and levels. - Crashed stack when optimizing where! (fatal bug in 3.18.1) - -Fri Nov 29 00:32:09 1996 Michael Widenius <monty@bitch.sci.fi> - -* Distribution 3.18.1 -* Fixed optimizeing bug. -* New ODBC version with traceing in all functions with isn't supported yet - for easier debugging. Added NO WARRANTY info. - Released as 1.0.2 - -Wed Nov 27 17:18:51 1996 Michael Widenius TcX DataKonsulter AB <monty@ozelot> - -* Added Henry Spencer's regexp in 'field REGEXP string'. Can only be used - in select_expression or HAVING until I fix the where clause. - -Mon Nov 25 20:01:05 1996 Michael Widenius TcX DataKonsulter AB <monty@ozelot> - -* Created files: CREDITS, PUBLIC. Updated FAQ, README, TODO, SQL_SYNTAX... -* Done a lot of testing on HAVING. - -Sun Nov 24 00:45:07 1996 Michael Widenius <monty@bitch.sci.fi> - -* mysql didn't stop on error in batch mode even if -f wasn't used. -* Fixed DBD Makefile.PL for linux -* Added a function.tst & function.res (test and result file of mysql functions) -* libmysql.c: Added some checking for calls after connection has gone done. -* Implemented HAVING with full expr syntax -* Changed operators '=, - -Sat Nov 23 20:52:42 1996 Michael Widenius <monty@bitch.sci.fi> - -* SQL_SYNTAX added 'like' as a boolean expression in select. -* mysqladmin.c: 'mysqladmin garbage' didn't give an error. -* sql_insert.cc: If one read a deleted record and did a insert with all fields - then the new record was marked deleted. -* perl DBI interface ported. - -Thu Nov 21 00:58:44 1996 Michael Widenius <monty@bitch.sci.fi> - -* mysql only used the TCP connection, no socket was ever created -* There was a bug in when reading from getenv(MYSQL_TCP_PORT) -* Added some more start-logging to check for port & socket. -* If something got wrong at startup some threads was kept alive in Linux -* If argument -h to mysqld is a relative path, change it to './' -* Search after the 'unireg' directory from: current dir, - mysqld program dir/.. and in env(MY_BASEDIR_VERSION) -* Added longlong support to Linux -* Added copyright notices to all files. Everything should be ready for - distribution. - -Wed Nov 20 19:03:02 1996 Michael Widenius <monty@bitch.sci.fi> - -* Added function IF. -* Added select without FROM clause (for easy test of functions) - -Tue Nov 19 11:48:55 1996 Michael Widenius <monty@bitch.sci.fi> - -* mysql.c: Sometimes 'in-string' was not initialized. -* linux distribution - -Mon Nov 18 13:47:09 1996 Michael Widenius <monty@bitch.sci.fi> - -* Fixed blob:s to work (as varchar) in ODBC (myodbc-1.0.1.zip) -* Added option -O to set buffer sizes to mysqld - -Wed Nov 13 15:21:14 1996 Michael Widenius <monty@monty.pp.sci.fi> - -* New sql functions: REPLACE, LCASE and UCASE -* hacked search on '%xxx' to work. - -Tue Nov 12 00:52:35 1996 Michael Widenius <monty@monty.pp.sci.fi> - -* mysql.cc: Fixed problems with strings containing not backslashed ' or ". - -Mon Nov 11 14:52:30 1996 Michael Widenius <monty@monty.pp.sci.fi> - -* added braces to where clause. Change where to use items. - -Wed Nov 6 00:17:37 1996 Michael Widenius <monty@analytikerna.se> - -* added PRIMARY KEY, KEY and UNIQUE to sql create. diff --git a/sql/Makefile.am b/sql/Makefile.am index 17930242dc4..a589f1379f9 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -21,27 +21,29 @@ MYSQLDATAdir = $(localstatedir) MYSQLSHAREdir = $(pkgdatadir) MYSQLBASEdir= $(prefix) INCLUDES = @MT_INCLUDES@ \ - @bdb_includes@ @innodb_includes@ @gemini_includes@ \ + @bdb_includes@ @innodb_includes@ \ -I$(srcdir)/../include \ -I$(srcdir)/../regex \ - -I$(srcdir) -I../include -I.. -I. + -I$(srcdir) -I../include -I. $(openssl_includes) WRAPLIBS= @WRAPLIBS@ SUBDIRS = share libexec_PROGRAMS = mysqld noinst_PROGRAMS = gen_lex_hash -gen_lex_hash_LDFLAGS = @NOINST_LDFLAGS@ -LDADD = ../isam/libnisam.a \ - ../merge/libmerge.a \ +gen_lex_hash_LDFLAGS = @NOINST_LDFLAGS@ +LDADD = @isam_libs@ \ ../myisam/libmyisam.a \ ../myisammrg/libmyisammrg.a \ ../heap/libheap.a \ + ../vio/libvio.a \ ../mysys/libmysys.a \ ../dbug/libdbug.a \ ../regex/libregex.a \ ../strings/libmystrings.a + mysqld_LDADD = @MYSQLD_EXTRA_LDFLAGS@ \ - @bdb_libs@ @innodb_libs@ @gemini_libs@ \ - $(LDADD) $(CXXLDFLAGS) $(WRAPLIBS) @LIBDL@ + @bdb_libs@ @innodb_libs@ @pstack_libs@ \ + @innodb_system_libs@ \ + $(LDADD) $(CXXLDFLAGS) $(WRAPLIBS) @LIBDL@ @openssl_libs@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \ item_strfunc.h item_timefunc.h item_uniq.h \ item_create.h mysql_priv.h \ @@ -49,37 +51,37 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \ sql_manager.h sql_map.h sql_string.h unireg.h \ field.h handler.h \ ha_isammrg.h ha_isam.h ha_myisammrg.h\ - ha_heap.h ha_myisam.h ha_berkeley.h ha_innobase.h \ - ha_gemini.h opt_range.h opt_ft.h \ + ha_heap.h ha_myisam.h ha_berkeley.h ha_innodb.h \ + opt_range.h opt_ft.h \ sql_select.h structs.h table.h sql_udf.h hash_filo.h\ - lex.h lex_symbol.h sql_acl.h sql_crypt.h md5.h \ - log_event.h mini_client.h sql_repl.h slave.h \ - stacktrace.h -mysqld_SOURCES = sql_lex.cc \ + lex.h lex_symbol.h sql_acl.h sql_crypt.h \ + log_event.h mini_client.h sql_repl.h slave.h \ + stacktrace.h sql_sort.h sql_cache.h set_var.h +mysqld_SOURCES = sql_lex.cc sql_handler.cc \ item.cc item_sum.cc item_buff.cc item_func.cc \ item_cmpfunc.cc item_strfunc.cc item_timefunc.cc \ thr_malloc.cc item_create.cc \ field.cc key.cc sql_class.cc sql_list.cc \ - net_serv.cc violite.c net_pkg.cc lock.cc my_lock.c \ + net_serv.cc net_pkg.cc lock.cc my_lock.c \ sql_string.cc sql_manager.cc sql_map.cc \ mysqld.cc password.c hash_filo.cc hostname.cc \ - convert.cc sql_parse.cc sql_yacc.yy \ + convert.cc set_var.cc sql_parse.cc sql_yacc.yy \ sql_base.cc table.cc sql_select.cc sql_insert.cc \ - sql_update.cc sql_delete.cc sql_do.cc \ + sql_update.cc sql_delete.cc uniques.cc sql_do.cc \ procedure.cc item_uniq.cc sql_test.cc \ log.cc log_event.cc init.cc derror.cc sql_acl.cc \ - unireg.cc \ + unireg.cc des_key_file.cc \ time.cc opt_range.cc opt_sum.cc opt_ft.cc \ records.cc filesort.cc handler.cc \ ha_heap.cc ha_myisam.cc ha_myisammrg.cc \ - ha_berkeley.cc ha_innobase.cc ha_gemini.cc \ + ha_berkeley.cc ha_innodb.cc \ ha_isam.cc ha_isammrg.cc \ sql_db.cc sql_table.cc sql_rename.cc sql_crypt.cc \ sql_load.cc mf_iocache.cc field_conv.cc sql_show.cc \ sql_udf.cc sql_analyse.cc sql_analyse.h sql_cache.cc \ - slave.cc sql_repl.cc \ + slave.cc sql_repl.cc sql_union.cc \ mini_client.cc mini_client_errors.c \ - md5.c stacktrace.c + stacktrace.c repl_failsafe.h repl_failsafe.cc sql_olap.cc gen_lex_hash_SOURCES = gen_lex_hash.cc gen_lex_hash_LDADD = $(LDADD) $(CXXLDFLAGS) @@ -118,6 +120,11 @@ lex_hash.h: lex.h gen_lex_hash.cc sql_yacc.h # Hack to ensure that lex_hash.h is built early sql_lex.o: lex_hash.h +# For testing of udf_example.so; Works on platforms with gcc +# (This is not part of our build process but only provided as an example) +udf_example.so: udf_example.cc + $(CXXCOMPILE) -shared -o $@ $< + #distclean: # rm -f lex_hash.h diff --git a/sql/cache_manager.cc b/sql/cache_manager.cc index 9ea25315f8c..307fe331e5c 100644 --- a/sql/cache_manager.cc +++ b/sql/cache_manager.cc @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -18,7 +18,7 @@ #pragma implementation /* gcc: Class implementation */ #endif -#include <global.h> +#include <my_global.h> #include <my_sys.h> #include "cache_manager.h" @@ -117,7 +117,7 @@ void *cache_manager::alloc(uint size) { void *llist; void *abs_ptr; - + size=ALIGN_SIZE(size+HEADER_LENGTH+SUFFIX_LENGTH); if (!(llist = find_in_llist(size))) { @@ -127,7 +127,7 @@ void *cache_manager::alloc(uint size) } size_of_found_block=int4korr((char*) llist) & ALLOC_MASK; // if (size_of_found_block < SMALLEST_BLOCK) - + abs_ptr = link_into_abs(llist); return abs_ptr; } diff --git a/sql/cache_manager.h b/sql/cache_manager.h index fc3b8f7016a..d422a86ea8e 100644 --- a/sql/cache_manager.h +++ b/sql/cache_manager.h @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -59,7 +59,3 @@ class cache_manager { bool *dealloc(void *); /* Deallocate blocks (with *ptr_arg) */ void clear(void); /* Clear the cache */ }; - - - - diff --git a/sql/convert.cc b/sql/convert.cc index 2c8b775dca2..e4ae13d1e07 100644 --- a/sql/convert.cc +++ b/sql/convert.cc @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -27,7 +27,7 @@ #include "mysql_priv.h" /**************************************************************************** -** Convert tables + Convert tables ****************************************************************************/ /* Windows cp1251->koi8 and reverse conversion by Timur I. Bakeyev <translate@bat.ru> */ @@ -397,19 +397,19 @@ static unsigned char win1251ukr_koi8_ukr[256] = { ****************************************************************************/ -CONVERT conv_cp1251_koi8("cp1251_koi8", cp1251_koi8, koi8_cp1251); +CONVERT conv_cp1251_koi8("cp1251_koi8", cp1251_koi8, koi8_cp1251, 1); #ifdef DEFINE_ALL_CHARACTER_SETS -CONVERT conv_cp1250_latin2("cp1250_latin2", t1250_til2, til2_t1250); -CONVERT conv_kam_latin2("kam_latin2", tkam_til2, til2_tkam); -CONVERT conv_mac_latin2("mac_latin2", tmac_til2, til2_tmac); -CONVERT conv_macce_latin2("macce_latin2", tmacce_til2, til2_tmacce); -CONVERT conv_pc2_latin2("pc2_latin2", tpc2_til2, til2_tpc2); -CONVERT conv_vga_latin2("vga_latin2", tvga_til2, til2_tvga); -CONVERT conv_koi8_cp1251("koi8_cp1251", koi8_cp1251, cp1251_koi8); +CONVERT conv_cp1250_latin2("cp1250_latin2", t1250_til2, til2_t1250, 2); +CONVERT conv_kam_latin2("kam_latin2", tkam_til2, til2_tkam, 3); +CONVERT conv_mac_latin2("mac_latin2", tmac_til2, til2_tmac, 4); +CONVERT conv_macce_latin2("macce_latin2", tmacce_til2, til2_tmacce, 5); +CONVERT conv_pc2_latin2("pc2_latin2", tpc2_til2, til2_tpc2, 6); +CONVERT conv_vga_latin2("vga_latin2", tvga_til2, til2_tvga, 7); +CONVERT conv_koi8_cp1251("koi8_cp1251", koi8_cp1251, cp1251_koi8, 8); CONVERT conv_win1251ukr_koi8_ukr("win1251ukr_koi8_ukr", win1251ukr_koi8_ukr, - koi8_ukr_win1251ukr); + koi8_ukr_win1251ukr, 9); CONVERT conv_koi8_ukr_win1251ukr("koi8_ukr_win1251ukr", koi8_ukr_win1251ukr, - win1251ukr_koi8_ukr); + win1251ukr_koi8_ukr, 10); #endif /* DEFINE_ALL_CHARACTER_SETS */ CONVERT *convert_tables[]= { diff --git a/sql/custom_conf.h b/sql/custom_conf.h index af6012e28ec..19ced12bfbb 100644 --- a/sql/custom_conf.h +++ b/sql/custom_conf.h @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ diff --git a/sql/derror.cc b/sql/derror.cc index 62971fde394..7ebe6e4b3c5 100644 --- a/sql/derror.cc +++ b/sql/derror.cc @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -44,13 +44,14 @@ static void read_texts(const char *file_name,const char ***point, uint error_messages) { register uint i; - uint ant,funktpos,length,textant; + uint count,funktpos,length,textcount; File file; char name[FN_REFLEN]; const char *buff; uchar head[32],*pos; DBUG_ENTER("read_texts"); + *point=0; // If something goes wrong LINT_INIT(buff); funktpos=0; if ((file=my_open(fn_format(name,file_name,language,"",4), @@ -63,37 +64,38 @@ static void read_texts(const char *file_name,const char ***point, if (head[0] != (uchar) 254 || head[1] != (uchar) 254 || head[2] != 2 || head[3] != 1) goto err; /* purecov: inspected */ - textant=head[4]; - length=uint2korr(head+6); ant=uint2korr(head+8); + textcount=head[4]; + length=uint2korr(head+6); count=uint2korr(head+8); - if (ant < error_messages) + if (count < error_messages) { - fprintf(stderr,"\n%s: Fatal error: Error message file '%s' had only %d error messages, but it should have at least %d error messages.\n\ -Check that the above file is the right version for this program!\n\n", - my_progname,name,ant,error_messages); + sql_print_error("\ +Error message file '%s' had only %d error messages,\n\ +but it should contain at least %d error messages.\n\ +Check that the above file is the right version for this program!", + name,count,error_messages); VOID(my_close(file,MYF(MY_WME))); - clean_up(0); /* Clean_up frees everything */ - exit(1); /* We can't continue */ + unireg_abort(1); } x_free((gptr) *point); /* Free old language */ if (!(*point= (const char**) - my_malloc((uint) (length+ant*sizeof(char*)),MYF(0)))) + my_malloc((uint) (length+count*sizeof(char*)),MYF(0)))) { funktpos=2; /* purecov: inspected */ goto err; /* purecov: inspected */ } - buff= (char*) (*point + ant); + buff= (char*) (*point + count); - if (my_read(file,(byte*) buff,(uint) ant*2,MYF(MY_NABP))) goto err; - for (i=0, pos= (uchar*) buff ; i< ant ; i++) + if (my_read(file,(byte*) buff,(uint) count*2,MYF(MY_NABP))) goto err; + for (i=0, pos= (uchar*) buff ; i< count ; i++) { (*point)[i]=buff+uint2korr(pos); pos+=2; } if (my_read(file,(byte*) buff,(uint) length,MYF(MY_NABP))) goto err; - for (i=1 ; i < textant ; i++) + for (i=1 ; i < textcount ; i++) { point[i]= *point +uint2korr(head+10+i+i); } @@ -103,20 +105,19 @@ Check that the above file is the right version for this program!\n\n", err: switch (funktpos) { case 2: - buff="\n%s: Fatal error: Not enough memory for messagefile '%s'\n\n"; + buff="Not enough memory for messagefile '%s'"; break; case 1: - buff="\n%s: Fatal error: Can't read from messagefile '%s'\n\n"; + buff="Can't read from messagefile '%s'"; break; default: - buff="\n%s: Fatal error: Can't find messagefile '%s'\n\n"; + buff="Can't find messagefile '%s'"; break; } if (file != FERR) VOID(my_close(file,MYF(MY_WME))); - fprintf(stderr,buff,my_progname,name); - clean_up(0); /* Clean_up frees everything */ - exit(1); /* We can't continue */ + sql_print_error(buff,name); + unireg_abort(1); } /* read_texts */ diff --git a/sql/des_key_file.cc b/sql/des_key_file.cc new file mode 100644 index 00000000000..891cf18ee53 --- /dev/null +++ b/sql/des_key_file.cc @@ -0,0 +1,118 @@ +/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <mysql_priv.h> +#include <m_ctype.h> + +#ifdef HAVE_OPENSSL + +struct st_des_keyschedule des_keyschedule[10]; +uint des_default_key; +pthread_mutex_t LOCK_des_key_file; +static int initialized; + +/* + Function which loads DES keys from plaintext file into memory on MySQL + server startup and on command FLUSH DES_KEY_FILE. + Blame tonu@spam.ee on bugs ;) + + RETURN + 0 ok + 1 Error +*/ + +bool +load_des_key_file(const char *file_name) +{ + bool result=1; + File file; + IO_CACHE io; + DBUG_ENTER("load_des_key_file"); + DBUG_PRINT("enter",("name: %s",file_name)); + + if (!initialized) + { + initialized=1; + pthread_mutex_init(&LOCK_des_key_file,MY_MUTEX_INIT_FAST); + } + + VOID(pthread_mutex_lock(&LOCK_des_key_file)); + if ((file=my_open(file_name,O_RDONLY | O_BINARY ,MYF(MY_WME))) < 0 || + init_io_cache(&io, file, IO_SIZE*2, READ_CACHE, 0, 0, MYF(MY_WME))) + goto error; + + bzero((char*) des_keyschedule,sizeof(struct st_des_keyschedule) * 10); + des_default_key=15; // Impossible key + for (;;) + { + char *start, *end; + char buf[1024], offset; + st_des_keyblock keyblock; + uint length; + + if (!(length=my_b_gets(&io,buf,sizeof(buf)-1))) + break; // End of file + offset=buf[0]; + if (offset >= '0' && offset <= '9') // If ok key + { + offset=(char) (offset - '0'); + // Remove newline and possible other control characters + for (start=buf+1 ; isspace(*start) ; start++) ; + end=buf+length; + for (end=strend(buf) ; end > start && !isgraph(end[-1]) ; end--) ; + + if (start != end) + { + des_cblock ivec; + bzero((char*) &ivec,sizeof(ivec)); + // We make good 24-byte (168 bit) key from given plaintext key with MD5 + EVP_BytesToKey(EVP_des_ede3_cbc(),EVP_md5(),NULL, + (uchar *) start, (int) (end-start),1, + (uchar *) &keyblock, + ivec); + des_set_key_unchecked(&keyblock.key1,des_keyschedule[(int)offset].ks1); + des_set_key_unchecked(&keyblock.key2,des_keyschedule[(int)offset].ks2); + des_set_key_unchecked(&keyblock.key3,des_keyschedule[(int)offset].ks3); + if (des_default_key == 15) + des_default_key= (uint) offset; // use first as def. + } + } + else if (offset != '#') + sql_print_error("load_des_file: Found wrong key_number: %c",offset); + } + result=0; + +error: + if (file >= 0) + { + my_close(file,MYF(0)); + end_io_cache(&io); + } + VOID(pthread_mutex_unlock(&LOCK_des_key_file)); + DBUG_RETURN(result); +} + + +void free_des_key_file() +{ + if (initialized) + { + initialized= 01; + pthread_mutex_destroy(&LOCK_des_key_file); + } +} + +#endif /* HAVE_OPENSSL */ diff --git a/sql/field.cc b/sql/field.cc index 246427cc2ac..aae4fac2a38 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -41,8 +41,13 @@ #include <floatingpoint.h> #endif +// Maximum allowed exponent value for converting string to decimal +#define MAX_EXPONENT 1024 + + + /***************************************************************************** -** Instansiate templates and static variables + Instansiate templates and static variables *****************************************************************************/ #ifdef __GNUC__ @@ -50,69 +55,13 @@ template class List<create_field>; template class List_iterator<create_field>; #endif -struct st_decstr { - uint nr_length,nr_dec,sign,extra; - char sign_char; -}; - uchar Field_null::null[1]={1}; const char field_separator=','; /***************************************************************************** -** Static help functions + Static help functions *****************************************************************************/ - /* - Calculate length of number and it's parts - Increment cuted_fields if wrong number - */ - -static bool -number_dec(struct st_decstr *sdec, const char *str, const char *end) -{ - sdec->sign=sdec->extra=0; - if (str == end) - { - current_thd->cuted_fields++; - sdec->nr_length=sdec->nr_dec=sdec->sign=0; - sdec->extra=1; // We must put one 0 before . - return 1; - } - - if (*str == '-' || *str == '+') /* sign */ - { - sdec->sign_char= *str; - sdec->sign=1; - str++; - } - const char *start=str; - while (str != end && isdigit(*str)) - str++; - if (!(sdec->nr_length=(uint) (str-start))) - sdec->extra=1; // We must put one 0 before . - start=str; - if (str != end && *str == '.') - { - str++; - start=str; - while (str != end && isdigit(*str)) - str++; - } - sdec->nr_dec=(uint) (str-start); - if (current_thd->count_cuted_fields) - { - while (str != end && isspace(*str)) - str++; /* purecov: inspected */ - if (str != end) - { - current_thd->cuted_fields++; - return 1; - } - } - return 0; -} - - void Field_num::prepend_zeros(String *value) { int diff; @@ -127,8 +76,8 @@ void Field_num::prepend_zeros(String *value) } /* -** Test if given number is a int (or a fixed format float with .000) -** This is only used to give warnings in ALTER TABLE or LOAD DATA... + Test if given number is a int (or a fixed format float with .000) + This is only used to give warnings in ALTER TABLE or LOAD DATA... */ bool test_if_int(const char *str,int length) @@ -141,7 +90,7 @@ bool test_if_int(const char *str,int length) str++; if (str == end) return 0; // Error: Empty string - for ( ; str != end ; str++) + for (; str != end ; str++) { if (!isdigit(*str)) { @@ -204,7 +153,7 @@ static bool test_if_real(const char *str,int length) length--; str++; } } - for ( ; length ; length--, str++) + for (; length ; length--, str++) { // Allow end space if (!isspace(*str)) return 0; @@ -215,18 +164,19 @@ static bool test_if_real(const char *str,int length) /**************************************************************************** ** Functions for the base classes -** This is a unpacked number. +** This is an unpacked number. ****************************************************************************/ Field::Field(char *ptr_arg,uint32 length_arg,uchar *null_ptr_arg, - uint null_bit_arg, + uchar null_bit_arg, utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg) - :ptr(ptr_arg),null_ptr(null_ptr_arg),null_bit(null_bit_arg), - table(table_arg),query_id(0),key_start(0),part_of_key(0),part_of_sortkey(0), - table_name(table_arg ? table_arg->table_name : 0), - field_name(field_name_arg), unireg_check(unireg_check_arg), - field_length(length_arg) + :ptr(ptr_arg),null_ptr(null_ptr_arg), + table(table_arg),table_name(table_arg ? table_arg->table_name : 0), + field_name(field_name_arg), + query_id(0),key_start(0),part_of_key(0),part_of_sortkey(0), + unireg_check(unireg_check_arg), + field_length(length_arg),null_bit(null_bit_arg) { flags=null_ptr ? 0: NOT_NULL_FLAG; } @@ -242,13 +192,13 @@ void Field::copy_from_tmp(int row_offset) memcpy(ptr,ptr+row_offset,pack_length()); if (null_ptr) { - *null_ptr= ((null_ptr[0] & (uchar) ~(uint) null_bit) | - null_ptr[row_offset] & (uchar) null_bit); + *null_ptr= (uchar) ((null_ptr[0] & (uchar) ~(uint) null_bit) | + null_ptr[row_offset] & (uchar) null_bit); } } -bool Field::send(String *packet) +bool Field::send(THD *thd, String *packet) { if (is_null()) return net_store_null(packet); @@ -256,7 +206,7 @@ bool Field::send(String *packet) String tmp(buff,sizeof(buff)); val_str(&tmp,&tmp); CONVERT *convert; - if ((convert=current_thd->convert_set)) + if ((convert=thd->variables.convert_set)) return convert->store(packet,tmp.ptr(),tmp.length()); return net_store_data(packet,tmp.ptr(),tmp.length()); } @@ -361,14 +311,14 @@ void Field::store_time(TIME *ltime,timestamp_type type) } -bool Field::optimize_range() +bool Field::optimize_range(uint idx) { - return test(table->file->option_flag() & HA_READ_NEXT); + return test(table->file->index_flags(idx) & HA_READ_NEXT); } /**************************************************************************** -** Functions for the Field_decimal class -** This is a unpacked number. + Functions for the Field_decimal class + This is an number stored as a pre-space (or pre-zero) string ****************************************************************************/ void @@ -381,6 +331,8 @@ void Field_decimal::overflow(bool negative) { uint len=field_length; char *to=ptr, filler= '9'; + + current_thd->cuted_fields++; if (negative) { if (!unsigned_flag) @@ -416,100 +368,348 @@ void Field_decimal::overflow(bool negative) void Field_decimal::store(const char *from,uint len) { - reg3 int i; - uint tmp_dec; - char fyllchar; - const char *end=from+len; - struct st_decstr decstr; - bool error; + const char *end= from+len; + /* The pointer where the field value starts (i.e., "where to write") */ + char *to=ptr; + uint tmp_dec, tmp_uint; + /* + The sign of the number : will be 0 (means positive but sign not + specified), '+' or '-' + */ + char sign_char=0; + /* The pointers where prezeros start and stop */ + const char *pre_zeros_from, *pre_zeros_end; + /* The pointers where digits at the left of '.' start and stop */ + const char *int_digits_from, *int_digits_end; + /* The pointers where digits at the right of '.' start and stop */ + const char *frac_digits_from, *frac_digits_end; + /* The sign of the exponent : will be 0 (means no exponent), '+' or '-' */ + char expo_sign_char=0; + uint exponent=0; // value of the exponent + /* + Pointers used when digits move from the left of the '.' to the + right of the '.' (explained below) + */ + const char *int_digits_tail_from; + /* Number of 0 that need to be added at the left of the '.' (1E3: 3 zeros) */ + uint int_digits_added_zeros; + /* + Pointer used when digits move from the right of the '.' to the left + of the '.' + */ + const char *frac_digits_head_end; + /* Number of 0 that need to be added at the right of the '.' (for 1E-3) */ + uint frac_digits_added_zeros; + char *pos,*tmp_left_pos,*tmp_right_pos; + /* Pointers that are used as limits (begin and end of the field buffer) */ + char *left_wall,*right_wall; + char tmp_char; + /* + To remember if current_thd->cuted_fields has already been incremented, + to do that only once + */ + bool is_cuted_fields_incr=0; + + LINT_INIT(int_digits_tail_from); + LINT_INIT(int_digits_added_zeros); + LINT_INIT(frac_digits_head_end); + LINT_INIT(frac_digits_added_zeros); - if ((tmp_dec= dec)) - tmp_dec++; // Calculate pos of '.' - while (from != end && isspace(*from)) - from++; - if (zerofill) + /* + There are three steps in this function : + - parse the input string + - modify the position of digits around the decimal dot '.' + according to the exponent value (if specified) + - write the formatted number + */ + + if ((tmp_dec=dec)) + tmp_dec++; + + for (; from !=end && isspace(*from); from++) ; // Read spaces + if (from == end) { - fyllchar = '0'; - if (from != end) - while (*from == '0' && from != end-1) // Skipp prezero - from++; + current_thd->cuted_fields++; + is_cuted_fields_incr=1; + } + else if (*from == '+' || *from == '-') // Found some sign ? + { + sign_char= *from++; + /* + We allow "+" for unsigned decimal unless defined different + Both options allowed as one may wish not to have "+" for unsigned numbers + because of data processing issues + */ + if (unsigned_flag) + { + if (sign_char=='-') + { + Field_decimal::overflow(1); + return; + } + /* + Defining this will not store "+" for unsigned decimal type even if + it is passed in numeric string. This will make some tests to fail + */ +#ifdef DONT_ALLOW_UNSIGNED_PLUS + else + sign_char=0; +#endif + } } - else - fyllchar=' '; - error=number_dec(&decstr,from,end); - if (decstr.sign) - { + + pre_zeros_from= from; + for (; from!=end && *from == '0'; from++) ; // Read prezeros + pre_zeros_end=int_digits_from=from; + /* Read non zero digits at the left of '.'*/ + for (; from!=end && isdigit(*from);from++) ; + int_digits_end=from; + if (from!=end && *from == '.') // Some '.' ? + from++; + frac_digits_from= from; + /* Read digits at the right of '.' */ + for (;from!=end && isdigit(*from); from++) ; + frac_digits_end=from; + // Some exponentiation symbol ? + if (from != end && (*from == 'e' || *from == 'E')) + { from++; - if (unsigned_flag) // No sign with zerofill + if (from != end && (*from == '+' || *from == '-')) // Some exponent sign ? + expo_sign_char= *from++; + else + expo_sign_char= '+'; + /* + Read digits of the exponent and compute its value. We must care about + 'exponent' overflow, because as unsigned arithmetic is "modulo", big + exponents will become small (e.g. 1e4294967296 will become 1e0, and the + field will finally contain 1 instead of its max possible value). + */ + for (;from!=end && isdigit(*from); from++) { - if (decstr.sign_char == '+') // just remove "+" - decstr.sign= 0; - else - { - if (!error) - current_thd->cuted_fields++; - Field_decimal::overflow(1); - return; - } + exponent=10*exponent+(*from-'0'); + if (exponent>MAX_EXPONENT) + break; } } + /* - ** Remove pre-zeros if too big number + We only have to generate warnings if count_cuted_fields is set. + This is to avoid extra checks of the number when they are not needed. + Even if this flag is not set, it's ok to increment warnings, if + it makes the code easer to read. */ - for (i= (int) (decstr.nr_length+decstr.extra -(field_length-tmp_dec)+ - decstr.sign) ; - i > 0 ; - i--) + + if (current_thd->count_cuted_fields) { - if (*from == '0') + for (;from != end && isspace(*from); from++) ; // Read end spaces + if (from != end) // If still something left, warn { - from++; - decstr.nr_length--; - continue; + current_thd->cuted_fields++; + is_cuted_fields_incr=1; } - if (decstr.sign && decstr.sign_char == '+' && i == 1) - { // Remove pre '+' - decstr.sign=0; - break; + } + + /* + Now "move" digits around the decimal dot according to the exponent value, + and add necessary zeros. + Examples : + - 1E+3 : needs 3 more zeros at the left of '.' (int_digits_added_zeros=3) + - 1E-3 : '1' moves at the right of '.', and 2 more zeros are needed + between '.' and '1' + - 1234.5E-3 : '234' moves at the right of '.' + These moves are implemented with pointers which point at the begin + and end of each moved segment. Examples : + - 1234.5E-3 : before the code below is executed, the int_digits part is + from '1' to '4' and the frac_digits part from '5' to '5'. After the code + below, the int_digits part is from '1' to '1', the frac_digits_head + part is from '2' to '4', and the frac_digits part from '5' to '5'. + - 1234.5E3 : before the code below is executed, the int_digits part is + from '1' to '4' and the frac_digits part from '5' to '5'. After the code + below, the int_digits part is from '1' to '4', the int_digits_tail + part is from '5' to '5', the frac_digits part is empty, and + int_digits_added_zeros=2 (to make 1234500). + */ + + /* + Below tmp_uint cannot overflow with small enough MAX_EXPONENT setting, + as int_digits_added_zeros<=exponent<4G and + (int_digits_end-int_digits_from)<=max_allowed_packet<=2G and + (frac_digits_from-int_digits_tail_from)<=max_allowed_packet<=2G + */ + + if (!expo_sign_char) + tmp_uint=tmp_dec+(uint)(int_digits_end-int_digits_from); + else if (expo_sign_char == '-') + { + tmp_uint=min(exponent,(uint)(int_digits_end-int_digits_from)); + frac_digits_added_zeros=exponent-tmp_uint; + int_digits_end -= tmp_uint; + frac_digits_head_end=int_digits_end+tmp_uint; + tmp_uint=tmp_dec+(uint)(int_digits_end-int_digits_from); + } + else // (expo_sign_char=='+') + { + tmp_uint=min(exponent,(uint)(frac_digits_end-frac_digits_from)); + int_digits_added_zeros=exponent-tmp_uint; + int_digits_tail_from=frac_digits_from; + frac_digits_from=frac_digits_from+tmp_uint; + /* + We "eat" the heading zeros of the + int_digits.int_digits_tail.int_digits_added_zeros concatenation + (for example 0.003e3 must become 3 and not 0003) + */ + if (int_digits_from == int_digits_end) + { + /* + There was nothing in the int_digits part, so continue + eating int_digits_tail zeros + */ + for (; int_digits_tail_from != frac_digits_from && + *int_digits_tail_from == '0'; int_digits_tail_from++) ; + if (int_digits_tail_from == frac_digits_from) + { + // there were only zeros in int_digits_tail too + int_digits_added_zeros=0; + } } - current_thd->cuted_fields++; + tmp_uint= (tmp_dec+(int_digits_end-int_digits_from)+ + (uint)(frac_digits_from-int_digits_tail_from)+ + int_digits_added_zeros); + } + + /* + Now write the formated number + + First the digits of the int_% parts. + Do we have enough room to write these digits ? + If the sign is defined and '-', we need one position for it + */ + + if (field_length < tmp_uint + (int) (sign_char == '-')) + { // too big number, change to max or min number - Field_decimal::overflow(decstr.sign && decstr.sign_char == '-'); + Field_decimal::overflow(sign_char == '-'); return; } - char *to=ptr; - for (i=(int) (field_length-tmp_dec-decstr.nr_length-decstr.extra - decstr.sign) ; - i-- > 0 ;) - *to++ = fyllchar; - if (decstr.sign) - *to++= decstr.sign_char; - if (decstr.extra) - *to++ = '0'; - for (i=(int) decstr.nr_length ; i-- > 0 ; ) - *to++ = *from++; - if (tmp_dec--) - { - *to++ ='.'; - if (decstr.nr_dec) from++; // Skipp '.' - for (i=(int) min(decstr.nr_dec,tmp_dec) ; i-- > 0 ; ) *to++ = *from++; - for (i=(int) (tmp_dec-min(decstr.nr_dec,tmp_dec)) ; i-- > 0 ; ) *to++ = '0'; + + /* + Tmp_left_pos is the position where the leftmost digit of + the int_% parts will be written + */ + tmp_left_pos=pos=to+(uint)(field_length-tmp_uint); + + // Write all digits of the int_% parts + while (int_digits_from != int_digits_end) + *pos++ = *int_digits_from++ ; + + if (expo_sign_char == '+') + { + while (int_digits_tail_from != frac_digits_from) + *pos++= *int_digits_tail_from++; + while (int_digits_added_zeros-- >0) + *pos++= '0'; } + /* + Note the position where the rightmost digit of the int_% parts has been + written (this is to later check if the int_% parts contained nothing, + meaning an extra 0 is needed). + */ + tmp_right_pos=pos; /* - ** Check for incorrect string if in batch mode (ALTER TABLE/LOAD DATA...) + Step back to the position of the leftmost digit of the int_% parts, + to write sign and fill with zeros or blanks or prezeros. */ - if (!error && current_thd->count_cuted_fields && from != end) - { // Check if number was cuted - for (; from != end ; from++) + pos=tmp_left_pos-1; + if (zerofill) + { + left_wall=to-1; + while (pos != left_wall) // Fill with zeros + *pos--='0'; + } + else + { + left_wall=to+(sign_char != 0)-1; + if (!expo_sign_char) // If exponent was specified, ignore prezeros { - if (*from != '0') + for (;pos != left_wall && pre_zeros_from !=pre_zeros_end; + pre_zeros_from++) + *pos--= '0'; + } + if (pos == tmp_right_pos-1) + *pos--= '0'; // no 0 has ever been written, so write one + left_wall= to-1; + if (sign_char && pos != left_wall) + { + /* Write sign if possible (it is if sign is '-') */ + *pos--= sign_char; + } + while (pos != left_wall) + *pos--=' '; //fill with blanks + } + + /* + Write digits of the frac_% parts ; + Depending on current_thd->count_cutted_fields, we may also want + to know if some non-zero tail of these parts will + be truncated (for example, 0.002->0.00 will generate a warning, + while 0.000->0.00 will not) + (and 0E1000000000 will not, while 1E-1000000000 will) + */ + + pos=to+(uint)(field_length-tmp_dec); // Calculate post to '.' + right_wall=to+field_length; + if (pos != right_wall) + *pos++='.'; + + if (expo_sign_char == '-') + { + while (frac_digits_added_zeros-- > 0) + { + if (pos == right_wall) + { + if (current_thd->count_cuted_fields && !is_cuted_fields_incr) + break; // Go on below to see if we lose non zero digits + return; + } + *pos++='0'; + } + while (int_digits_end != frac_digits_head_end) + { + tmp_char= *int_digits_end++; + if (pos == right_wall) + { + if (tmp_char != '0') // Losing a non zero digit ? + { + if (!is_cuted_fields_incr) + current_thd->cuted_fields++; + return; + } + continue; + } + *pos++= tmp_char; + } + } + + for (;frac_digits_from!=frac_digits_end;) + { + tmp_char= *frac_digits_from++; + if (pos == right_wall) + { + if (tmp_char != '0') // Losing a non zero digit ? { - if (!isspace(*from)) // Space is ok + if (!is_cuted_fields_incr) current_thd->cuted_fields++; - break; + return; } + continue; } + *pos++= tmp_char; } + + while (pos != right_wall) + *pos++='0'; // Fill with zeros at right of '.' + } @@ -518,9 +718,17 @@ void Field_decimal::store(double nr) if (unsigned_flag && nr < 0) { overflow(1); - current_thd->cuted_fields++; return; } + +#ifdef HAVE_FINITE + if (!finite(nr)) // Handle infinity as special case + { + overflow(nr < 0.0); + return; + } +#endif + reg4 uint i,length; char fyllchar,*to; char buff[320]; @@ -535,10 +743,7 @@ void Field_decimal::store(double nr) length=(uint) strlen(buff); if (length > field_length) - { overflow(nr < 0.0); - current_thd->cuted_fields++; - } else { to=ptr; @@ -554,7 +759,6 @@ void Field_decimal::store(longlong nr) if (unsigned_flag && nr < 0) { overflow(1); - current_thd->cuted_fields++; return; } char buff[22]; @@ -562,10 +766,7 @@ void Field_decimal::store(longlong nr) uint int_part=field_length- (dec ? dec+1 : 0); if (length > int_part) - { overflow(test(nr < 0L)); /* purecov: inspected */ - current_thd->cuted_fields++; /* purecov: inspected */ - } else { char fyllchar = zerofill ? (char) '0' : (char) ' '; @@ -1075,7 +1276,7 @@ void Field_short::sort_string(char *to,uint length __attribute__((unused))) if (unsigned_flag) to[0] = ptr[0]; else - to[0] = ptr[0] ^ 128; /* Revers signbit */ + to[0] = (char) (ptr[0] ^ 128); /* Revers signbit */ to[1] = ptr[1]; } else @@ -1084,7 +1285,7 @@ void Field_short::sort_string(char *to,uint length __attribute__((unused))) if (unsigned_flag) to[0] = ptr[1]; else - to[0] = ptr[1] ^ 128; /* Revers signbit */ + to[0] = (char) (ptr[1] ^ 128); /* Revers signbit */ to[1] = ptr[0]; } } @@ -1155,12 +1356,12 @@ void Field_medium::store(double nr) } else if (nr >= (double) (long) (1L << 24)) { - ulong tmp=(ulong) (1L << 24)-1L; + uint32 tmp=(uint32) (1L << 24)-1L; int3store(ptr,tmp); current_thd->cuted_fields++; } else - int3store(ptr,(ulong) nr); + int3store(ptr,(uint32) nr); } else { @@ -1197,7 +1398,7 @@ void Field_medium::store(longlong nr) current_thd->cuted_fields++; } else - int3store(ptr,(ulong) nr); + int3store(ptr,(uint32) nr); } else { @@ -1290,12 +1491,14 @@ void Field_medium::sql_type(String &res) const void Field_long::store(const char *from,uint len) { + char *end; while (len && isspace(*from)) { len--; from++; } long tmp; String tmp_str(from,len); + from= tmp_str.c_ptr(); // Add end null if needed errno=0; if (unsigned_flag) { @@ -1305,11 +1508,13 @@ void Field_long::store(const char *from,uint len) errno=ERANGE; } else - tmp=(long) strtoul(tmp_str.c_ptr(),NULL,10); + tmp=(long) strtoul(from, &end, 10); } else - tmp=strtol(tmp_str.c_ptr(),NULL,10); - if (errno || current_thd->count_cuted_fields && !test_if_int(from,len)) + tmp=strtol(from, &end, 10); + if (errno || + (from+len != end && current_thd->count_cuted_fields && + !test_if_int(from,len))) current_thd->cuted_fields++; #ifdef WORDS_BIGENDIAN if (table->db_low_byte_first) @@ -1475,7 +1680,7 @@ int Field_long::cmp(const char *a_ptr, const char *b_ptr) longget(b,b_ptr); } if (unsigned_flag) - return ((ulong) a < (ulong) b) ? -1 : ((ulong) a > (ulong) b) ? 1 : 0; + return ((uint32) a < (uint32) b) ? -1 : ((uint32) a > (uint32) b) ? 1 : 0; return (a < b) ? -1 : (a > b) ? 1 : 0; } @@ -1487,7 +1692,7 @@ void Field_long::sort_string(char *to,uint length __attribute__((unused))) if (unsigned_flag) to[0] = ptr[0]; else - to[0] = ptr[0] ^ 128; /* Revers signbit */ + to[0] = (char) (ptr[0] ^ 128); /* Revers signbit */ to[1] = ptr[1]; to[2] = ptr[2]; to[3] = ptr[3]; @@ -1498,7 +1703,7 @@ void Field_long::sort_string(char *to,uint length __attribute__((unused))) if (unsigned_flag) to[0] = ptr[3]; else - to[0] = ptr[3] ^ 128; /* Revers signbit */ + to[0] = (char) (ptr[3] ^ 128); /* Revers signbit */ to[1] = ptr[2]; to[2] = ptr[1]; to[3] = ptr[0]; @@ -1518,12 +1723,14 @@ void Field_long::sql_type(String &res) const void Field_longlong::store(const char *from,uint len) { + char *end; while (len && isspace(*from)) { // For easy error check len--; from++; } longlong tmp; String tmp_str(from,len); + from= tmp_str.c_ptr(); // Add end null if needed errno=0; if (unsigned_flag) { @@ -1533,12 +1740,14 @@ void Field_longlong::store(const char *from,uint len) errno=ERANGE; } else - tmp=(longlong) strtoull(tmp_str.c_ptr(),NULL,10); + tmp=(longlong) strtoull(from, &end, 10); } else - tmp=strtoll(tmp_str.c_ptr(),NULL,10); - if (errno || current_thd->count_cuted_fields && !test_if_int(from,len)) - current_thd->cuted_fields++; + tmp=strtoll(from, &end, 10); + if (errno || + (from+len != end && current_thd->count_cuted_fields && + !test_if_int(from,len))) + current_thd->cuted_fields++; #ifdef WORDS_BIGENDIAN if (table->db_low_byte_first) { @@ -1686,7 +1895,7 @@ void Field_longlong::sort_string(char *to,uint length __attribute__((unused))) if (unsigned_flag) to[0] = ptr[0]; else - to[0] = ptr[0] ^ 128; /* Revers signbit */ + to[0] = (char) (ptr[0] ^ 128); /* Revers signbit */ to[1] = ptr[1]; to[2] = ptr[2]; to[3] = ptr[3]; @@ -1701,7 +1910,7 @@ void Field_longlong::sort_string(char *to,uint length __attribute__((unused))) if (unsigned_flag) to[0] = ptr[7]; else - to[0] = ptr[7] ^ 128; /* Revers signbit */ + to[0] = (char) (ptr[7] ^ 128); /* Revers signbit */ to[1] = ptr[6]; to[2] = ptr[5]; to[3] = ptr[4]; @@ -1738,6 +1947,11 @@ void Field_float::store(double nr) float j; if (dec < NOT_FIXED_DEC) nr=floor(nr*log_10[dec]+0.5)/log_10[dec]; // To fixed point + if (unsigned_flag && nr < 0) + { + current_thd->cuted_fields++; + nr=0; + } if (nr < -FLT_MAX) { j= -FLT_MAX; @@ -1764,6 +1978,11 @@ void Field_float::store(double nr) void Field_float::store(longlong nr) { float j= (float) nr; + if (unsigned_flag && j < 0) + { + current_thd->cuted_fields++; + j=0; + } #ifdef WORDS_BIGENDIAN if (table->db_low_byte_first) { @@ -1936,7 +2155,7 @@ void Field_float::sort_string(char *to,uint length __attribute__((unused))) { /* make complement */ uint i; for (i=0 ; i < sizeof(nr); i++) - tmp[i]=tmp[i] ^ (uchar) 255; + tmp[i]= (uchar) (tmp[i] ^ (uchar) 255); } else { @@ -1970,6 +2189,11 @@ void Field_double::store(const char *from,uint len) double j= atof(tmp_str.c_ptr()); if (errno || current_thd->count_cuted_fields && !test_if_real(from,len)) current_thd->cuted_fields++; + if (unsigned_flag && j < 0) + { + current_thd->cuted_fields++; + j=0; + } #ifdef WORDS_BIGENDIAN if (table->db_low_byte_first) { @@ -1985,6 +2209,11 @@ void Field_double::store(double nr) { if (dec < NOT_FIXED_DEC) nr=floor(nr*log_10[dec]+0.5)/log_10[dec]; // To fixed point + if (unsigned_flag && nr < 0) + { + current_thd->cuted_fields++; + nr=0; + } #ifdef WORDS_BIGENDIAN if (table->db_low_byte_first) { @@ -1999,6 +2228,11 @@ void Field_double::store(double nr) void Field_double::store(longlong nr) { double j= (double) nr; + if (unsigned_flag && j < 0) + { + current_thd->cuted_fields++; + j=0; + } #ifdef WORDS_BIGENDIAN if (table->db_low_byte_first) { @@ -2304,10 +2538,10 @@ void Field_timestamp::store(longlong nr) { part1=(long) (nr/LL(1000000)); part2=(long) (nr - (longlong) part1*LL(1000000)); - l_time.year= part1/10000L; part1%=10000L; + l_time.year= (int) (part1/10000L); part1%=10000L; l_time.month= (int) part1 / 100; - l_time.day= (int) part1 % 100; - l_time.hour= part2/10000L; part2%=10000L; + l_time.day= (int) part1 % 100; + l_time.hour= (int) (part2/10000L); part2%=10000L; l_time.minute=(int) part2 / 100; l_time.second=(int) part2 % 100; timestamp=my_gmt_sec(&l_time); @@ -2321,7 +2555,7 @@ void Field_timestamp::store(longlong nr) } else #endif - longstore(ptr,(ulong)timestamp); + longstore(ptr,(uint32) timestamp); } @@ -2622,7 +2856,7 @@ void Field_time::store(longlong nr) double Field_time::val_real(void) { - ulong j= (ulong) uint3korr(ptr); + uint32 j= (uint32) uint3korr(ptr); return (double) j; } @@ -2658,19 +2892,19 @@ bool Field_time::get_time(TIME *ltime) ltime->neg= 1; tmp=-tmp; } - ltime->hour=tmp/10000; + ltime->hour= (int) (tmp/10000); tmp-=ltime->hour*10000; - ltime->minute= tmp/100; - ltime->second= tmp % 100; + ltime->minute= (int) tmp/100; + ltime->second= (int) tmp % 100; ltime->second_part=0; return 0; } int Field_time::cmp(const char *a_ptr, const char *b_ptr) { - long a,b; - a=(long) sint3korr(a_ptr); - b=(long) sint3korr(b_ptr); + int32 a,b; + a=(int32) sint3korr(a_ptr); + b=(int32) sint3korr(b_ptr); return (a < b) ? -1 : (a > b) ? 1 : 0; } @@ -2781,14 +3015,14 @@ void Field_year::sql_type(String &res) const ** Stored as a 4 byte unsigned int ****************************************************************************/ -void Field_date::store(const char *from,uint len) +void Field_date::store(const char *from, uint len) { TIME l_time; - ulong tmp; + uint32 tmp; if (str_to_TIME(from,len,&l_time,1) == TIMESTAMP_NONE) tmp=0; else - tmp=(ulong) l_time.year*10000L + (ulong) (l_time.month*100+l_time.day); + tmp=(uint32) l_time.year*10000L + (uint32) (l_time.month*100+l_time.day); #ifdef WORDS_BIGENDIAN if (table->db_low_byte_first) { @@ -2960,7 +3194,7 @@ void Field_newdate::store(double nr) void Field_newdate::store(longlong nr) { - long tmp; + int32 tmp; if (nr >= LL(100000000) && nr <= LL(99991231235959)) nr=nr/LL(1000000); // Timestamp to date if (nr < 0L || nr > 99991231L) @@ -2970,16 +3204,16 @@ void Field_newdate::store(longlong nr) } else { - tmp=(long) nr; + tmp=(int32) nr; if (tmp) { if (tmp < YY_PART_YEAR*10000L) // Fix short dates - tmp+=20000000L; + tmp+= (uint32) 20000000L; else if (tmp < 999999L) - tmp+=19000000L; + tmp+= (uint32) 19000000L; } - uint month=((tmp/100) % 100); - uint day= tmp%100; + uint month= (uint) ((tmp/100) % 100); + uint day= (uint) (tmp%100); if (month > 12 || day > 31) { tmp=0L; // Don't allow date to change @@ -2988,7 +3222,7 @@ void Field_newdate::store(longlong nr) else tmp= day + month*32 + (tmp/10000)*16*32; } - int3store(ptr,tmp); + int3store(ptr,(int32) tmp); } void Field_newdate::store_time(TIME *ltime,timestamp_type type) @@ -3013,7 +3247,7 @@ double Field_newdate::val_real(void) longlong Field_newdate::val_int(void) { - ulong j=uint3korr(ptr); + ulong j= uint3korr(ptr); j= (j % 32L)+(j / 32L % 16L)*100L + (j/(16L*32L))*10000L; return (longlong) j; } @@ -3023,25 +3257,25 @@ String *Field_newdate::val_str(String *val_buffer, { val_buffer->alloc(field_length); val_buffer->length(field_length); - ulong tmp=(ulong) uint3korr(ptr); + uint32 tmp=(uint32) uint3korr(ptr); int part; char *pos=(char*) val_buffer->ptr()+10; /* Open coded to get more speed */ - *pos--=0; + *pos--=0; // End NULL part=(int) (tmp & 31); - *pos--='0'+part%10; - *pos--='0'+part/10; - *pos--='-'; + *pos--= (char) ('0'+part%10); + *pos--= (char) ('0'+part/10); + *pos--= '-'; part=(int) (tmp >> 5 & 15); - *pos--='0'+part%10; - *pos--='0'+part/10; - *pos--='-'; + *pos--= (char) ('0'+part%10); + *pos--= (char) ('0'+part/10); + *pos--= '-'; part=(int) (tmp >> 9); - *pos--='0'+part%10; part/=10; - *pos--='0'+part%10; part/=10; - *pos--='0'+part%10; part/=10; - *pos='0'+part; + *pos--= (char) ('0'+part%10); part/=10; + *pos--= (char) ('0'+part%10); part/=10; + *pos--= (char) ('0'+part%10); part/=10; + *pos= (char) ('0'+part); return val_buffer; } @@ -3049,7 +3283,7 @@ bool Field_newdate::get_date(TIME *ltime,bool fuzzydate) { if (is_null()) return 1; - ulong tmp=(ulong) uint3korr(ptr); + uint32 tmp=(uint32) uint3korr(ptr); bzero((char*) ltime,sizeof(*ltime)); ltime->day= tmp & 31; ltime->month= (tmp >> 5) & 15; @@ -3065,9 +3299,9 @@ bool Field_newdate::get_time(TIME *ltime) int Field_newdate::cmp(const char *a_ptr, const char *b_ptr) { - ulong a,b; - a=(ulong) uint3korr(a_ptr); - b=(ulong) uint3korr(b_ptr); + uint32 a,b; + a=(uint32) uint3korr(a_ptr); + b=(uint32) uint3korr(b_ptr); return (a < b) ? -1 : (a > b) ? 1 : 0; } @@ -3201,44 +3435,44 @@ String *Field_datetime::val_str(String *val_buffer, pos=(char*) val_buffer->ptr()+19; *pos--=0; - *pos--='0'+(char) (part2%10); part2/=10; - *pos--='0'+(char) (part2%10); part3= (int) (part2 / 10); - *pos--=':'; - *pos--='0'+(char) (part3%10); part3/=10; - *pos--='0'+(char) (part3%10); part3/=10; - *pos--=':'; - *pos--='0'+(char) (part3%10); part3/=10; - *pos--='0'+(char) part3; - *pos--=' '; - *pos--='0'+(char) (part1%10); part1/=10; - *pos--='0'+(char) (part1%10); part1/=10; - *pos--='-'; - *pos--='0'+(char) (part1%10); part1/=10; - *pos--='0'+(char) (part1%10); part3= (int) (part1/10); - *pos--='-'; - *pos--='0'+(char) (part3%10); part3/=10; - *pos--='0'+(char) (part3%10); part3/=10; - *pos--='0'+(char) (part3%10); part3/=10; - *pos='0'+(char) part3; + *pos--= (char) ('0'+(char) (part2%10)); part2/=10; + *pos--= (char) ('0'+(char) (part2%10)); part3= (int) (part2 / 10); + *pos--= ':'; + *pos--= (char) ('0'+(char) (part3%10)); part3/=10; + *pos--= (char) ('0'+(char) (part3%10)); part3/=10; + *pos--= ':'; + *pos--= (char) ('0'+(char) (part3%10)); part3/=10; + *pos--= (char) ('0'+(char) part3); + *pos--= ' '; + *pos--= (char) ('0'+(char) (part1%10)); part1/=10; + *pos--= (char) ('0'+(char) (part1%10)); part1/=10; + *pos--= '-'; + *pos--= (char) ('0'+(char) (part1%10)); part1/=10; + *pos--= (char) ('0'+(char) (part1%10)); part3= (int) (part1/10); + *pos--= '-'; + *pos--= (char) ('0'+(char) (part3%10)); part3/=10; + *pos--= (char) ('0'+(char) (part3%10)); part3/=10; + *pos--= (char) ('0'+(char) (part3%10)); part3/=10; + *pos=(char) ('0'+(char) part3); return val_buffer; } bool Field_datetime::get_date(TIME *ltime,bool fuzzydate) { longlong tmp=Field_datetime::val_int(); - long part1,part2; - part1=(long) (tmp/LL(1000000)); - part2=(long) (tmp - (ulonglong) part1*LL(1000000)); + uint32 part1,part2; + part1=(uint32) (tmp/LL(1000000)); + part2=(uint32) (tmp - (ulonglong) part1*LL(1000000)); ltime->time_type= TIMESTAMP_FULL; - ltime->neg=0; - ltime->second_part=0; - ltime->second= part2%100; - ltime->minute= part2/100%100; - ltime->hour= part2/10000; - ltime->day= part1%100; - ltime->month= part1/100%100; - ltime->year= part1/10000; + ltime->neg= 0; + ltime->second_part= 0; + ltime->second= (int) (part2%100); + ltime->minute= (int) (part2/100%100); + ltime->hour= (int) (part2/10000); + ltime->day= (int) (part1%100); + ltime->month= (int) (part1/100%100); + ltime->year= (int) (part1/10000); return (!fuzzydate && (!ltime->month || !ltime->day)) ? 1 : 0; } @@ -3310,9 +3544,9 @@ void Field_datetime::sql_type(String &res) const void Field_string::store(const char *from,uint length) { #ifdef USE_TIS620 - if(!binary_flag) { + if (!binary_flag) { ThNormalize((uchar *)ptr, field_length, (uchar *)from, length); - if(length < field_length) { + if (length < field_length) { bfill(ptr + length, field_length - length, ' '); } } @@ -3357,7 +3591,7 @@ void Field_string::store(longlong nr) { char buff[22]; char *end=longlong10_to_str(nr,buff,-10); - Field_string::store(buff,end-buff); + Field_string::store(buff,(uint) (end-buff)); } @@ -3492,12 +3726,12 @@ int Field_string::pack_cmp(const char *b, uint length) } -uint Field_string::packed_col_length(const char *ptr, uint length) +uint Field_string::packed_col_length(const char *data_ptr, uint length) { if (length > 255) - return uint2korr(ptr)+2; + return uint2korr(data_ptr)+2; else - return (uint) ((uchar) *ptr)+1; + return (uint) ((uchar) *data_ptr)+1; } uint Field_string::max_packed_col_length(uint max_length) @@ -3514,7 +3748,7 @@ uint Field_string::max_packed_col_length(uint max_length) void Field_varstring::store(const char *from,uint length) { #ifdef USE_TIS620 - if(!binary_flag) + if (!binary_flag) { ThNormalize((uchar *) ptr+2, field_length, (uchar *) from, length); } @@ -3548,7 +3782,7 @@ void Field_varstring::store(longlong nr) { char buff[22]; char *end=longlong10_to_str(nr,buff,-10); - Field_varstring::store(buff,end-buff); + Field_varstring::store(buff,(uint) (end-buff)); } @@ -3639,9 +3873,9 @@ char *Field_varstring::pack(char *to, const char *from, uint max_length) uint length=uint2korr(from); if (length > max_length) length=max_length; - *to++= (length & 255); + *to++= (char) (length & 255); if (max_length > 255) - *to++= (uchar) (length >> 8); + *to++= (char) (length >> 8); if (length) memcpy(to, from+2, length); return to+length; @@ -3711,12 +3945,12 @@ int Field_varstring::pack_cmp(const char *b, uint key_length) return my_sortncmp(a,a_length, b,b_length); } -uint Field_varstring::packed_col_length(const char *ptr, uint length) +uint Field_varstring::packed_col_length(const char *data_ptr, uint length) { if (length > 255) - return uint2korr(ptr)+2; + return uint2korr(data_ptr)+2; else - return (uint) ((uchar) *ptr)+1; + return (uint) ((uchar) *data_ptr)+1; } uint Field_varstring::max_packed_col_length(uint max_length) @@ -3730,7 +3964,7 @@ uint Field_varstring::max_packed_col_length(uint max_length) ** packlength slot and may be from 1-4. ****************************************************************************/ -Field_blob::Field_blob(char *ptr_arg, uchar *null_ptr_arg, uint null_bit_arg, +Field_blob::Field_blob(char *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg,uint blob_pack_length, bool binary_arg) @@ -3747,7 +3981,7 @@ Field_blob::Field_blob(char *ptr_arg, uchar *null_ptr_arg, uint null_bit_arg, } -void Field_blob::store_length(ulong number) +void Field_blob::store_length(uint32 number) { switch (packlength) { case 1: @@ -3774,9 +4008,9 @@ void Field_blob::store_length(ulong number) shortstore(ptr,(unsigned short) number); break; case 3: - if (number > (ulong) (1L << 24)) + if (number > (uint32) (1L << 24)) { - number= (ulong) (1L << 24)-1L; + number= (uint32) (1L << 24)-1L; current_thd->cuted_fields++; } int3store(ptr,number); @@ -3794,11 +4028,11 @@ void Field_blob::store_length(ulong number) } -ulong Field_blob::get_length(const char *pos) +uint32 Field_blob::get_length(const char *pos) { switch (packlength) { case 1: - return (ulong) (uchar) pos[0]; + return (uint32) (uchar) pos[0]; case 2: { uint16 tmp; @@ -3808,10 +4042,10 @@ ulong Field_blob::get_length(const char *pos) else #endif shortget(tmp,pos); - return (ulong) tmp; + return (uint32) tmp; } case 3: - return (ulong) uint3korr(pos); + return (uint32) uint3korr(pos); case 4: { uint32 tmp; @@ -3821,7 +4055,7 @@ ulong Field_blob::get_length(const char *pos) else #endif longget(tmp,pos); - return (ulong) tmp; + return (uint32) tmp; } } return 0; // Impossible @@ -3843,7 +4077,7 @@ void Field_blob::store(const char *from,uint len) if (table->copy_blobs || len <= MAX_FIELD_WIDTH) { // Must make a copy #ifdef USE_TIS620 - if(!binary_flag) + if (!binary_flag) { /* If there isn't enough memory, use original string */ if ((th_ptr=(char * ) my_malloc(sizeof(char) * len,MYF(0)))) @@ -3867,14 +4101,14 @@ void Field_blob::store(const char *from,uint len) void Field_blob::store(double nr) { value.set(nr); - Field_blob::store(value.ptr(),value.length()); + Field_blob::store(value.ptr(),(uint) value.length()); } void Field_blob::store(longlong nr) { value.set(nr); - Field_blob::store(value.ptr(),value.length()); + Field_blob::store(value.ptr(), (uint) value.length()); } @@ -3885,7 +4119,7 @@ double Field_blob::val_real(void) memcpy_fixed(&blob,ptr+packlength,sizeof(char*)); if (!blob) return 0.0; - ulong length=get_length(ptr); + uint32 length=get_length(ptr); char save=blob[length]; // Ok to patch blob in NISAM blob[length]=0; @@ -3901,7 +4135,7 @@ longlong Field_blob::val_int(void) memcpy_fixed(&blob,ptr+packlength,sizeof(char*)); if (!blob) return 0; - ulong length=get_length(ptr); + uint32 length=get_length(ptr); char save=blob[length]; // Ok to patch blob in NISAM blob[length]=0; @@ -3924,8 +4158,8 @@ String *Field_blob::val_str(String *val_buffer __attribute__((unused)), } -int Field_blob::cmp(const char *a,ulong a_length, const char *b, - ulong b_length) +int Field_blob::cmp(const char *a,uint32 a_length, const char *b, + uint32 b_length) { int diff; if (binary_flag) @@ -3959,11 +4193,11 @@ int Field_blob::cmp_binary_offset(uint row_offset) int Field_blob::cmp_binary(const char *a_ptr, const char *b_ptr, - ulong max_length) + uint32 max_length) { char *a,*b; uint diff; - ulong a_length,b_length; + uint32 a_length,b_length; memcpy_fixed(&a,a_ptr+packlength,sizeof(char*)); memcpy_fixed(&b,b_ptr+packlength,sizeof(char*)); a_length=get_length(a_ptr); @@ -3982,13 +4216,15 @@ int Field_blob::cmp_binary(const char *a_ptr, const char *b_ptr, void Field_blob::get_key_image(char *buff,uint length) { length-=HA_KEY_BLOB_LENGTH; - ulong blob_length=get_length(ptr); + uint32 blob_length=get_length(ptr); char *blob; - if ((ulong) length > blob_length) + if ((uint32) length > blob_length) { -#ifdef HAVE_purify + /* + Must clear this as we do a memcmp in opt_range.cc to detect + identical keys + */ bzero(buff+2+blob_length, (length-blob_length)); -#endif length=(uint) blob_length; } int2store(buff,length); @@ -4078,7 +4314,7 @@ char *Field_blob::pack(char *to, const char *from, uint max_length) { char *save=ptr; ptr=(char*) from; - ulong length=get_length(); // Length of from string + uint32 length=get_length(); // Length of from string if (length > max_length) { ptr=to; @@ -4101,7 +4337,7 @@ char *Field_blob::pack(char *to, const char *from, uint max_length) const char *Field_blob::unpack(char *to, const char *from) { memcpy(to,from,packlength); - ulong length=get_length(from); + uint32 length=get_length(from); from+=packlength; if (length) memcpy_fixed(to+packlength, &from, sizeof(from)); @@ -4110,60 +4346,6 @@ const char *Field_blob::unpack(char *to, const char *from) return from+length; } - -#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) - id=uint8korr(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) @@ -4220,7 +4402,7 @@ char *Field_blob::pack_key(char *to, const char *from, uint max_length) { char *save=ptr; ptr=(char*) from; - ulong length=get_length(); // Length of from string + uint32 length=get_length(); // Length of from string if (length > max_length) length=max_length; *to++= (uchar) length; @@ -4243,20 +4425,20 @@ char *Field_blob::pack_key_from_key_image(char *to, const char *from, uint length=uint2korr(from); if (length > max_length) length=max_length; - *to++= (length & 255); + *to++= (char) (length & 255); if (max_length > 255) - *to++= (uchar) (length >> 8); + *to++= (char) (length >> 8); if (length) memcpy(to, from+2, length); return to+length; } -uint Field_blob::packed_col_length(const char *ptr, uint length) +uint Field_blob::packed_col_length(const char *data_ptr, uint length) { if (length > 255) - return uint2korr(ptr)+2; + return uint2korr(data_ptr)+2; else - return (uint) ((uchar) *ptr)+1; + return (uint) ((uchar) *data_ptr)+1; } uint Field_blob::max_packed_col_length(uint max_length) @@ -4358,7 +4540,7 @@ void Field_enum::store(const char *from,uint length) conv=buff; } my_errno=0; - tmp=strtoul(conv,&end,10); + tmp=(uint) strtoul(conv,&end,10); if (my_errno || end != conv+length || tmp > typelib->count) { tmp=0; @@ -4517,7 +4699,7 @@ ulonglong find_set(TYPELIB *lib,const char *x,uint length) for (;;) { const char *pos=start; - for ( ; pos != end && *pos != field_separator ; pos++) ; + for (; pos != end && *pos != field_separator ; pos++) ; uint find=find_enum(lib,start,(uint) (pos-start)); if (!find) error=1; @@ -4628,7 +4810,7 @@ bool Field_enum::eq_def(Field *field) if (!Field::eq_def(field)) return 0; TYPELIB *from_lib=((Field_enum*) field)->typelib; - + if (typelib->count < from_lib->count) return 0; for (uint i=0 ; i < from_lib->count ; i++) @@ -4638,7 +4820,7 @@ bool Field_enum::eq_def(Field *field) } bool Field_num::eq_def(Field *field) -{ +{ if (!Field::eq_def(field)) return 0; Field_num *from_num= (Field_num*) field; @@ -4656,7 +4838,7 @@ bool Field_num::eq_def(Field *field) *****************************************************************************/ /* -** Make a field from the .frm file info + Make a field from the .frm file info */ uint32 calc_pack_length(enum_field_types type,uint32 length) @@ -4685,6 +4867,7 @@ uint32 calc_pack_length(enum_field_types type,uint32 length) case FIELD_TYPE_LONG_BLOB: return 4+portable_sizeof_char_ptr; case FIELD_TYPE_SET: case FIELD_TYPE_ENUM: abort(); return 0; // This shouldn't happen + default: return 0; } return 0; // This shouldn't happen } @@ -4704,8 +4887,9 @@ uint pack_length_to_packflag(uint type) Field *make_field(char *ptr, uint32 field_length, - uchar *null_pos, uint null_bit, + uchar *null_pos, uchar null_bit, uint pack_flag, + enum_field_types field_type, Field::utype unireg_check, TYPELIB *interval, const char *field_name, @@ -4731,6 +4915,9 @@ Field *make_field(char *ptr, uint32 field_length, return new Field_blob(ptr,null_pos,null_bit, unireg_check, field_name, table, pack_length,f_is_binary(pack_flag) != 0); + if (f_is_geom(pack_flag)) + return 0; + if (interval) { if (f_is_enum(pack_flag)) @@ -4744,7 +4931,7 @@ Field *make_field(char *ptr, uint32 field_length, } } - switch ((enum enum_field_types) f_packtype(pack_flag)) { + switch (field_type) { case FIELD_TYPE_DECIMAL: return new Field_decimal(ptr,field_length,null_pos,null_bit, unireg_check, field_name, table, @@ -4807,10 +4994,11 @@ Field *make_field(char *ptr, uint32 field_length, return new Field_datetime(ptr,null_pos,null_bit, unireg_check, field_name, table); case FIELD_TYPE_NULL: - default: // Impossible (Wrong version) return new Field_null(ptr,field_length,unireg_check,field_name,table); + default: // Impossible (Wrong version) + break; } - return 0; // Impossible (Wrong version) + return 0; // Impossible } diff --git a/sql/field.h b/sql/field.h index fb3cf2178e2..29c185505c7 100644 --- a/sql/field.h +++ b/sql/field.h @@ -1,23 +1,23 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* -** Because of the function new_field all field classes that have static -** variables must declare the size_of() member function. + Because of the function new_field() all field classes that have static + variables must declare the size_of() member function. */ #ifdef __GNUC__ @@ -31,27 +31,28 @@ struct st_cache_field; void field_conv(Field *to,Field *from); class Field { - Field(const Item &); /* Prevent use of theese */ + Field(const Item &); /* Prevent use of these */ void operator=(Field &); public: static void *operator new(size_t size) {return (void*) sql_alloc((uint) size); } static void operator delete(void *ptr_arg, size_t size) {} /*lint -e715 */ - enum utype { NONE,DATE,SHIELD,NOEMPTY,CASEUP,PNR,BGNR,PGNR,YES,NO,REL, - CHECK,EMPTY,UNKNOWN_FIELD,CASEDN,NEXT_NUMBER,INTERVAL_FIELD, - BIT_FIELD, TIMESTAMP_FIELD,CAPITALIZE,BLOB_FIELD}; - char *ptr; // Position to field in record + char *ptr; // Position to field in record uchar *null_ptr; // Byte where null_bit is - uint8 null_bit; // And position to it struct st_table *table; // Pointer for table - ulong query_id; // For quick test of used fields - key_map key_start,part_of_key,part_of_sortkey;// Field is part of these keys. - const char *table_name,*field_name; - utype unireg_check; - uint32 field_length; // Length of field - uint16 flags; - - Field(char *ptr_arg,uint32 length_arg,uchar *null_ptr_arg,uint null_bit_arg, + const char *table_name,*field_name; + ulong query_id; // For quick test of used fields + /* Field is part of the following keys */ + key_map key_start,part_of_key,part_of_sortkey; + enum utype { NONE,DATE,SHIELD,NOEMPTY,CASEUP,PNR,BGNR,PGNR,YES,NO,REL, + CHECK,EMPTY,UNKNOWN_FIELD,CASEDN,NEXT_NUMBER,INTERVAL_FIELD, + BIT_FIELD, TIMESTAMP_FIELD,CAPITALIZE,BLOB_FIELD}; + utype unireg_check; + uint32 field_length; // Length of field + uint16 flags; + uchar null_bit; // Bit used to test null bit + + Field(char *ptr_arg,uint32 length_arg,uchar *null_ptr_arg,uchar null_bit_arg, utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg); virtual ~Field() {} @@ -69,6 +70,13 @@ public: virtual uint32 pack_length() const { return (uint32) field_length; } virtual void reset(void) { bzero(ptr,pack_length()); } virtual void reset_fields() {} + virtual void set_default() + { + memcpy(ptr, ptr + table->rec_buff_length, pack_length()); + if (null_ptr) + *null_ptr= ((*null_ptr & (uchar) ~null_bit) | + null_ptr[table->rec_buff_length] & null_bit); + } virtual bool binary() const { return 1; } virtual bool zero_pack() const { return 1; } virtual enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; } @@ -77,7 +85,7 @@ public: virtual enum_field_types real_type() const { return type(); } inline int cmp(const char *str) { return cmp(ptr,str); } virtual int cmp(const char *,const char *)=0; - virtual int cmp_binary(const char *a,const char *b, ulong max_length=~0L) + virtual int cmp_binary(const char *a,const char *b, uint32 max_length=~0L) { return memcmp(a,b,pack_length()); } virtual int cmp_offset(uint row_offset) { return memcmp(ptr,ptr+row_offset,pack_length()); } @@ -88,12 +96,13 @@ public: virtual int key_cmp(const byte *str, uint length) { return cmp(ptr,(char*) str); } virtual uint decimals() const { return 0; } + /* + Caller beware: sql_type can change str.Ptr, so check + ptr() to see if it changed if you are using your own buffer + in str and restore it with set() if needed + */ virtual void sql_type(String &str) const =0; - // Caller beware: sql_type can change str.Ptr, so check - // ptr() to see if it changed if you are using your own buffer - // in str and restore it with set() if needed - - virtual uint size_of() const =0; // For new field + virtual uint size_of() const =0; // For new field inline bool is_null(uint row_offset=0) { return null_ptr ? (null_ptr[row_offset] & null_bit ? 1 : 0) : table->null_row; } inline bool is_real_null(uint row_offset=0) @@ -101,30 +110,30 @@ public: inline void set_null(int row_offset=0) { if (null_ptr) null_ptr[row_offset]|= null_bit; } inline void set_notnull(int row_offset=0) - { if (null_ptr) null_ptr[row_offset]&= ~null_bit; } + { if (null_ptr) null_ptr[row_offset]&= (uchar) ~null_bit; } inline bool maybe_null(void) { return null_ptr != 0 || table->maybe_null; } inline bool real_maybe_null(void) { return null_ptr != 0; } virtual void make_field(Send_field *)=0; virtual void sort_string(char *buff,uint length)=0; - virtual bool optimize_range(); + virtual bool optimize_range(uint idx); virtual bool store_for_compare() { return 0; } - inline Field *new_field(struct st_table *new_table) - { - Field *tmp= (Field*) sql_memdup((char*) this,size_of()); - if (tmp) - { - tmp->table=new_table; - tmp->key_start=tmp->part_of_key=tmp->part_of_sortkey=0; - tmp->unireg_check=Field::NONE; - tmp->flags&= (NOT_NULL_FLAG | BLOB_FLAG | UNSIGNED_FLAG | ZEROFILL_FLAG | BINARY_FLAG | ENUM_FLAG | SET_FLAG); - tmp->reset_fields(); - } - return tmp; - } - inline void move_field(char *ptr_arg,uchar *null_ptr_arg,uint null_bit_arg) + Field *new_field(MEM_ROOT *root, struct st_table *new_table) + { + Field *tmp= (Field*) memdup_root(root,(char*) this,size_of()); + if (tmp) { - ptr=ptr_arg; null_ptr=null_ptr_arg; null_bit=null_bit_arg; + tmp->table=new_table; + tmp->key_start=tmp->part_of_key=tmp->part_of_sortkey=0; + tmp->unireg_check=Field::NONE; + tmp->flags&= (NOT_NULL_FLAG | BLOB_FLAG | UNSIGNED_FLAG | ZEROFILL_FLAG | BINARY_FLAG | ENUM_FLAG | SET_FLAG); + tmp->reset_fields(); } + return tmp; + } + inline void move_field(char *ptr_arg,uchar *null_ptr_arg,uchar null_bit_arg) + { + ptr=ptr_arg; null_ptr=null_ptr_arg; null_bit=null_bit_arg; + } inline void move_field(char *ptr_arg) { ptr=ptr_arg; } inline void move_field(my_ptrdiff_t ptr_diff) { @@ -154,10 +163,10 @@ public: ptr-=row_offset; return tmp; } - bool send(String *packet); + bool send(THD *thd, String *packet); virtual char *pack(char* to, const char *from, uint max_length=~(uint) 0) { - uint length=pack_length(); + uint32 length=pack_length(); memcpy(to,from,length); return to+length; } @@ -185,7 +194,7 @@ public: { return cmp(a,b); } virtual int pack_cmp(const char *b, uint key_length_arg) { return cmp(ptr,b); } - uint offset(); // Should be inline ... + uint offset(); // Should be inline ... void copy_from_tmp(int offset); uint fill_cache_field(struct st_cache_field *copy); virtual bool get_date(TIME *ltime,bool fuzzydate); @@ -210,12 +219,12 @@ public: class Field_num :public Field { public: const uint8 dec; - bool zerofill,unsigned_flag; // Purify cannot handle bit fields + bool zerofill,unsigned_flag; // Purify cannot handle bit fields Field_num(char *ptr_arg,uint32 len_arg, uchar *null_ptr_arg, - uint null_bit_arg, utype unireg_check_arg, + uchar null_bit_arg, utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg, - uint dec_arg,bool zero_arg,bool unsigned_arg) + uint8 dec_arg,bool zero_arg,bool unsigned_arg) :Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, table_arg), dec(dec_arg),zerofill(zero_arg),unsigned_flag(unsigned_arg) @@ -230,7 +239,7 @@ public: void add_zerofill_and_unsigned(String &res) const; friend class create_field; void make_field(Send_field *); - uint decimals() const { return dec; } + uint decimals() const { return (uint) dec; } uint size_of() const { return sizeof(*this); } bool eq_def(Field *field); }; @@ -239,7 +248,7 @@ public: class Field_str :public Field { public: Field_str(char *ptr_arg,uint32 len_arg, uchar *null_ptr_arg, - uint null_bit_arg, utype unireg_check_arg, + uchar null_bit_arg, utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg) :Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, @@ -256,10 +265,10 @@ public: class Field_decimal :public Field_num { public: Field_decimal(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, - uint null_bit_arg, + uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg, - uint dec_arg,bool zero_arg,bool unsigned_arg) + uint8 dec_arg,bool zero_arg,bool unsigned_arg) :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, table_arg, dec_arg, zero_arg,unsigned_arg) @@ -285,7 +294,7 @@ public: class Field_tiny :public Field_num { public: Field_tiny(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, - uint null_bit_arg, + uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg, bool zero_arg, bool unsigned_arg) @@ -314,7 +323,7 @@ public: class Field_short :public Field_num { public: Field_short(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, - uint null_bit_arg, + uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg, bool zero_arg, bool unsigned_arg) @@ -343,7 +352,7 @@ public: class Field_medium :public Field_num { public: Field_medium(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, - uint null_bit_arg, + uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg, bool zero_arg, bool unsigned_arg) @@ -372,7 +381,7 @@ public: class Field_long :public Field_num { public: Field_long(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, - uint null_bit_arg, + uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg, bool zero_arg, bool unsigned_arg) @@ -407,7 +416,7 @@ public: class Field_longlong :public Field_num { public: Field_longlong(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, - uint null_bit_arg, + uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg, bool zero_arg, bool unsigned_arg) @@ -415,10 +424,11 @@ public: unireg_check_arg, field_name_arg, table_arg, 0, zero_arg,unsigned_arg) {} - Field_longlong(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg, - struct st_table *table_arg) + Field_longlong(uint32 len_arg,bool maybe_null_arg, + const char *field_name_arg, + struct st_table *table_arg, bool unsigned_arg) :Field_num((char*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0,0, - NONE, field_name_arg, table_arg,0,0,0) + NONE, field_name_arg, table_arg,0,0,unsigned_arg) {} enum Item_result result_type () const { return INT_RESULT; } enum_field_types type() const { return FIELD_TYPE_LONGLONG;} @@ -435,16 +445,17 @@ public: void sort_string(char *buff,uint length); uint32 pack_length() const { return 8; } void sql_type(String &str) const; + bool store_for_compare() { return 1; } }; #endif class Field_float :public Field_num { public: Field_float(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, - uint null_bit_arg, + uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg, - uint dec_arg,bool zero_arg,bool unsigned_arg) + uint8 dec_arg,bool zero_arg,bool unsigned_arg) :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, table_arg, dec_arg, zero_arg,unsigned_arg) @@ -468,16 +479,16 @@ public: class Field_double :public Field_num { public: Field_double(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, - uint null_bit_arg, + uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg, - uint dec_arg,bool zero_arg,bool unsigned_arg) + uint8 dec_arg,bool zero_arg,bool unsigned_arg) :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, table_arg, dec_arg, zero_arg,unsigned_arg) {} Field_double(uint32 len_arg, bool maybe_null_arg, const char *field_name_arg, - struct st_table *table_arg, uint dec_arg) + struct st_table *table_arg, uint8 dec_arg) :Field_num((char*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0, (uint) 0, NONE, field_name_arg, table_arg,dec_arg,0,0) {} @@ -547,6 +558,10 @@ public: bool store_for_compare() { return 1; } bool zero_pack() const { return 0; } void set_time(); + virtual void set_default() + { + set_time(); + } inline long get_timestamp() { #ifdef WORDS_BIGENDIAN @@ -566,7 +581,7 @@ public: class Field_year :public Field_tiny { public: Field_year(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, - uint null_bit_arg, + uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg) :Field_tiny(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, @@ -585,12 +600,16 @@ public: class Field_date :public Field_str { public: - Field_date(char *ptr_arg, uchar *null_ptr_arg, uint null_bit_arg, + Field_date(char *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg) :Field_str(ptr_arg, 10, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, table_arg) {} + Field_date(bool maybe_null_arg, const char *field_name_arg, + struct st_table *table_arg) + :Field_str((char*) 0,10, maybe_null_arg ? (uchar*) "": 0,0, + NONE, field_name_arg, table_arg) {} enum_field_types type() const { return FIELD_TYPE_DATE;} enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONG_INT; } enum Item_result cmp_type () const { return INT_RESULT; } @@ -611,7 +630,7 @@ public: class Field_newdate :public Field_str { public: - Field_newdate(char *ptr_arg, uchar *null_ptr_arg, uint null_bit_arg, + Field_newdate(char *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg) :Field_str(ptr_arg, 10, null_ptr_arg, null_bit_arg, @@ -642,12 +661,16 @@ public: class Field_time :public Field_str { public: - Field_time(char *ptr_arg, uchar *null_ptr_arg, uint null_bit_arg, + Field_time(char *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg) :Field_str(ptr_arg, 8, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, table_arg) {} + Field_time(bool maybe_null_arg, const char *field_name_arg, + struct st_table *table_arg) + :Field_str((char*) 0,8, maybe_null_arg ? (uchar*) "": 0,0, + NONE, field_name_arg, table_arg) {} enum_field_types type() const { return FIELD_TYPE_TIME;} enum ha_base_keytype key_type() const { return HA_KEYTYPE_INT24; } enum Item_result cmp_type () const { return INT_RESULT; } @@ -670,12 +693,16 @@ public: class Field_datetime :public Field_str { public: - Field_datetime(char *ptr_arg, uchar *null_ptr_arg, uint null_bit_arg, + Field_datetime(char *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg) :Field_str(ptr_arg, 19, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, table_arg) {} + Field_datetime(bool maybe_null_arg, const char *field_name_arg, + struct st_table *table_arg) + :Field_str((char*) 0,19, maybe_null_arg ? (uchar*) "": 0,0, + NONE, field_name_arg, table_arg) {} enum_field_types type() const { return FIELD_TYPE_DATETIME;} #ifdef HAVE_LONG_LONG enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONGLONG; } @@ -704,7 +731,7 @@ class Field_string :public Field_str { bool binary_flag; public: Field_string(char *ptr_arg, uint32 len_arg,uchar *null_ptr_arg, - uint null_bit_arg, + uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg,bool binary_arg) :Field_str(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, @@ -759,7 +786,7 @@ class Field_varstring :public Field_str { bool binary_flag; public: Field_varstring(char *ptr_arg, uint32 len_arg,uchar *null_ptr_arg, - uint null_bit_arg, + uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg,bool binary_arg) :Field_str(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, @@ -809,10 +836,10 @@ public: class Field_blob :public Field_str { uint packlength; - String value; // For temporaries + String value; // For temporaries bool binary_flag; public: - Field_blob(char *ptr_arg, uchar *null_ptr_arg, uint null_bit_arg, + Field_blob(char *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg,uint blob_pack_length, bool binary_arg); @@ -836,21 +863,22 @@ public: longlong val_int(void); String *val_str(String*,String *); int cmp(const char *,const char*); - int cmp(const char *a, ulong a_length, const char *b, ulong b_length); + int cmp(const char *a, uint32 a_length, const char *b, uint32 b_length); int cmp_offset(uint offset); - int cmp_binary(const char *a,const char *b, ulong max_length=~0L); + int cmp_binary(const char *a,const char *b, uint32 max_length=~0L); int cmp_binary_offset(uint row_offset); int key_cmp(const byte *,const byte*); int key_cmp(const byte *str, uint length); uint32 key_length() const { return 0; } void sort_string(char *buff,uint length); - uint32 pack_length() const { return (uint32) (packlength+table->blob_ptr_size); } - void reset(void) { bzero(ptr,packlength+sizeof(char*)); } + uint32 pack_length() const + { return (uint32) (packlength+table->blob_ptr_size); } + void reset(void) { bzero(ptr, packlength+sizeof(char*)); } void reset_fields() { bzero((char*) &value,sizeof(value)); } - void store_length(ulong number); - inline ulong get_length(uint row_offset=0) + void store_length(uint32 number); + inline uint32 get_length(uint row_offset=0) { return get_length(ptr+row_offset); } - ulong get_length(const char *ptr); + uint32 get_length(const char *ptr); bool binary() const { return binary_flag; } inline void get_ptr(char **str) { @@ -861,7 +889,7 @@ public: memcpy(ptr,length,packlength); memcpy_fixed(ptr+packlength,&data,sizeof(char*)); } - inline void set_ptr(ulong length,char *data) + inline void set_ptr(uint32 length,char *data) { store_length(length); memcpy_fixed(ptr+packlength,&data,sizeof(char*)); @@ -882,21 +910,6 @@ 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); - inline void get_ptr_from_key_image(char **str,char *key_str) - { - *str = key_str + sizeof(uint16); - } - inline uint get_length_from_key_image(char *key_str) - { - return uint2korr(key_str); - } - 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); @@ -916,16 +929,16 @@ protected: public: TYPELIB *typelib; Field_enum(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, - uint null_bit_arg, + uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg,uint packlength_arg, TYPELIB *typelib_arg) :Field_str(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, table_arg), packlength(packlength_arg),typelib(typelib_arg) - { + { flags|=ENUM_FLAG; - } + } enum_field_types type() const { return FIELD_TYPE_STRING; } enum Item_result cmp_type () const { return INT_RESULT; } enum ha_base_keytype key_type() const; @@ -944,7 +957,7 @@ public: uint size_of() const { return sizeof(*this); } enum_field_types real_type() const { return FIELD_TYPE_ENUM; } virtual bool zero_pack() const { return 0; } - bool optimize_range() { return 0; } + bool optimize_range(uint idx) { return 0; } bool binary() const { return 0; } bool eq_def(Field *field); }; @@ -953,7 +966,7 @@ public: class Field_set :public Field_enum { public: Field_set(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, - uint null_bit_arg, + uchar null_bit_arg, enum utype unireg_check_arg, const char *field_name_arg, struct st_table *table_arg,uint32 packlength_arg, TYPELIB *typelib_arg) @@ -975,23 +988,23 @@ public: /* -** Create field class for CREATE TABLE + Create field class for CREATE TABLE */ class create_field :public Sql_alloc { public: const char *field_name; - const char *change; // If done with alter table - const char *after; // Put column after this one - Item *def; // Default value + const char *change; // If done with alter table + const char *after; // Put column after this one + Item *def; // Default value enum enum_field_types sql_type; uint32 length; uint decimals,flags,pack_length; Field::utype unireg_check; - TYPELIB *interval; // Which interval to use - Field *field; // For alter table + TYPELIB *interval; // Which interval to use + Field *field; // For alter table - uint8 row,col,sc_length,interval_id; // For rea_create_table + uint8 row,col,sc_length,interval_id; // For rea_create_table uint offset,pack_flag; create_field() :after(0) {} create_field(Field *field, Field *orig_field); @@ -999,7 +1012,7 @@ public: /* -** A class for sending info to the client + A class for sending info to the client */ class Send_field { @@ -1012,7 +1025,7 @@ class Send_field { /* -** A class for quick copying data to fields + A class for quick copying data to fields */ class Copy_field :public Sql_alloc { @@ -1028,7 +1041,7 @@ public: Copy_field() {} ~Copy_field() {} - void set(Field *to,Field *from,bool save); // Field to field + void set(Field *to,Field *from,bool save); // Field to field void set(char *to,Field *from); // Field to string void (*do_copy)(Copy_field *); void (*do_copy2)(Copy_field *); // Used to handle null values @@ -1036,30 +1049,33 @@ public: Field *make_field(char *ptr, uint32 field_length, - uchar *null_pos, uint null_bit, - uint pack_flag, Field::utype unireg_check, + uchar *null_pos, uchar null_bit, + uint pack_flag, + enum_field_types field_type, + Field::utype unireg_check, TYPELIB *interval, const char *field_name, struct st_table *table); uint pack_length_to_packflag(uint type); uint32 calc_pack_length(enum_field_types type,uint32 length); bool set_field_to_null(Field *field); -bool set_field_to_null_with_conversions(Field *field); +bool set_field_to_null_with_conversions(Field *field, bool no_conversions); uint find_enum(TYPELIB *typelib,const char *x, uint length); ulonglong find_set(TYPELIB *typelib,const char *x, uint length); bool test_if_int(const char *str,int length); /* -** The following are for the interface with the .frm file + The following are for the interface with the .frm file */ #define FIELDFLAG_DECIMAL 1 -#define FIELDFLAG_BINARY 1 // Shares same flag +#define FIELDFLAG_BINARY 1 // Shares same flag #define FIELDFLAG_NUMBER 2 #define FIELDFLAG_ZEROFILL 4 #define FIELDFLAG_PACK 120 // Bits used for packing #define FIELDFLAG_INTERVAL 256 #define FIELDFLAG_BITFIELD 512 // mangled with dec! #define FIELDFLAG_BLOB 1024 // mangled with dec! +#define FIELDFLAG_GEOM 2048 #define FIELDFLAG_LEFT_FULLSCREEN 8192 #define FIELDFLAG_RIGHT_FULLSCREEN 16384 #define FIELDFLAG_FORMAT_NUMBER 16384 // predit: ###,,## in output @@ -1080,12 +1096,13 @@ bool test_if_int(const char *str,int length); #define f_is_zerofill(x) ((x) & FIELDFLAG_ZEROFILL) #define f_is_packed(x) ((x) & FIELDFLAG_PACK) #define f_packtype(x) (((x) >> FIELDFLAG_PACK_SHIFT) & 15) -#define f_decimals(x) (((x) >> FIELDFLAG_DEC_SHIFT) & FIELDFLAG_MAX_DEC) +#define f_decimals(x) ((uint8) (((x) >> FIELDFLAG_DEC_SHIFT) & FIELDFLAG_MAX_DEC)) #define f_is_alpha(x) (!f_is_num(x)) #define f_is_binary(x) ((x) & FIELDFLAG_BINARY) #define f_is_enum(x) ((x) & FIELDFLAG_INTERVAL) #define f_is_bitfield(x) ((x) & FIELDFLAG_BITFIELD) #define f_is_blob(x) (((x) & (FIELDFLAG_BLOB | FIELDFLAG_NUMBER)) == FIELDFLAG_BLOB) +#define f_is_geom(x) ((x) & FIELDFLAG_GEOM) #define f_is_equ(x) ((x) & (1+2+FIELDFLAG_PACK+31*256)) #define f_settype(x) (((int) x) << FIELDFLAG_PACK_SHIFT) #define f_maybe_null(x) (x & FIELDFLAG_MAYBE_NULL) diff --git a/sql/field_conv.cc b/sql/field_conv.cc index dab96a9b827..42272dd616f 100644 --- a/sql/field_conv.cc +++ b/sql/field_conv.cc @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -118,12 +118,39 @@ set_field_to_null(Field *field) field->reset(); return 0; } + field->reset(); + if (current_thd->count_cuted_fields) + { + current_thd->cuted_fields++; // Increment error counter + return 0; + } + if (!current_thd->no_errors) + my_printf_error(ER_BAD_NULL_ERROR,ER(ER_BAD_NULL_ERROR),MYF(0), + field->field_name); return 1; } +/* + Set field to NULL or TIMESTAMP or to next auto_increment number + + SYNOPSIS + set_field_to_null_with_conversions() + field Field to update + no_conversion Set to 1 if we should return 1 if field can't + take null values. + If set to 0 we will do store the 'default value' + if the field is a special field. If not we will + give an error. + + RETURN VALUES + 0 Field could take 0 or an automatic conversion was used + 1 Field could not take NULL and no conversion was used. + If no_conversion was not set, an error message is printed +*/ + bool -set_field_to_null_with_conversions(Field *field) +set_field_to_null_with_conversions(Field *field, bool no_conversions) { if (field->real_maybe_null()) { @@ -131,6 +158,8 @@ set_field_to_null_with_conversions(Field *field) field->reset(); return 0; } + if (no_conversions) + return 1; /* Check if this is a special type, which will get a special walue @@ -156,8 +185,6 @@ set_field_to_null_with_conversions(Field *field) } - - static void do_skip(Copy_field *copy __attribute__((unused))) { } diff --git a/sql/filesort.cc b/sql/filesort.cc index ee87d508dd3..2ac05ef0496 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -22,13 +22,11 @@ #include <stddef.h> /* for macro offsetof */ #endif #include <m_ctype.h> +#include "sql_sort.h" + #ifndef THREAD -#define SKIPP_DBUG_IN_FILESORT +#define SKIP_DBUG_IN_FILESORT #endif - /* static variabels */ - -#define MERGEBUFF 7 -#define MERGEBUFF2 15 /* How to write record_ref. */ @@ -36,85 +34,65 @@ if (my_b_write((file),(byte*) (from),param->ref_length)) \ DBUG_RETURN(1); -typedef struct st_buffpek { /* Struktur om sorteringsbuffrarna */ - my_off_t file_pos; /* Where we are in the sort file */ - uchar *base,*key; /* key pointers */ - ha_rows count; /* Number of rows in table */ - ulong mem_count; /* numbers of keys in memory */ - ulong max_keys; /* Max keys in buffert */ -} BUFFPEK; - - -typedef struct st_sort_param { - uint sort_length; /* Length of sortarg */ - uint keys; /* Max antal nycklar / buffert */ - uint ref_length; /* Length of record ref. */ - ha_rows max_rows,examined_rows; - TABLE *sort_form; /* For quicker make_sortkey */ - SORT_FIELD *local_sortorder; - SORT_FIELD *end; -#ifdef USE_STRCOLL - char* tmp_buffer; -#endif -} SORTPARAM; - /* functions defined in this file */ static char **make_char_array(register uint fields, uint length, myf my_flag); +static BUFFPEK *read_buffpek_from_file(IO_CACHE *buffer_file, uint count); static ha_rows find_all_keys(SORTPARAM *param,SQL_SELECT *select, - uchar * *sort_keys, - BUFFPEK *buffpek,uint *maxbuffer, + uchar * *sort_keys, IO_CACHE *buffer_file, IO_CACHE *tempfile,IO_CACHE *indexfile); static int write_keys(SORTPARAM *param,uchar * *sort_keys, - uint count,BUFFPEK *buffpek, - IO_CACHE *tempfile); -static void make_sortkey(SORTPARAM *param,uchar *to, - byte *ref_pos); -static bool save_index(SORTPARAM *param,uchar **sort_keys, uint count); -static int merge_many_buff(SORTPARAM *param,uchar * *sort_keys, - BUFFPEK *buffpek, - uint *maxbuffer, IO_CACHE *t_file); -static uint read_to_buffer(IO_CACHE *fromfile,BUFFPEK *buffpek, - uint sort_length); -static int merge_buffers(SORTPARAM *param,IO_CACHE *from_file, - IO_CACHE *to_file,uchar * *sort_keys, - BUFFPEK *lastbuff,BUFFPEK *Fb, - BUFFPEK *Tb,int flag); -static int merge_index(SORTPARAM *param,uchar * *sort_keys, + uint count, IO_CACHE *buffer_file, IO_CACHE *tempfile); +static void make_sortkey(SORTPARAM *param,uchar *to, byte *ref_pos); +static int merge_index(SORTPARAM *param,uchar *sort_buffer, BUFFPEK *buffpek, uint maxbuffer,IO_CACHE *tempfile, IO_CACHE *outfile); +static bool save_index(SORTPARAM *param,uchar **sort_keys, uint count); static uint sortlength(SORT_FIELD *sortorder,uint length); - /* Makes a indexfil of recordnumbers of a sorted database */ - /* outfile is reset before data is written to it, if it wasn't - open a new file is opened */ + /* + Creates a set of pointers that can be used to read the rows + in sorted order. This should be done with the functions + in records.cc + + Before calling filesort, one must have done + table->file->info(HA_STATUS_VARIABLE) + + The result set is stored in table->io_cache or + table->record_pointers + */ -ha_rows filesort(TABLE **table, SORT_FIELD *sortorder, uint s_length, +ha_rows filesort(TABLE *table, SORT_FIELD *sortorder, uint s_length, SQL_SELECT *select, ha_rows special, ha_rows max_rows, ha_rows *examined_rows) { int error; - uint memavl,old_memavl,maxbuffer,skr; + ulong memavl; + uint maxbuffer; BUFFPEK *buffpek; ha_rows records; uchar **sort_keys; - IO_CACHE tempfile,*selected_records_file,*outfile; + IO_CACHE tempfile, buffpek_pointers, *selected_records_file, *outfile; SORTPARAM param; + THD *thd= current_thd; + DBUG_ENTER("filesort"); - DBUG_EXECUTE("info",TEST_filesort(table,sortorder,s_length,special);); -#ifdef SKIPP_DBUG_IN_FILESORT + DBUG_EXECUTE("info",TEST_filesort(sortorder,s_length,special);); +#ifdef SKIP_DBUG_IN_FILESORT DBUG_PUSH(""); /* No DBUG here */ #endif - outfile= table[0]->io_cache; + outfile= table->io_cache; my_b_clear(&tempfile); - buffpek= (BUFFPEK *) NULL; sort_keys= (uchar **) NULL; error= 1; - maxbuffer=1; - param.ref_length= table[0]->file->ref_length; + my_b_clear(&buffpek_pointers); + buffpek=0; + sort_keys= (uchar **) NULL; + error= 1; + bzero((char*) ¶m,sizeof(param)); + param.ref_length= table->file->ref_length; param.sort_length=sortlength(sortorder,s_length)+ param.ref_length; param.max_rows= max_rows; - param.examined_rows=0; if (select && select->quick) { @@ -139,17 +117,14 @@ ha_rows filesort(TABLE **table, SORT_FIELD *sortorder, uint s_length, #ifdef CAN_TRUST_RANGE else if (select && select->quick && select->quick->records > 0L) { - /* Get record-count */ - table[0]->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); records=min((ha_rows) (select->quick->records*2+EXTRA_RECORDS*2), - table[0]->file->records)+EXTRA_RECORDS; + table->file->records)+EXTRA_RECORDS; selected_records_file=0; } #endif else { - table[0]->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);/* Get record-count */ - records=table[0]->file->estimate_number_of_rows(); + records=table->file->estimate_number_of_rows(); selected_records_file= 0; } if (param.sort_length == param.ref_length && records > param.max_rows) @@ -161,54 +136,38 @@ ha_rows filesort(TABLE **table, SORT_FIELD *sortorder, uint s_length, goto err; #endif - memavl=sortbuff_size; + memavl= thd->variables.sortbuff_size; while (memavl >= MIN_SORT_MEMORY) { - if ((ulonglong) (records+1)*(param.sort_length+sizeof(char*))+sizeof(BUFFPEK)*10 < - (ulonglong) memavl) - param.keys=(uint) records+1; - else - { - maxbuffer=1; - do - { - skr=maxbuffer; - if (memavl < sizeof(BUFFPEK)*maxbuffer) - { - my_error(ER_OUT_OF_SORTMEMORY,MYF(ME_ERROR+ME_WAITTANG)); - goto err; - } - param.keys=(memavl-sizeof(BUFFPEK)*maxbuffer)/ - (param.sort_length+sizeof(char*)); - } - while ((maxbuffer= (uint) (records/param.keys+1)) != skr); - } - if ((sort_keys= (uchar **) make_char_array(param.keys,param.sort_length, + ulong old_memavl; + ulong keys= memavl/(param.sort_length+sizeof(char*)); + param.keys=(uint) min(records+1, keys); + if ((sort_keys= (uchar **) make_char_array(param.keys, param.sort_length, MYF(0)))) - if ((buffpek = (BUFFPEK*) my_malloc((uint) sizeof(BUFFPEK)* - (maxbuffer+10), - MYF(0)))) - break; - else - my_free((gptr) sort_keys,MYF(0)); + break; old_memavl=memavl; if ((memavl=memavl/4*3) < MIN_SORT_MEMORY && old_memavl > MIN_SORT_MEMORY) memavl=MIN_SORT_MEMORY; } - param.keys--; - maxbuffer+=10; /* Some extra range */ - if (memavl < MIN_SORT_MEMORY) { - my_error(ER_OUTOFMEMORY,MYF(ME_ERROR+ME_WAITTANG),sortbuff_size); + my_error(ER_OUTOFMEMORY,MYF(ME_ERROR+ME_WAITTANG), + thd->variables.sortbuff_size); goto err; } - param.sort_form= table[0]; + if (open_cached_file(&buffpek_pointers,mysql_tmpdir,TEMP_PREFIX, + DISK_BUFFER_SIZE, MYF(MY_WME))) + goto err; + + param.keys--; + param.sort_form= table; param.end=(param.local_sortorder=sortorder)+s_length; - if ((records=find_all_keys(¶m,select,sort_keys,buffpek,&maxbuffer, + if ((records=find_all_keys(¶m,select,sort_keys, &buffpek_pointers, &tempfile, selected_records_file)) == HA_POS_ERROR) goto err; + maxbuffer= (uint) (my_b_tell(&buffpek_pointers)/sizeof(*buffpek)); + if (maxbuffer == 0) // The whole set is in memory { if (save_index(¶m,sort_keys,(uint) records)) @@ -216,6 +175,9 @@ ha_rows filesort(TABLE **table, SORT_FIELD *sortorder, uint s_length, } else { + if (!(buffpek=read_buffpek_from_file(&buffpek_pointers, maxbuffer))) + goto err; + close_cached_file(&buffpek_pointers); /* Open cached file if it isn't open */ if (! my_b_inited(outfile) && open_cached_file(outfile,mysql_tmpdir,TEMP_PREFIX,READ_RECORD_BUFFER, @@ -223,14 +185,21 @@ ha_rows filesort(TABLE **table, SORT_FIELD *sortorder, uint s_length, goto err; reinit_io_cache(outfile,WRITE_CACHE,0L,0,0); + /* + Use also the space previously used by string pointers in sort_buffer + for temporary key storage. + */ param.keys=((param.keys*(param.sort_length+sizeof(char*))) / param.sort_length-1); - if (merge_many_buff(¶m,sort_keys,buffpek,&maxbuffer,&tempfile)) + maxbuffer--; // Offset from 0 + if (merge_many_buff(¶m,(uchar*) sort_keys,buffpek,&maxbuffer, + &tempfile)) goto err; if (flush_io_cache(&tempfile) || reinit_io_cache(&tempfile,READ_CACHE,0L,0,0)) goto err; - if (merge_index(¶m,sort_keys,buffpek,maxbuffer,&tempfile,outfile)) + if (merge_index(¶m,(uchar*) sort_keys,buffpek,maxbuffer,&tempfile, + outfile)) goto err; } if (records > param.max_rows) @@ -245,6 +214,7 @@ ha_rows filesort(TABLE **table, SORT_FIELD *sortorder, uint s_length, x_free((gptr) sort_keys); x_free((gptr) buffpek); close_cached_file(&tempfile); + close_cached_file(&buffpek_pointers); if (my_b_inited(outfile)) { if (flush_io_cache(outfile)) @@ -260,9 +230,9 @@ ha_rows filesort(TABLE **table, SORT_FIELD *sortorder, uint s_length, if (error) my_error(ER_FILSORT_ABORT,MYF(ME_ERROR+ME_WAITTANG)); else - statistic_add(filesort_rows, records, &LOCK_status); + statistic_add(filesort_rows, (ulong) records, &LOCK_status); *examined_rows= param.examined_rows; -#ifdef SKIPP_DBUG_IN_FILESORT +#ifdef SKIP_DBUG_IN_FILESORT DBUG_POP(); /* Ok to DBUG */ #endif DBUG_PRINT("exit",("records: %ld",records)); @@ -289,11 +259,33 @@ static char **make_char_array(register uint fields, uint length, myf my_flag) } /* make_char_array */ + /* Read all buffer pointers into memory */ + +static BUFFPEK *read_buffpek_from_file(IO_CACHE *buffpek_pointers, uint count) +{ + ulong length; + BUFFPEK *tmp; + DBUG_ENTER("read_buffpek_from_file"); + tmp=(BUFFPEK*) my_malloc(length=sizeof(BUFFPEK)*count, MYF(MY_WME)); + if (tmp) + { + if (reinit_io_cache(buffpek_pointers,READ_CACHE,0L,0,0) || + my_b_read(buffpek_pointers, (byte*) tmp, length)) + { + my_free((char*) tmp, MYF(0)); + tmp=0; + } + } + DBUG_RETURN(tmp); +} + + + /* Search after sort_keys and place them in a temp. file */ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, uchar **sort_keys, - BUFFPEK *buffpek, uint *maxbuffer, + IO_CACHE *buffpek_pointers, IO_CACHE *tempfile, IO_CACHE *indexfile) { int error,flag,quick_select; @@ -314,7 +306,7 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, ref_pos= ref_buff; quick_select=select && select->quick; record=0; - flag= ((!indexfile && file->option_flag() & HA_REC_NOT_IN_SEQ) + flag= ((!indexfile && file->table_flags() & HA_REC_NOT_IN_SEQ) || quick_select); if (indexfile || flag) ref_pos= &file->ref[0]; @@ -326,7 +318,8 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, file->extra(HA_EXTRA_KEYREAD); // QQ is removed next_pos=(byte*) 0; /* Find records in sequence */ file->rnd_init(); - file->extra(HA_EXTRA_CACHE); /* Quicker reads */ + file->extra_opt(HA_EXTRA_CACHE, + current_thd->variables.read_buff_size); } for (;;) @@ -375,9 +368,8 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, { if (idx == param->keys) { - if (indexpos >= *maxbuffer || - write_keys(param,sort_keys,idx,buffpek+indexpos,tempfile)) - DBUG_RETURN(HA_POS_ERROR); + if (write_keys(param,sort_keys,idx,buffpek_pointers,tempfile)) + DBUG_RETURN(HA_POS_ERROR); idx=0; if (param->ref_length == param->sort_length && my_b_tell(tempfile)/param->sort_length >= param->max_rows) @@ -404,11 +396,9 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, file->print_error(error,MYF(ME_ERROR | ME_WAITTANG)); /* purecov: inspected */ DBUG_RETURN(HA_POS_ERROR); /* purecov: inspected */ } - if (indexpos && idx) - if (indexpos >= *maxbuffer || - write_keys(param,sort_keys,idx,buffpek+indexpos,tempfile)) - DBUG_RETURN(HA_POS_ERROR); /* purecov: inspected */ - *maxbuffer=indexpos; + if (indexpos && idx && + write_keys(param,sort_keys,idx,buffpek_pointers,tempfile)) + DBUG_RETURN(HA_POS_ERROR); /* purecov: inspected */ DBUG_RETURN(my_b_inited(tempfile) ? (ha_rows) (my_b_tell(tempfile)/param->sort_length) : idx); @@ -417,10 +407,13 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, /* Skriver en buffert med nycklar till filen */ -static int write_keys(SORTPARAM *param, register uchar **sort_keys, uint count, - BUFFPEK *buffpek, IO_CACHE *tempfile) +static int +write_keys(SORTPARAM *param, register uchar **sort_keys, uint count, + IO_CACHE *buffpek_pointers, IO_CACHE *tempfile) { uint sort_length; + uchar **end; + BUFFPEK buffpek; DBUG_ENTER("write_keys"); sort_length=param->sort_length; @@ -432,15 +425,20 @@ static int write_keys(SORTPARAM *param, register uchar **sort_keys, uint count, if (!my_b_inited(tempfile) && open_cached_file(tempfile,mysql_tmpdir,TEMP_PREFIX,DISK_BUFFER_SIZE, MYF(MY_WME))) - DBUG_RETURN(1); /* purecov: inspected */ - buffpek->file_pos=my_b_tell(tempfile); + goto err; /* purecov: inspected */ + buffpek.file_pos=my_b_tell(tempfile); if ((ha_rows) count > param->max_rows) count=(uint) param->max_rows; /* purecov: inspected */ - buffpek->count=(ha_rows) count; - for (uchar **end=sort_keys+count ; sort_keys != end ; sort_keys++) + buffpek.count=(ha_rows) count; + for (end=sort_keys+count ; sort_keys != end ; sort_keys++) if (my_b_write(tempfile,(byte*) *sort_keys,(uint) sort_length)) - DBUG_RETURN(1); + goto err; + if (my_b_write(buffpek_pointers, (byte*) &buffpek, sizeof(buffpek))) + goto err; DBUG_RETURN(0); + +err: + DBUG_RETURN(1); } /* write_keys */ @@ -636,8 +634,8 @@ static bool save_index(SORTPARAM *param, uchar **sort_keys, uint count) /* Merge buffers to make < MERGEBUFF2 buffers */ -static int merge_many_buff(SORTPARAM *param, uchar **sort_keys, - BUFFPEK *buffpek, uint *maxbuffer, IO_CACHE *t_file) +int merge_many_buff(SORTPARAM *param, uchar *sort_buffer, + BUFFPEK *buffpek, uint *maxbuffer, IO_CACHE *t_file) { register int i; IO_CACHE t_file2,*from_file,*to_file,*temp; @@ -659,11 +657,11 @@ static int merge_many_buff(SORTPARAM *param, uchar **sort_keys, lastbuff=buffpek; for (i=0 ; i <= (int) *maxbuffer-MERGEBUFF*3/2 ; i+=MERGEBUFF) { - if (merge_buffers(param,from_file,to_file,sort_keys,lastbuff++, + if (merge_buffers(param,from_file,to_file,sort_buffer,lastbuff++, buffpek+i,buffpek+i+MERGEBUFF-1,0)) break; /* purecov: inspected */ } - if (merge_buffers(param,from_file,to_file,sort_keys,lastbuff++, + if (merge_buffers(param,from_file,to_file,sort_buffer,lastbuff++, buffpek+i,buffpek+ *maxbuffer,0)) break; /* purecov: inspected */ if (flush_io_cache(to_file)) @@ -682,8 +680,8 @@ static int merge_many_buff(SORTPARAM *param, uchar **sort_keys, /* Read data to buffer */ /* This returns (uint) -1 if something goes wrong */ -static uint read_to_buffer(IO_CACHE *fromfile, BUFFPEK *buffpek, - uint sort_length) +uint read_to_buffer(IO_CACHE *fromfile, BUFFPEK *buffpek, + uint sort_length) { register uint count; uint length; @@ -704,39 +702,44 @@ static uint read_to_buffer(IO_CACHE *fromfile, BUFFPEK *buffpek, /* Merge buffers to one buffer */ -static int merge_buffers(SORTPARAM *param, IO_CACHE *from_file, - IO_CACHE *to_file, uchar **sort_keys, - BUFFPEK *lastbuff, BUFFPEK *Fb, BUFFPEK *Tb, - int flag) +int merge_buffers(SORTPARAM *param, IO_CACHE *from_file, + IO_CACHE *to_file, uchar *sort_buffer, + BUFFPEK *lastbuff, BUFFPEK *Fb, BUFFPEK *Tb, + int flag) { int error; uint sort_length,offset; ulong maxcount; - ha_rows count,max_rows; + ha_rows max_rows,org_max_rows; my_off_t to_start_filepos; uchar *strpos; BUFFPEK *buffpek,**refpek; QUEUE queue; + qsort2_cmp cmp; volatile bool *killed= ¤t_thd->killed; + bool not_killable; DBUG_ENTER("merge_buffers"); statistic_increment(filesort_merge_passes, &LOCK_status); + if (param->not_killable) + { + killed= ¬_killable; + not_killable=0; + } - count=error=0; - offset=param->sort_length-param->ref_length; + error=0; + offset=(sort_length=param->sort_length)-param->ref_length; maxcount=(ulong) (param->keys/((uint) (Tb-Fb) +1)); to_start_filepos=my_b_tell(to_file); - strpos=(uchar*) sort_keys; - sort_length=param->sort_length; - max_rows=param->max_rows; + strpos=(uchar*) sort_buffer; + org_max_rows=max_rows=param->max_rows; if (init_queue(&queue,(uint) (Tb-Fb)+1,offsetof(BUFFPEK,key),0, - (int (*) (void *, byte *,byte*)) - get_ptr_compare(sort_length),(void*) &sort_length)) + (queue_compare) + (cmp=get_ptr_compare(sort_length)),(void*) &sort_length)) DBUG_RETURN(1); /* purecov: inspected */ for (buffpek= Fb ; buffpek <= Tb ; buffpek++) { - count+= buffpek->count; buffpek->base= strpos; buffpek->max_keys=maxcount; strpos+= (uint) (error=(int) read_to_buffer(from_file,buffpek, @@ -746,15 +749,50 @@ static int merge_buffers(SORTPARAM *param, IO_CACHE *from_file, queue_insert(&queue,(byte*) buffpek); } + if (param->unique_buff) + { + /* + Called by Unique::get() + Copy the first argument to param->unique_buff for unique removal. + Store it also in 'to_file'. + + This is safe as we know that there is always more than one element + in each block to merge (This is guaranteed by the Unique:: algorithm + */ + buffpek=(BUFFPEK*) queue_top(&queue); + memcpy(param->unique_buff, buffpek->key, sort_length); + if (my_b_write(to_file,(byte*) buffpek->key, sort_length)) + { + error=1; goto err; /* purecov: inspected */ + } + buffpek->key+=sort_length; + buffpek->mem_count--; + if (!--max_rows) + { + error=0; /* purecov: inspected */ + goto end; /* purecov: inspected */ + } + queue_replaced(&queue); // Top element has been used + } + else + cmp=0; // Not unique + while (queue.elements > 1) { if (*killed) { - error=1; goto err; /* purecov: inspected */ + error=1; goto err; /* purecov: inspected */ } for (;;) { buffpek=(BUFFPEK*) queue_top(&queue); + if (cmp) // Remove duplicates + { + if (!(*cmp)(&sort_length, &(param->unique_buff), + (uchar**) &buffpek->key)) + goto skip_duplicate; + memcpy(param->unique_buff, (uchar*) buffpek->key,sort_length); + } if (flag == 0) { if (my_b_write(to_file,(byte*) buffpek->key, sort_length)) @@ -771,6 +809,8 @@ static int merge_buffers(SORTPARAM *param, IO_CACHE *from_file, error=0; /* purecov: inspected */ goto end; /* purecov: inspected */ } + + skip_duplicate: buffpek->key+=sort_length; if (! --buffpek->mem_count) { @@ -803,14 +843,28 @@ static int merge_buffers(SORTPARAM *param, IO_CACHE *from_file, break; /* One buffer have been removed */ } else if (error == -1) - goto err; /* purecov: inspected */ + goto err; /* purecov: inspected */ } queue_replaced(&queue); /* Top element has been replaced */ } } buffpek=(BUFFPEK*) queue_top(&queue); - buffpek->base=(uchar *) sort_keys; + buffpek->base= sort_buffer; buffpek->max_keys=param->keys; + + /* + As we know all entries in the buffer are unique, we only have to + check if the first one is the same as the last one we wrote + */ + if (cmp) + { + if (!(*cmp)(&sort_length, &(param->unique_buff), (uchar**) &buffpek->key)) + { + buffpek->key+=sort_length; // Remove duplicate + --buffpek->mem_count; + } + } + do { if ((ha_rows) buffpek->mem_count > max_rows) @@ -818,6 +872,7 @@ static int merge_buffers(SORTPARAM *param, IO_CACHE *from_file, buffpek->mem_count=(uint) max_rows; buffpek->count=0; /* Don't read more */ } + max_rows-=buffpek->mem_count; if (flag == 0) { if (my_b_write(to_file,(byte*) buffpek->key, @@ -842,7 +897,7 @@ static int merge_buffers(SORTPARAM *param, IO_CACHE *from_file, != -1 && error != 0); end: - lastbuff->count=min(count,param->max_rows); + lastbuff->count=min(org_max_rows-max_rows,param->max_rows); lastbuff->file_pos=to_start_filepos; err: delete_queue(&queue); @@ -852,12 +907,12 @@ err: /* Do a merge to output-file (save only positions) */ -static int merge_index(SORTPARAM *param, uchar **sort_keys, +static int merge_index(SORTPARAM *param, uchar *sort_buffer, BUFFPEK *buffpek, uint maxbuffer, IO_CACHE *tempfile, IO_CACHE *outfile) { DBUG_ENTER("merge_index"); - if (merge_buffers(param,tempfile,outfile,sort_keys,buffpek,buffpek, + if (merge_buffers(param,tempfile,outfile,sort_buffer,buffpek,buffpek, buffpek+maxbuffer,1)) DBUG_RETURN(1); /* purecov: inspected */ DBUG_RETURN(0); @@ -870,6 +925,7 @@ static uint sortlength(SORT_FIELD *sortorder, uint s_length) { reg2 uint length; + THD *thd= current_thd; length=0; for (; s_length-- ; sortorder++) @@ -877,7 +933,7 @@ sortlength(SORT_FIELD *sortorder, uint s_length) if (sortorder->field) { if (sortorder->field->type() == FIELD_TYPE_BLOB) - sortorder->length=max_item_sort_length; + sortorder->length= thd->variables.max_sort_length; else { sortorder->length=sortorder->field->pack_length(); @@ -913,7 +969,7 @@ sortlength(SORT_FIELD *sortorder, uint s_length) if (sortorder->item->maybe_null) length++; // Place for NULL marker } - set_if_smaller(sortorder->length,max_item_sort_length); + set_if_smaller(sortorder->length, thd->variables.max_sort_length); length+=sortorder->length; } sortorder->field= (Field*) 0; // end marker diff --git a/sql/frm_crypt.cc b/sql/frm_crypt.cc index 629e4ffab95..8dd70900648 100644 --- a/sql/frm_crypt.cc +++ b/sql/frm_crypt.cc @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ diff --git a/sql/gen_lex_hash.cc b/sql/gen_lex_hash.cc index f94d8dddb59..8139cf4fdb0 100644 --- a/sql/gen_lex_hash.cc +++ b/sql/gen_lex_hash.cc @@ -16,19 +16,19 @@ #define NO_YACC_SYMBOLS -#include <global.h> +#include <my_global.h> #include <my_sys.h> #include <m_string.h> #ifndef __GNU_LIBRARY__ -#define __GNU_LIBRARY__ // Skipp warnings in getopt.h +#define __GNU_LIBRARY__ // Skip warnings in getopt.h #endif -#include <getopt.h> +#include <my_getopt.h> #include "mysql_version.h" #include "lex.h" -bool opt_search=0; -uint opt_verbose=0; -ulong opt_count=100000; +my_bool opt_search; +int opt_verbose; +ulong opt_count; #define max_allowed_array 8000 // Don't generate bigger arrays than this #define max_symbol 32767 // Use this for 'not found' @@ -55,6 +55,26 @@ static uchar bits[how_much_and/8+1]; static uint primes[max_allowed_array+1]; static ulong hash_results[type_count][how_much_for_plus+1][total_symbols]; static ulong start_value=0; +static uint best_type; +static ulong best_t1,best_t2, best_start_value; + +static struct my_option my_long_options[] = +{ + {"help", '?', "Display help and exit", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"count", 'c', "Try count times to find a optimal hash table", + (gptr*) &opt_count, (gptr*) &opt_count, 0, GET_ULONG, REQUIRED_ARG, + 100000, 0, 0, 0, 0, 0}, + {"search", 'S', "Search after good rnd1 and rnd2 values", + (gptr*) &opt_search, (gptr*) &opt_search, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, + 0, 0}, + {"verbose", 'v', "Write some information while the program executes", + (gptr*) &opt_verbose, (gptr*) &opt_verbose, 0, GET_INT, NO_ARG, 0, 0, 0, + 0, 0, 0}, + {"version", 'V', "Output version information and exit", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} +}; struct rand_struct { unsigned long seed1,seed2,max_value; @@ -325,86 +345,53 @@ void print_arrays() } -static struct option long_options[] = -{ - {"count", required_argument, 0, 'c'}, - {"search", no_argument, 0, 'S'}, - {"verbose", no_argument, 0, 'v'}, - {"version", no_argument, 0, 'V'}, - {"rnd1", required_argument, 0, 'r'}, - {"rnd2", required_argument, 0, 'R'}, - {"type", required_argument, 0, 't'}, - {0, 0, 0, 0} -}; - - static void usage(int version) { - printf("%s Ver 3.3 Distrib %s, for %s (%s)\n", + printf("%s Ver 3.5 Distrib %s, for %s (%s)\n", my_progname, MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE); if (version) return; puts("Copyright (C) 2001 MySQL AB, by Sinisa and Monty"); puts("This software comes with ABSOLUTELY NO WARRANTY. This is free software,\nand you are welcome to modify and redistribute it under the GPL license\n"); puts("This program generates a perfect hashing function for the sql_lex.cc"); - printf("Usage: %s [OPTIONS]\n", my_progname); - printf("\n\ --c, --count=# Try count times to find a optimal hash table\n\ --r, --rnd1=# Set 1 part of rnd value for hash generator\n\ --R, --rnd2=# Set 2 part of rnd value for hash generator\n\ --t, --type=# Set type of char table to generate\n\ --S, --search Search after good rnd1 and rnd2 values\n\ --v, --verbose Write some information while the program executes\n\ --V, --version Output version information and exit\n"); + printf("Usage: %s [OPTIONS]\n\n", my_progname); + my_print_help(my_long_options); + my_print_variables(my_long_options); +} + +extern "C" my_bool +get_one_option(int optid, const struct my_option *opt __attribute__((unused)), + char *argument __attribute__((unused))) +{ + switch (optid) { + case 'v': + opt_verbose++; + break; + case 'V': + usage(1); + exit(0); + case 'I': + case '?': + usage(0); + exit(0); + } + return 0; } -static uint best_type; -static ulong best_t1,best_t2, best_start_value; static int get_options(int argc, char **argv) { - int c,option_index=0; + int ho_error; + + if ((ho_error=handle_options(&argc, &argv, my_long_options, get_one_option))) + exit(ho_error); - while ((c=getopt_long(argc,argv,"?SvVc:r:R:t:", - long_options, &option_index)) != EOF) - { - switch(c) { - case 'c': - opt_count=atol(optarg); - break; - case 'r': - best_t1=atol(optarg); - break; - case 'R': - best_t2=atol(optarg); - break; - case 't': - best_type=atoi(optarg); - break; - case 'S': - opt_search=1; - break; - case 'v': - opt_verbose++; - break; - case 'V': usage(1); exit(0); - case 'I': - case '?': - usage(0); - exit(0); - default: - fprintf(stderr,"illegal option: -%c\n",opterr); - usage(0); - exit(1); - } - } - argc-=optind; - argv+=optind; if (argc >= 1) { + fprintf(stderr,"%s: Too many arguments\n", my_progname); usage(0); - exit(1); + exit(1); } return(0); } @@ -482,7 +469,7 @@ int main(int argc,char **argv) int error; MY_INIT(argv[0]); - start_value=2744811L; best_t1=5135075L; best_t2=1719450L; best_type=0; /* mode=4999 add=1 type: 0 */ + start_value=2925024L; best_t1=654916L; best_t2=1723390L; best_type=3; /* mode=4943 add=1 type: 0 */ if (get_options(argc,(char **) argv)) exit(1); @@ -502,7 +489,6 @@ int main(int argc,char **argv) printf("start_value=%ldL; best_t1=%ldL; best_t2=%ldL; best_type=%d; /* mode=%d add=%d type: %d */\n", start_value, best_t1,best_t2,best_type,best_mod,best_add, best_functype); - best_start_value=start_value; for (uint i=1 ; i <= opt_count ; i++) { diff --git a/sql/ha_berkeley.cc b/sql/ha_berkeley.cc index f52b99f5a12..2154fbd7a32 100644 --- a/sql/ha_berkeley.cc +++ b/sql/ha_berkeley.cc @@ -358,7 +358,7 @@ berkeley_cmp_packed_key(DB *file, const DBT *new_key, const DBT *saved_key) KEY_PART_INFO *key_part= key->key_part, *end=key_part+key->key_parts; uint key_length=new_key->size; - for ( ; key_part != end && (int) key_length > 0; key_part++) + for (; key_part != end && (int) key_length > 0; key_part++) { int cmp; if (key_part->null_bit) @@ -396,7 +396,7 @@ berkeley_cmp_fix_length_key(DB *file, const DBT *new_key, const DBT *saved_key) KEY_PART_INFO *key_part= key->key_part, *end=key_part+key->key_parts; uint key_length=new_key->size; - for ( ; key_part != end && (int) key_length > 0 ; key_part++) + for (; key_part != end && (int) key_length > 0 ; key_part++) { int cmp; if ((cmp=key_part->field->pack_cmp(new_key_ptr,saved_key_ptr,0))) @@ -417,7 +417,7 @@ berkeley_key_cmp(TABLE *table, KEY *key_info, const char *key, uint key_length) KEY_PART_INFO *key_part= key_info->key_part, *end=key_part+key_info->key_parts; - for ( ; key_part != end && (int) key_length > 0; key_part++) + for (; key_part != end && (int) key_length > 0; key_part++) { int cmp; if (key_part->null_bit) @@ -561,7 +561,7 @@ int ha_berkeley::open(const char *name, int mode, uint test_if_locked) ref_length=0; KEY_PART_INFO *key_part= table->key_info[primary_key].key_part; KEY_PART_INFO *end=key_part+table->key_info[primary_key].key_parts; - for ( ; key_part != end ; key_part++) + for (; key_part != end ; key_part++) ref_length+= key_part->field->max_packed_col_length(key_part->length); share->fixed_length_primary_key= (ref_length == table->key_info[primary_key].key_length); @@ -701,7 +701,7 @@ void ha_berkeley::unpack_key(char *record, DBT *key, uint index) *end=key_part+key_info->key_parts; char *pos=(char*) key->data; - for ( ; key_part != end; key_part++) + for (; key_part != end; key_part++) { if (key_part->null_bit) { @@ -747,7 +747,7 @@ DBT *ha_berkeley::create_key(DBT *key, uint keynr, char *buff, key->data=buff; key->app_private= key_info; - for ( ; key_part != end && key_length > 0; key_part++) + for (; key_part != end && key_length > 0; key_part++) { if (key_part->null_bit) { @@ -925,7 +925,7 @@ int ha_berkeley::key_cmp(uint keynr, const byte * old_row, 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 (; key_part != end ; key_part++) { if (key_part->null_bit) { @@ -1043,9 +1043,9 @@ int ha_berkeley::restore_keys(DB_TXN *trans, key_map changed_keys, break; /* purecov: inspected */ } } - + err: - dbug_assert(error != DB_KEYEXIST); + DBUG_ASSERT(error != DB_KEYEXIST); DBUG_RETURN(error); } @@ -1187,7 +1187,7 @@ int ha_berkeley::remove_key(DB_TXN *trans, uint keynr, const byte *record, ((table->key_info[keynr].flags & (HA_NOSAME | HA_NULL_PART_KEY)) == HA_NOSAME)) { // Unique key - dbug_assert(keynr == primary_key || prim_key->data != key_buff2); + DBUG_ASSERT(keynr == primary_key || prim_key->data != key_buff2); error=key_file[keynr]->del(key_file[keynr], trans, keynr == primary_key ? prim_key : @@ -1201,7 +1201,7 @@ int ha_berkeley::remove_key(DB_TXN *trans, uint keynr, const byte *record, row to find the key to be delete and delete it. We will never come here with keynr = primary_key */ - dbug_assert(keynr != primary_key && prim_key->data != key_buff2); + DBUG_ASSERT(keynr != primary_key && prim_key->data != key_buff2); DBC *tmp_cursor; if (!(error=key_file[keynr]->cursor(key_file[keynr], trans, &tmp_cursor, 0))) @@ -1454,6 +1454,37 @@ int ha_berkeley::index_read(byte * buf, const byte * key, DBUG_RETURN(error); } +/* + Read last key is solved by reading the next key and then reading + the previous key +*/ + +int ha_berkeley::index_read_last(byte * buf, const byte * key, uint key_len) +{ + DBT row; + int error; + KEY *key_info= &table->key_info[active_index]; + DBUG_ENTER("ha_berkeley::index_read"); + + statistic_increment(ha_read_key_count,&LOCK_status); + bzero((char*) &row,sizeof(row)); + + /* read of partial key */ + pack_key(&last_key, active_index, key_buff, key, key_len); + /* Store for compare */ + memcpy(key_buff2, key_buff, (key_len=last_key.size)); + key_info->handler.bdb_return_if_eq= 1; + error=read_row(cursor->c_get(cursor, &last_key, &row, DB_SET_RANGE), + (char*) buf, active_index, &row, (DBT*) 0, 0); + key_info->handler.bdb_return_if_eq= 0; + bzero((char*) &row,sizeof(row)); + if (read_row(cursor->c_get(cursor, &last_key, &row, DB_PREV), + (char*) buf, active_index, &row, &last_key, 1) || + berkeley_key_cmp(table, key_info, key_buff2, key_len)) + error=HA_ERR_KEY_NOT_FOUND; + DBUG_RETURN(error); +} + int ha_berkeley::index_next(byte * buf) { @@ -1553,7 +1584,7 @@ DBT *ha_berkeley::get_pos(DBT *to, byte *pos) KEY_PART_INFO *key_part=table->key_info[primary_key].key_part; KEY_PART_INFO *end=key_part+table->key_info[primary_key].key_parts; - for ( ; key_part != end ; key_part++) + for (; key_part != end ; key_part++) pos+=key_part->field->packed_col_length((char*) pos,key_part->length); to->size= (uint) (pos- (byte*) to->data); } @@ -1565,12 +1596,13 @@ int ha_berkeley::rnd_pos(byte * buf, byte *pos) { DBT db_pos; statistic_increment(ha_read_rnd_count,&LOCK_status); + DBUG_ENTER("ha_berkeley::rnd_pos"); active_index= (uint) -1; // Don't delete via cursor - return read_row(file->get(file, transaction, - get_pos(&db_pos, pos), - ¤t_row, 0), - (char*) buf, primary_key, ¤t_row, (DBT*) 0, 0); + DBUG_RETURN(read_row(file->get(file, transaction, + get_pos(&db_pos, pos), + ¤t_row, 0), + (char*) buf, primary_key, ¤t_row, (DBT*) 0, 0)); } void ha_berkeley::position(const byte *record) @@ -1669,7 +1701,7 @@ int ha_berkeley::external_lock(THD *thd, int lock_type) DBUG_ASSERT(thd->transaction.stmt.bdb_tid == 0); transaction=0; // Safety /* First table lock, start transaction */ - if ((thd->options & (OPTION_NOT_AUTO_COMMIT | OPTION_BEGIN | + if ((thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN | OPTION_TABLE_LOCK)) && !thd->transaction.all.bdb_tid) { @@ -1879,15 +1911,36 @@ int ha_berkeley::delete_table(const char *name) { int error; char name_buff[FN_REFLEN]; + DBUG_ENTER("delete_table"); if ((error=db_create(&file, db_env, 0))) my_errno=error; /* purecov: inspected */ else error=file->remove(file,fn_format(name_buff,name,"",ha_berkeley_ext,2 | 4), NULL,0); file=0; // Safety + DBUG_RETURN(error); +} + +int ha_berkeley::rename_table(const char * from, const char * to) +{ + int error; + char from_buff[FN_REFLEN]; + char to_buff[FN_REFLEN]; + + if ((error= db_create(&file, db_env, 0))) + my_errno= error; + else + { + /* On should not do a file->close() after rename returns */ + error= file->rename(file, + fn_format(from_buff, from, "", ha_berkeley_ext, 2 | 4), + NULL, fn_format(to_buff, to, "", ha_berkeley_ext, + 2 | 4), 0); + } return error; } + /* How many seeks it will take to read through the table This is to be comparable to the number returned by records_in_range so @@ -1896,7 +1949,7 @@ int ha_berkeley::delete_table(const char *name) double ha_berkeley::scan_time() { - return records/3; + return rows2double(records/3); } ha_rows ha_berkeley::records_in_range(int keynr, @@ -2047,7 +2100,7 @@ int ha_berkeley::analyze(THD* thd, HA_CHECK_OPT* check_opt) free(stat); stat=0; } - if (key_file[i]->stat(key_file[i], (void*) &stat, 0, 0)) + if ((key_file[i]->stat)(key_file[i], (void*) &stat, 0, 0)) goto err; /* purecov: inspected */ share->rec_per_key[i]= (stat->bt_ndata / (stat->bt_nkeys ? stat->bt_nkeys : 1)); @@ -2060,7 +2113,7 @@ int ha_berkeley::analyze(THD* thd, HA_CHECK_OPT* check_opt) free(stat); stat=0; } - if (file->stat(file, (void*) &stat, 0, 0)) + if ((file->stat)(file, (void*) &stat, 0, 0)) goto err; /* purecov: inspected */ } pthread_mutex_lock(&share->mutex); @@ -2171,11 +2224,11 @@ static BDB_SHARE *get_share(const char *table_name, TABLE *table) if (!(share=(BDB_SHARE*) hash_search(&bdb_open_tables, (byte*) table_name, length))) { - ha_rows *rec_per_key; + ulong *rec_per_key; char *tmp_name; DB **key_file; u_int32_t *key_type; - + if ((share=(BDB_SHARE *) my_multi_malloc(MYF(MY_WME | MY_ZEROFILL), &share, sizeof(*share), diff --git a/sql/ha_berkeley.h b/sql/ha_berkeley.h index ab1ead5a3e9..d2dc5e3216d 100644 --- a/sql/ha_berkeley.h +++ b/sql/ha_berkeley.h @@ -27,7 +27,8 @@ typedef struct st_berkeley_share { ulonglong auto_ident; - ha_rows rows, org_rows, *rec_per_key; + ha_rows rows, org_rows; + ulong *rec_per_key; THR_LOCK lock; pthread_mutex_t mutex; char *table_name; @@ -52,7 +53,7 @@ class ha_berkeley: public handler u_int32_t *key_type; DBC *cursor; BDB_SHARE *share; - ulong int_option_flag; + ulong int_table_flags; ulong alloced_rec_buff_length; ulong changed_rows; uint primary_key,last_dup_key, hidden_primary_key, version; @@ -86,20 +87,19 @@ class ha_berkeley: public handler public: ha_berkeley(TABLE *table): handler(table), alloc_ptr(0),rec_buff(0), 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_BLOB_KEY | HA_NOT_EXACT_COUNT | HA_NO_FULLTEXT_KEY | - HA_PRIMARY_KEY_IN_READ_INDEX | HA_DROP_BEFORE_CREATE | - HA_AUTO_PART_KEY), + int_table_flags(HA_REC_NOT_IN_SEQ | + HA_KEYPOS_TO_RNDPOS | HA_LASTKEY_ORDER | + HA_NULL_KEY | HA_BLOB_KEY | HA_NOT_EXACT_COUNT | + HA_PRIMARY_KEY_IN_READ_INDEX | HA_DROP_BEFORE_CREATE | + HA_AUTO_PART_KEY | HA_TABLE_SCAN_ON_INDEX), changed_rows(0),last_dup_key((uint) -1),version(0),using_ignore(0) { } ~ha_berkeley() {} const char *table_type() const { return "BerkeleyDB"; } + const char *index_type(uint key_number) { return "BTREE"; } const char **bas_ext() const; - ulong option_flag() const { return int_option_flag; } + ulong table_flags(void) const { return int_table_flags; } uint max_record_length() const { return HA_MAX_REC_LENGTH; } uint max_keys() const { return MAX_KEY-1; } uint max_key_parts() const { return MAX_REF_PARTS; } @@ -122,6 +122,7 @@ class ha_berkeley: public handler uint key_len, enum ha_rkey_function find_flag); int index_read_idx(byte * buf, uint index, const byte * key, uint key_len, enum ha_rkey_function find_flag); + int index_read_last(byte * buf, const byte * key, uint key_len); int index_next(byte * buf); int index_next_same(byte * buf, const byte *key, uint keylen); int index_prev(byte * buf); @@ -151,6 +152,7 @@ class ha_berkeley: public handler int create(const char *name, register TABLE *form, HA_CREATE_INFO *create_info); int delete_table(const char *name); + int rename_table(const char* from, const char* to); THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to, enum thr_lock_type lock_type); @@ -167,7 +169,6 @@ class ha_berkeley: public handler }; extern bool berkeley_skip, berkeley_shared_data; -extern SHOW_COMP_OPTION have_berkeley_db; extern u_int32_t berkeley_init_flags,berkeley_env_flags, berkeley_lock_type, berkeley_lock_types[]; extern ulong berkeley_cache_size, berkeley_max_lock, berkeley_log_buffer_size; diff --git a/sql/ha_gemini.cc b/sql/ha_gemini.cc deleted file mode 100644 index e8130c55fc7..00000000000 --- a/sql/ha_gemini.cc +++ /dev/null @@ -1,3630 +0,0 @@ -/* Copyright (C) 2000 MySQL AB & NuSphere Corporation - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -/* This file is based on ha_berkeley.cc */ - -#ifdef __GNUC__ -#pragma implementation // gcc: Class implementation -#endif - -#include "mysql_priv.h" -#ifdef HAVE_GEMINI_DB -#include "ha_gemini.h" -#include "dbconfig.h" -#include "dsmpub.h" -#include "recpub.h" -#include "vststat.h" - -#include <m_ctype.h> -#include <myisampack.h> -#include <m_string.h> -#include <assert.h> -#include <hash.h> -#include <stdarg.h> -#include "geminikey.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"; - -bool gemini_skip=0; -long gemini_options = 0; -long gemini_buffer_cache; -long gemini_io_threads; -long gemini_log_cluster_size; -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; - -ulong gemini_recovery_options = GEMINI_RECOVERY_FULL; -/* bits in gemini_recovery_options */ -const char *gemini_recovery_names[] = -{ "FULL", "NONE", "FORCE" }; -TYPELIB gemini_recovery_typelib= {array_elements(gemini_recovery_names),"", - 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 = 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, - const char *msg_type, const char *fmt, ...); - -static int gemini_helper_threads(dsmContext_t *pContext); -pthread_handler_decl(gemini_watchdog,arg ); -pthread_handler_decl(gemini_rl_writer,arg ); -pthread_handler_decl(gemini_apw,arg); - -/* General functions */ - -bool gemini_init(void) -{ - dsmStatus_t rc = 0; - char pmsgsfile[MAXPATHN]; - - DBUG_ENTER("gemini_init"); - - gemini_basedir=mysql_home; - /* If datadir isn't set, bail out */ - if (*mysql_real_data_home == '\0') - { - 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 ) - { - 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 ) - { - 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 ) - { - 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 ) - { - gemini_msg(pfirstContext, "ACCESS TAG set failed %l",rc); - goto badret; - } - rc = dsmContextSetLong(pfirstContext,DSM_TAGDB_ACCESS_ENV, DSM_SQL_ENGINE); - if( rc != 0 ) - { - gemini_msg(pfirstContext, "ACCESS_ENV set failed %l",rc); - goto badret; - } - - rc = dsmContextSetString(pfirstContext, DSM_TAGDB_DATADIR, - strlen(mysql_real_data_home), - (TEXT *)mysql_real_data_home); - if( rc != 0 ) - { - gemini_msg(pfirstContext, "Datadir tag failed %l", rc); - goto badret; - } - - rc = dsmContextSetLong(pfirstContext, DSM_TAGDB_MAX_USERS, - gemini_connection_limit); - if(rc != 0) - { - gemini_msg(pfirstContext, "MAX_USERS tag set failed %l",rc); - goto badret; - } - - rc = dsmContextSetLong(pfirstContext, DSM_TAGDB_DEFAULT_LOCK_TIMEOUT, - gemini_lock_wait_timeout); - if(rc != 0) - { - gemini_msg(pfirstContext, "MAX_LOCK_ENTRIES tag set failed %l",rc); - goto badret; - } - - rc = dsmContextSetLong(pfirstContext, DSM_TAGDB_MAX_LOCK_ENTRIES, - gemini_locktablesize); - if(rc != 0) - { - gemini_msg(pfirstContext, "MAX_LOCK_ENTRIES tag set failed %l",rc); - goto badret; - } - - rc = dsmContextSetLong(pfirstContext, DSM_TAGDB_SPIN_AMOUNT, - gemini_spin_retries); - if(rc != 0) - { - gemini_msg(pfirstContext, "SPIN_AMOUNT tag set failed %l",rc); - goto badret; - } - - /* blocksize is hardcoded to 8K. Buffer cache is in bytes - need to convert this to 8K blocks */ - gemini_buffer_cache = gemini_buffer_cache / 8192; - rc = dsmContextSetLong(pfirstContext, DSM_TAGDB_DB_BUFFERS, - gemini_buffer_cache); - if(rc != 0) - { - 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) ? 0 : 1)); - if(rc != 0) - { - 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) - { - gemini_msg(pfirstContext, "DIRECT_IO tag set failed %l",rc); - goto badret; - } - - rc = dsmContextSetLong(pfirstContext, DSM_TAGDB_CRASH_PROTECTION, - ((gemini_recovery_options & GEMINI_RECOVERY_FULL) ? 1 : 0)); - if(rc != 0) - { - 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; - rc = dsmContextSetLong(pfirstContext, DSM_TAGDB_BI_CLUSTER_SIZE, - gemini_log_cluster_size); - - if(rc != 0) - { - gemini_msg(pfirstContext, "CRASH_PROTECTION tag set failed %l",rc); - goto badret; - } - - rc = dsmUserConnect(pfirstContext,(TEXT *)"Multi-user", - DSM_DB_OPENDB | DSM_DB_OPENFILE); - if( rc != 0 ) - { - /* 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: - gemini_skip = 1; - DBUG_RETURN(0); -} - -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"); - - (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) - { - gemini_msg(pContext, "Can't Create gemini watchdog thread"); - goto done; - } - if(!gemini_io_threads) - goto done; - - rc = pthread_create(&hThread, &thr_attr, gemini_rl_writer, (void *)pContext); - if(rc) - { - gemini_msg(pContext, "Can't create Gemini recovery log writer thread"); - goto done; - } - - for(i = gemini_io_threads - 1;i;i--) - { - rc = pthread_create(&hThread, &thr_attr, gemini_apw, (void *)pContext); - if(rc) - { - gemini_msg(pContext, "Can't create Gemini database page writer thread"); - goto done; - } - } -done: - - DBUG_RETURN(rc); -} - -pthread_handler_decl(gemini_watchdog,arg ) -{ - int rc = 0; - dsmContext_t *pcontext = (dsmContext_t *)arg; - dsmContext_t *pmyContext = NULL; - - - rc = dsmContextCopy(pcontext,&pmyContext, DSMCONTEXTDB); - if( rc != 0 ) - { - gemini_msg(pcontext, "dsmContextCopy failed for Gemini watchdog %d",rc); - - return 0; - } - rc = dsmUserConnect(pmyContext,NULL,0); - - if( rc != 0 ) - { - gemini_msg(pcontext, "dsmUserConnect failed for Gemini watchdog %d",rc); - - return 0; - } - - my_thread_init(); - pthread_detach_this_thread(); - - while(rc == 0) - { - rc = dsmDatabaseProcessEvents(pmyContext); - if(!rc) - rc = dsmWatchdog(pmyContext); - sleep(1); - } - rc = dsmUserDisconnect(pmyContext,0); - my_thread_end(); - return 0; -} - -pthread_handler_decl(gemini_rl_writer,arg ) -{ - int rc = 0; - dsmContext_t *pcontext = (dsmContext_t *)arg; - dsmContext_t *pmyContext = NULL; - - - rc = dsmContextCopy(pcontext,&pmyContext, DSMCONTEXTDB); - if( rc != 0 ) - { - gemini_msg(pcontext, "dsmContextCopy failed for Gemini recovery log writer %d",rc); - - return 0; - } - rc = dsmUserConnect(pmyContext,NULL,0); - - if( rc != 0 ) - { - gemini_msg(pcontext, "dsmUserConnect failed for Gemini recovery log writer %d",rc); - - return 0; - } - - my_thread_init(); - pthread_detach_this_thread(); - - while(rc == 0) - { - rc = dsmRLwriter(pmyContext); - } - rc = dsmUserDisconnect(pmyContext,0); - my_thread_end(); - return 0; -} - -pthread_handler_decl(gemini_apw,arg ) -{ - int rc = 0; - dsmContext_t *pcontext = (dsmContext_t *)arg; - dsmContext_t *pmyContext = NULL; - - my_thread_init(); - pthread_detach_this_thread(); - - rc = dsmContextCopy(pcontext,&pmyContext, DSMCONTEXTDB); - if( rc != 0 ) - { - gemini_msg(pcontext, "dsmContextCopy failed for Gemini page writer %d",rc); - my_thread_end(); - return 0; - } - rc = dsmUserConnect(pmyContext,NULL,0); - - if( rc != 0 ) - { - gemini_msg(pcontext, "dsmUserConnect failed for Gemini page writer %d",rc); - my_thread_end(); - return 0; - } - - while(rc == 0) - { - rc = dsmAPW(pmyContext); - } - rc = dsmUserDisconnect(pmyContext,0); - my_thread_end(); - return 0; -} - -int gemini_set_option_long(int optid, long optval) -{ - dsmStatus_t rc = 0; - - switch (optid) - { - case GEM_OPTID_SPIN_RETRIES: - /* If we don't have a context yet, skip the set and just save the - ** value in gemini_spin_retries for a later gemini_init(). This - ** may not ever happen, but we're covered if it does. - */ - if (pfirstContext) - { - rc = dsmContextSetLong(pfirstContext, DSM_TAGDB_SPIN_AMOUNT, - optval); - } - if (rc) - { - gemini_msg(pfirstContext, "SPIN_AMOUNT tag set failed %l",rc); - } - else - { - gemini_spin_retries = optval; - } - break; - } - - return rc; -} - -static int gemini_connect(THD *thd) -{ - DBUG_ENTER("gemini_connect"); - - dsmStatus_t rc; - - rc = dsmContextCopy(pfirstContext,(dsmContext_t **)&thd->gemini.context, - DSMCONTEXTDB); - if( rc != 0 ) - { - gemini_msg(pfirstContext, "dsmContextCopy failed %l",rc); - - return(rc); - } - rc = dsmUserConnect((dsmContext_t *)thd->gemini.context,NULL,0); - - if( rc != 0 ) - { - gemini_msg(pfirstContext, "dsmUserConnect failed %l",rc); - - return(rc); - } - rc = (dsmStatus_t)gemini_tx_begin(thd); - - DBUG_RETURN(rc); -} - -void gemini_disconnect(THD *thd) -{ - dsmStatus_t rc; - - if(thd->gemini.context) - { - rc = dsmUserDisconnect((dsmContext_t *)thd->gemini.context,0); - } - return; -} - -bool gemini_end(void) -{ - dsmStatus_t rc; - THD *thd; - - DBUG_ENTER("gemini_end"); - - hash_free(&gem_open_tables); - pthread_mutex_destroy(&gem_mutex); - if(pfirstContext) - { - rc = dsmShutdownSet(pfirstContext, DSM_SHUTDOWN_NORMAL); - sleep(2); - rc = dsmContextSetLong(pfirstContext,DSM_TAGDB_ACCESS_TYPE,DSM_ACCESS_STARTUP); - rc = dsmShutdown(pfirstContext, DSMNICEBIT,DSMNICEBIT); - } - DBUG_RETURN(0); -} - -bool gemini_flush_logs() -{ - DBUG_ENTER("gemini_flush_logs"); - - DBUG_RETURN(0); -} - -static int gemini_tx_begin(THD *thd) -{ - dsmStatus_t rc; - DBUG_ENTER("gemini_tx_begin"); - - thd->gemini.savepoint = 1; - - rc = dsmTransaction((dsmContext_t *)thd->gemini.context, - &thd->gemini.savepoint,DSMTXN_START,0,NULL); - if(!rc) - thd->gemini.needSavepoint = 1; - - thd->gemini.tx_isolation = thd->tx_isolation; - - DBUG_PRINT("trans",("beginning transaction")); - DBUG_RETURN(rc); -} - -int gemini_commit(THD *thd) -{ - dsmStatus_t rc; - LONG txNumber = 0; - - DBUG_ENTER("gemini_commit"); - - if(!thd->gemini.context) - DBUG_RETURN(0); - - rc = dsmTransaction((dsmContext_t *)thd->gemini.context, - 0,DSMTXN_COMMIT,0,NULL); - if(!rc) - rc = gemini_tx_begin(thd); - - thd->gemini.lock_count = 0; - - DBUG_PRINT("trans",("ending transaction")); - DBUG_RETURN(rc); -} - -int gemini_rollback(THD *thd) -{ - dsmStatus_t rc; - LONG txNumber; - - DBUG_ENTER("gemini_rollback"); - DBUG_PRINT("trans",("aborting transaction")); - - if(!thd->gemini.context) - DBUG_RETURN(0); - - thd->gemini.savepoint = 0; - rc = dsmTransaction((dsmContext_t *)thd->gemini.context, - &thd->gemini.savepoint,DSMTXN_ABORT,0,NULL); - if(!rc) - rc = gemini_tx_begin(thd); - - thd->gemini.lock_count = 0; - - DBUG_RETURN(rc); -} - -int gemini_rollback_to_savepoint(THD *thd) -{ - dsmStatus_t rc = 0; - DBUG_ENTER("gemini_rollback_to_savepoint"); - if(thd->gemini.savepoint > 1) - { - rc = dsmTransaction((dsmContext_t *)thd->gemini.context, - &thd->gemini.savepoint,DSMTXN_UNSAVE,0,NULL); - } - 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 ) -{ - switch (mysqlType) - { - case FIELD_TYPE_LONG: - case FIELD_TYPE_TINY: - case FIELD_TYPE_SHORT: - case FIELD_TYPE_TIMESTAMP: - case FIELD_TYPE_LONGLONG: - case FIELD_TYPE_INT24: - case FIELD_TYPE_DATE: - case FIELD_TYPE_TIME: - case FIELD_TYPE_DATETIME: - case FIELD_TYPE_YEAR: - case FIELD_TYPE_NEWDATE: - case FIELD_TYPE_ENUM: - case FIELD_TYPE_SET: - return GEM_INT; - case FIELD_TYPE_DECIMAL: - return GEM_DECIMAL; - case FIELD_TYPE_FLOAT: - return GEM_FLOAT; - case FIELD_TYPE_DOUBLE: - return GEM_DOUBLE; - case FIELD_TYPE_TINY_BLOB: - return GEM_TINYBLOB; - case FIELD_TYPE_MEDIUM_BLOB: - return GEM_MEDIUMBLOB; - case FIELD_TYPE_LONG_BLOB: - return GEM_LONGBLOB; - case FIELD_TYPE_BLOB: - return GEM_BLOB; - case FIELD_TYPE_VAR_STRING: - case FIELD_TYPE_STRING: - return GEM_CHAR; - } - return -1; -} - -/***************************************************************************** -** Gemini tables -*****************************************************************************/ - -const char **ha_gemini::bas_ext() const -{ static const char *ext[]= { ha_gemini_ext, ha_gemini_idx_ext, NullS }; - return ext; -} - - -int ha_gemini::open(const char *name, int mode, uint test_if_locked) -{ - dsmObject_t tableId = 0; - THD *thd; - char name_buff[FN_REFLEN]; - char tabname_buff[FN_REFLEN]; - char dbname_buff[FN_REFLEN]; - unsigned i,nameLen; - LONG txNumber; - dsmStatus_t rc; - - DBUG_ENTER("ha_gemini::open"); - - thd = current_thd; - /* 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) - { - /* Need to get this thread a connection into the database */ - rc = gemini_connect(thd); - if(rc) - return rc; - } - if (!(rec_buff=(byte*)my_malloc(table->rec_buff_length, - MYF(MY_WME)))) - { - DBUG_RETURN(1); - } - - /* separate out the name of the table and the database (a VST must be - ** created in the mysql database) - */ - rc = gemini_parse_table_name(name, dbname_buff, tabname_buff); - if (rc == 0) - { - if (strcmp(dbname_buff, "mysql") == 0) - { - tableId = gemini_is_vst(tabname_buff); - } - } - sprintf(name_buff, "%s.%s", dbname_buff, tabname_buff); - - /* if it's not a VST, get the table number the regular way */ - if (!tableId) - { - 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; - - if(!rc) - rc = index_open(name_buff); - - fixed_length_row=!(table->db_create_options & HA_OPTION_PACK_RECORD); - key_read = 0; - using_ignore = 0; - - /* Get the gemini table status -- we want to know if the table - crashed while being in the midst of a repair operation */ - rc = dsmTableStatus((dsmContext_t *)thd->gemini.context, - tableNumber,&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); -} - -/* Look up and store the object numbers for the indexes on this table */ -int ha_gemini::index_open(char *tableName) -{ - dsmStatus_t rc = 0; - int nameLen; - - DBUG_ENTER("ha_gemini::index_open"); - if(table->keys) - { - THD *thd = current_thd; - dsmObject_t objectNumber; - if (!(pindexNumbers=(dsmIndex_t *)my_malloc(table->keys*sizeof(dsmIndex_t), - MYF(MY_WME)))) - { - DBUG_RETURN(1); - } - nameLen = strlen(tableName); - tableName[nameLen] = '.'; - nameLen++; - - for( uint i = 0; i < table->keys && !rc; i++) - { - strcpy(&tableName[nameLen],table->key_info[i].name); - 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; - } - } - else - pindexNumbers = 0; - - DBUG_RETURN(rc); -} - -int ha_gemini::close(void) -{ - DBUG_ENTER("ha_gemini::close"); - my_free((char*)rec_buff,MYF(MY_ALLOW_ZERO_PTR)); - rec_buff = 0; - my_free((char *)pindexNumbers,MYF(MY_ALLOW_ZERO_PTR)); - pindexNumbers = 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)); -} - - -int ha_gemini::write_row(byte * record) -{ - int error = 0; - dsmRecord_t dsmRecord; - THD *thd; - - DBUG_ENTER("write_row"); - - if(tableStatus == HA_ERR_CRASHED) - DBUG_RETURN(tableStatus); - - thd = current_thd; - - statistic_increment(ha_write_count,&LOCK_status); - if (table->time_stamp) - update_timestamp(record+table->time_stamp-1); - - if(thd->gemini.needSavepoint || using_ignore) - { - thd->gemini.savepoint++; - error = dsmTransaction((dsmContext_t *)thd->gemini.context, - &thd->gemini.savepoint, - DSMTXN_SAVE, 0, 0); - if (error) - DBUG_RETURN(error); - thd->gemini.needSavepoint = 0; - } - - if (table->next_number_field && record == table->record[0]) - { - if(thd->next_insert_id) - { - ULONG64 nr; - /* 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,1); - if(thd->next_insert_id > nr) - { - error = dsmTableAutoIncrementSet((dsmContext_t *)thd->gemini.context, - tableNumber, - (ULONG64)thd->next_insert_id); - } - } - - update_auto_increment(); - } - - dsmRecord.table = tableNumber; - dsmRecord.maxLength = table->rec_buff_length; - - if ((error=pack_row((byte **)&dsmRecord.pbuffer, (int *)&dsmRecord.recLength, - record, FALSE))) - { - DBUG_RETURN(error); - } - - error = dsmRecordCreate((dsmContext_t *)thd->gemini.context, - &dsmRecord,0); - - if(!error) - { - error = handleIndexEntries(record, dsmRecord.recid,KEY_CREATE); - if(error == HA_ERR_FOUND_DUPP_KEY && using_ignore) - { - dsmStatus_t rc; - rc = dsmTransaction((dsmContext_t *)thd->gemini.context, - &thd->gemini.savepoint,DSMTXN_UNSAVE,0,NULL); - thd->gemini.needSavepoint = 1; - } - } - if(error == DSM_S_RQSTREJ) - error = HA_ERR_LOCK_WAIT_TIMEOUT; - - DBUG_RETURN(error); -} - -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, - update); - return nr; -} - -/* Put or delete index entries for a row */ -int ha_gemini::handleIndexEntries(const byte * record, dsmRecid_t recid, - enum_key_string_options option) -{ - dsmStatus_t rc = 0; - - DBUG_ENTER("handleIndexEntries"); - - for (uint i = 0; i < table->keys && rc == 0; i++) - { - rc = handleIndexEntry(record, recid,option, i); - } - DBUG_RETURN(rc); -} - -int ha_gemini::handleIndexEntry(const byte * record, dsmRecid_t recid, - enum_key_string_options option,uint keynr) -{ - dsmStatus_t rc = 0; - KEY *key_info; - int keyStringLen; - bool thereIsAnull; - THD *thd; - - AUTOKEY(theKey,keyBufSize); - - DBUG_ENTER("handleIndexEntry"); - - thd = current_thd; - key_info=table->key_info+keynr; - thereIsAnull = FALSE; - rc = createKeyString(record, key_info, theKey.akey.keystr, - sizeof(theKey.apad),&keyStringLen, - (short)pindexNumbers[keynr], - &thereIsAnull); - if(!rc) - { - theKey.akey.index = pindexNumbers[keynr]; - theKey.akey.keycomps = (COUNT)key_info->key_parts; - - /* 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 */ - 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) - { - rc = dsmKeyCreate((dsmContext_t *)thd->gemini.context, &theKey.akey, - (dsmTable_t)tableNumber, recid, NULL); - if(rc == DSM_S_IXDUPKEY) - { - last_dup_key=keynr; - rc = HA_ERR_FOUND_DUPP_KEY; - } - } - else if(option == KEY_DELETE) - { - rc = dsmKeyDelete((dsmContext_t *)thd->gemini.context, &theKey.akey, - (dsmTable_t)tableNumber, recid, 0, NULL); - } - else - { - /* KEY_CHECK */ - dsmCursid_t aCursorId; - int error; - - rc = dsmCursorCreate((dsmContext_t *)thd->gemini.context, - (dsmTable_t)tableNumber, - (dsmIndex_t)pindexNumbers[keynr], - &aCursorId,NULL); - - rc = dsmCursorFind((dsmContext_t *)thd->gemini.context, - &aCursorId,&theKey.akey,NULL,DSMDBKEY, - DSMFINDFIRST,DSM_LK_SHARE,0, - &lastRowid,0); - error = dsmCursorDelete((dsmContext_t *)thd->gemini.context, - &aCursorId, 0); - - } - } - DBUG_RETURN(rc); -} - -int ha_gemini::createKeyString(const byte * record, KEY *pkeyinfo, - unsigned char *pkeyBuf, int bufSize, - int *pkeyStringLen, - short geminiIndexNumber, - bool *thereIsAnull) -{ - dsmStatus_t rc = 0; - int componentLen; - int fieldType; - int isNull; - uint key_part_length; - - KEY_PART_INFO *key_part; - - DBUG_ENTER("createKeyString"); - - rc = gemKeyInit(pkeyBuf,pkeyStringLen, geminiIndexNumber); - - for(uint i = 0; i < pkeyinfo->key_parts && rc == 0; i++) - { - unsigned char *pos; - - key_part = pkeyinfo->key_part + i; - key_part_length = key_part->length; - fieldType = gemDataType(key_part->field->type()); - 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((char*)rec_buff, key_part->length); - key_part->field->ptr = ptr; - pos = (unsigned char *)rec_buff; - } - 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; - if(isNull) - *thereIsAnull = TRUE; - - rc = gemFieldToIdxComponent(pos, - (unsigned long) key_part_length, - fieldType, - isNull , - key_part->field->flags & UNSIGNED_FLAG, - pkeyBuf + *pkeyStringLen, - bufSize, - &componentLen); - *pkeyStringLen += componentLen; - } - DBUG_RETURN(rc); -} - - -int ha_gemini::update_row(const byte * old_record, byte * new_record) -{ - int error = 0; - dsmRecord_t dsmRecord; - unsigned long savepoint; - THD *thd = current_thd; - DBUG_ENTER("update_row"); - - statistic_increment(ha_update_count,&LOCK_status); - if (table->time_stamp) - update_timestamp(new_record+table->time_stamp-1); - - if(thd->gemini.needSavepoint || using_ignore) - { - thd->gemini.savepoint++; - error = dsmTransaction((dsmContext_t *)thd->gemini.context, - &thd->gemini.savepoint, - DSMTXN_SAVE, 0, 0); - if (error) - DBUG_RETURN(error); - thd->gemini.needSavepoint = 0; - } - for (uint keynr=0 ; keynr < table->keys ; keynr++) - { - if(key_cmp(keynr,old_record, new_record,FALSE)) - { - error = handleIndexEntry(old_record,lastRowid,KEY_DELETE,keynr); - if(error) - DBUG_RETURN(error); - error = handleIndexEntry(new_record, lastRowid, KEY_CREATE, keynr); - if(error) - { - if (using_ignore && error == HA_ERR_FOUND_DUPP_KEY) - { - dsmStatus_t rc; - rc = dsmTransaction((dsmContext_t *)thd->gemini.context, - &thd->gemini.savepoint,DSMTXN_UNSAVE,0,NULL); - thd->gemini.needSavepoint = 1; - } - DBUG_RETURN(error); - } - } - } - - dsmRecord.table = tableNumber; - dsmRecord.recid = lastRowid; - dsmRecord.maxLength = table->rec_buff_length; - - if ((error=pack_row((byte **)&dsmRecord.pbuffer, (int *)&dsmRecord.recLength, - new_record, TRUE))) - { - DBUG_RETURN(error); - } - error = dsmRecordUpdate((dsmContext_t *)thd->gemini.context, - &dsmRecord, 0, NULL); - - DBUG_RETURN(error); -} - - -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); - - if(thd->gemini.needSavepoint) - { - thd->gemini.savepoint++; - error = dsmTransaction(pcontext, &thd->gemini.savepoint, DSMTXN_SAVE, 0, 0); - if (error) - DBUG_RETURN(error); - thd->gemini.needSavepoint = 0; - } - - dsmRecord.table = tableNumber; - dsmRecord.recid = lastRowid; - - error = handleIndexEntries(record, dsmRecord.recid,KEY_DELETE); - if(!error) - { - 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); -} - -int ha_gemini::index_init(uint keynr) -{ - int error = 0; - THD *thd; - DBUG_ENTER("index_init"); - thd = current_thd; - - lastRowid = 0; - active_index=keynr; - error = dsmCursorCreate((dsmContext_t *)thd->gemini.context, - (dsmTable_t)tableNumber, - (dsmIndex_t)pindexNumbers[keynr], - &cursorId,NULL); - pbracketBase = (dsmKey_t *)my_malloc(sizeof(dsmKey_t) + keyBufSize, - MYF(MY_WME)); - if(!pbracketBase) - DBUG_RETURN(1); - pbracketLimit = (dsmKey_t *)my_malloc(sizeof(dsmKey_t) + keyBufSize,MYF(MY_WME)); - if(!pbracketLimit) - { - my_free((char *)pbracketLimit,MYF(0)); - DBUG_RETURN(1); - } - pbracketBase->index = 0; - pbracketLimit->index = (dsmIndex_t)pindexNumbers[keynr]; - 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) - { - my_free((char *)pbracketLimit,MYF(0)); - my_free((char *)pbracketBase,MYF(0)); - DBUG_RETURN(1); - } - - DBUG_RETURN(error); -} - -int ha_gemini::index_end() -{ - int error = 0; - THD *thd; - DBUG_ENTER("index_end"); - thd = current_thd; - error = dsmCursorDelete((dsmContext_t *)thd->gemini.context, - &cursorId, 0); - if(pbracketLimit) - my_free((char *)pbracketLimit,MYF(0)); - if(pbracketBase) - my_free((char *)pbracketBase,MYF(0)); - if(pfoundKey) - my_free((char *)pfoundKey,MYF(0)); - - pbracketLimit = 0; - pbracketBase = 0; - pfoundKey = 0; - DBUG_RETURN(error); -} - -/* This is only used to read whole keys */ - -int ha_gemini::index_read_idx(byte * buf, uint keynr, const byte * key, - uint key_len, enum ha_rkey_function find_flag) -{ - int error = 0; - DBUG_ENTER("index_read_idx"); - statistic_increment(ha_read_key_count,&LOCK_status); - - error = index_init(keynr); - if (!error) - error = index_read(buf,key,key_len,find_flag); - - if(error == HA_ERR_END_OF_FILE) - error = HA_ERR_KEY_NOT_FOUND; - - table->status = error ? STATUS_NOT_FOUND : 0; - DBUG_RETURN(error); -} - -int ha_gemini::pack_key( uint keynr, dsmKey_t *pkey, - const byte *key_ptr, uint key_length) -{ - KEY *key_info=table->key_info+keynr; - KEY_PART_INFO *key_part=key_info->key_part; - KEY_PART_INFO *end=key_part+key_info->key_parts; - int rc; - int componentLen; - DBUG_ENTER("pack_key"); - - rc = gemKeyInit(pkey->keystr,&componentLen, - (short)pindexNumbers[active_index]); - pkey->keyLen = componentLen; - - for (; key_part != end && (int) key_length > 0 && !rc; key_part++) - { - uint offset=0; - unsigned char *pos; - uint key_part_length = key_part->length; - - int fieldType; - if (key_part->null_bit) - { - offset=1; - if (*key_ptr != 0) // Store 0 if NULL - { - key_length-= key_part->store_length; - key_ptr+= key_part->store_length; - rc = gemFieldToIdxComponent( - (unsigned char *)key_ptr + offset, - (unsigned long) key_part_length, - 0, - 1 , /* Tells it to build a null component */ - key_part->field->flags & UNSIGNED_FLAG, - pkey->keystr + pkey->keyLen, - keyBufSize, - &componentLen); - pkey->keyLen += componentLen; - continue; - } - } - fieldType = gemDataType(key_part->field->type()); - switch (fieldType) - { - 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; - 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, - fieldType, - 0 , - key_part->field->flags & UNSIGNED_FLAG, - pkey->keystr + pkey->keyLen, - keyBufSize, - &componentLen); - - key_ptr+=key_part->store_length; - key_length-=key_part->store_length; - pkey->keyLen += componentLen; - } - DBUG_RETURN(rc); -} - -void ha_gemini::unpack_key(char *record, dsmKey_t *key, uint index) -{ - KEY *key_info=table->key_info+index; - KEY_PART_INFO *key_part= key_info->key_part, - *end=key_part+key_info->key_parts; - int fieldIsNull, fieldType; - int rc = 0; - - char unsigned *pos= &key->keystr[FULLKEYHDRSZ+4/* 4 for the index number*/]; - - for ( ; key_part != end; key_part++) - { - fieldType = gemDataType(key_part->field->type()); - if(fieldType == GEM_CHAR) - { - /* Can't get data from character indexes since the sort weights - are in the index and not the characters. */ - key_read = 0; - } - rc = gemIdxComponentToField(pos, fieldType, - (unsigned char *)record + key_part->field->offset(), - //key_part->field->field_length, - key_part->length, - key_part->field->decimals(), - &fieldIsNull); - if(fieldIsNull) - { - record[key_part->null_offset] |= key_part->null_bit; - } - else if (key_part->null_bit) - { - record[key_part->null_offset]&= ~key_part->null_bit; - } - while(*pos++); /* Advance to next field in key by finding */ - /* a null byte */ - } -} - -int ha_gemini::index_read(byte * buf, const byte * key, - uint key_len, enum ha_rkey_function find_flag) -{ - int error = 0; - THD *thd; - int componentLen; - - DBUG_ENTER("index_read"); - statistic_increment(ha_read_key_count,&LOCK_status); - - - pbracketBase->index = (short)pindexNumbers[active_index]; - pbracketBase->keycomps = 1; - - - /* Its a greater than operation so create a base bracket - from the input key data. */ - error = pack_key(active_index, pbracketBase, key, key_len); - if(error) - goto errorReturn; - - if(find_flag == HA_READ_AFTER_KEY) - { - /* A greater than operation */ - error = gemKeyAddLow(pbracketBase->keystr + pbracketBase->keyLen, - &componentLen); - pbracketBase->keyLen += componentLen; - } - if(find_flag == HA_READ_KEY_EXACT) - { - /* Need to set up a high bracket for an equality operator - Which is a copy of the base bracket plus a hi lim term */ - bmove(pbracketLimit,pbracketBase,(size_t)pbracketBase->keyLen + sizeof(dsmKey_t)); - error = gemKeyAddHigh(pbracketLimit->keystr + pbracketLimit->keyLen, - &componentLen); - if(error) - goto errorReturn; - pbracketLimit->keyLen += componentLen; - } - else - { - /* Always add a high range -- except for HA_READ_KEY_EXACT this - is all we need for the upper index bracket */ - error = gemKeyHigh(pbracketLimit->keystr, &componentLen, - pbracketLimit->index); - - pbracketLimit->keyLen = componentLen; - } - /* 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 -= FULLKEYHDRSZ; - pbracketLimit->keyLen -= FULLKEYHDRSZ; - - thd = current_thd; - - error = findRow(thd, DSMFINDFIRST, buf); - -errorReturn: - if (error == DSM_S_ENDLOOP) - error = HA_ERR_KEY_NOT_FOUND; - - table->status = error ? STATUS_NOT_FOUND : 0; - DBUG_RETURN(error); -} - - -int ha_gemini::index_next(byte * buf) -{ - THD *thd; - int error = 1; - int keyStringLen=0; - dsmMask_t findMode; - DBUG_ENTER("index_next"); - - if(tableStatus == HA_ERR_CRASHED) - DBUG_RETURN(tableStatus); - - thd = current_thd; - - if(pbracketBase->index == 0) - { - error = gemKeyLow(pbracketBase->keystr, &keyStringLen, - pbracketLimit->index); - - pbracketBase->keyLen = (COUNT)keyStringLen - FULLKEYHDRSZ; - pbracketBase->index = pbracketLimit->index; - error = gemKeyHigh(pbracketLimit->keystr, &keyStringLen, - pbracketLimit->index); - pbracketLimit->keyLen = (COUNT)keyStringLen - FULLKEYHDRSZ; - - findMode = DSMFINDFIRST; - } - else - findMode = DSMFINDNEXT; - - error = findRow(thd,findMode,buf); - - if (error == DSM_S_ENDLOOP) - error = HA_ERR_END_OF_FILE; - - table->status = error ? STATUS_NOT_FOUND : 0; - DBUG_RETURN(error); -} - -int ha_gemini::index_next_same(byte * buf, const byte *key, uint keylen) -{ - int error = 0; - DBUG_ENTER("index_next_same"); - statistic_increment(ha_read_next_count,&LOCK_status); - DBUG_RETURN(index_next(buf)); -} - - -int ha_gemini::index_prev(byte * buf) -{ - int error = 0; - THD *thd = current_thd; - - DBUG_ENTER("index_prev"); - statistic_increment(ha_read_prev_count,&LOCK_status); - - error = findRow(thd, DSMFINDPREV, buf); - - if (error == DSM_S_ENDLOOP) - error = HA_ERR_END_OF_FILE; - - - table->status = error ? STATUS_NOT_FOUND : 0; - DBUG_RETURN(error); -} - - -int ha_gemini::index_first(byte * buf) -{ - DBUG_ENTER("index_first"); - statistic_increment(ha_read_first_count,&LOCK_status); - DBUG_RETURN(index_next(buf)); -} - -int ha_gemini::index_last(byte * buf) -{ - int error = 0; - THD *thd; - int keyStringLen; - dsmMask_t findMode; - thd = current_thd; - - DBUG_ENTER("index_last"); - statistic_increment(ha_read_last_count,&LOCK_status); - - error = gemKeyLow(pbracketBase->keystr, &keyStringLen, - pbracketLimit->index); - - pbracketBase->keyLen = (COUNT)keyStringLen - FULLKEYHDRSZ; - pbracketBase->index = pbracketLimit->index; - error = gemKeyHigh(pbracketLimit->keystr, &keyStringLen, - pbracketLimit->index); - pbracketLimit->keyLen = (COUNT)keyStringLen - FULLKEYHDRSZ; - - error = findRow(thd,DSMFINDLAST,buf); - - if (error == DSM_S_ENDLOOP) - error = HA_ERR_END_OF_FILE; - - table->status = error ? STATUS_NOT_FOUND : 0; - DBUG_RETURN(error); -} - -int ha_gemini::rnd_init(bool scan) -{ - THD *thd = current_thd; - - lastRowid = 0; - - return 0; -} - -int ha_gemini::rnd_end() -{ -/* - return gem_scan_end(); -*/ - return 0; -} - -int ha_gemini::rnd_next(byte *buf) -{ - int error = 0; - dsmRecord_t dsmRecord; - THD *thd; - - DBUG_ENTER("rnd_next"); - - if(tableStatus == HA_ERR_CRASHED) - DBUG_RETURN(tableStatus); - - thd = current_thd; - if(thd->gemini.tx_isolation == ISO_READ_COMMITTED && !(lockMode & DSM_LK_EXCL) - && lastRowid) - error = dsmObjectUnlock((dsmContext_t *)thd->gemini.context, - tableNumber, DSMOBJECT_RECORD, lastRowid, - lockMode | DSM_UNLK_FREE, 0); - - statistic_increment(ha_read_rnd_next_count,&LOCK_status); - dsmRecord.table = tableNumber; - dsmRecord.recid = lastRowid; - dsmRecord.pbuffer = (dsmBuffer_t *)rec_buff; - dsmRecord.recLength = table->reclength; - dsmRecord.maxLength = table->rec_buff_length; - - error = dsmTableScan((dsmContext_t *)thd->gemini.context, - &dsmRecord, DSMFINDNEXT, lockMode, 0); - - if(!error) - { - lastRowid = dsmRecord.recid; - error = unpack_row((char *)buf,(char *)dsmRecord.pbuffer); - } - if(!error) - ; - 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); -} - - -int ha_gemini::rnd_pos(byte * buf, byte *pos) -{ - int error; - int rc; - - THD *thd; - - statistic_increment(ha_read_rnd_count,&LOCK_status); - thd = current_thd; - memcpy((void *)&lastRowid,pos,ref_length); - if(thd->gemini.tx_isolation == ISO_READ_COMMITTED && !(lockMode & DSM_LK_EXCL)) - { - /* Lock the row */ - - error = dsmObjectLock((dsmContext_t *)thd->gemini.context, - (dsmObject_t)tableNumber,DSMOBJECT_RECORD,lastRowid, - lockMode, 1, 0); - if ( error ) - goto errorReturn; - } - error = fetch_row(thd->gemini.context, buf); - if(thd->gemini.tx_isolation == ISO_READ_COMMITTED && !(lockMode & DSM_LK_EXCL)) - { - /* Unlock the row */ - - rc = dsmObjectUnlock((dsmContext_t *)thd->gemini.context, - (dsmObject_t)tableNumber,DSMOBJECT_RECORD,lastRowid, - lockMode | DSM_UNLK_FREE , 0); - } - if(error == DSM_S_RMNOTFND) - error = HA_ERR_RECORD_DELETED; - - errorReturn: - table->status = error ? STATUS_NOT_FOUND : 0; - return error; -} - -int ha_gemini::fetch_row(void *gemini_context,const byte *buf) -{ - dsmStatus_t rc = 0; - dsmRecord_t dsmRecord; - - DBUG_ENTER("fetch_row"); - dsmRecord.table = tableNumber; - dsmRecord.recid = lastRowid; - dsmRecord.pbuffer = (dsmBuffer_t *)rec_buff; - dsmRecord.recLength = table->reclength; - dsmRecord.maxLength = table->rec_buff_length; - - rc = dsmRecordGet((dsmContext_t *)gemini_context, - &dsmRecord, 0); - - if(!rc) - { - rc = unpack_row((char *)buf,(char *)dsmRecord.pbuffer); - } - - DBUG_RETURN(rc); -} -int ha_gemini::findRow(THD *thd, dsmMask_t findMode, byte *buf) -{ - dsmStatus_t rc; - dsmKey_t *pkey; - - DBUG_ENTER("findRow"); - - if(thd->gemini.tx_isolation == ISO_READ_COMMITTED && !(lockMode & DSM_LK_EXCL) - && lastRowid) - rc = dsmObjectUnlock((dsmContext_t *)thd->gemini.context, - tableNumber, DSMOBJECT_RECORD, lastRowid, - lockMode | DSM_UNLK_FREE, 0); - if( key_read ) - pkey = pfoundKey; - else - pkey = 0; - - rc = dsmCursorFind((dsmContext_t *)thd->gemini.context, - &cursorId, - pbracketBase, - pbracketLimit, - DSMPARTIAL, - findMode, - lockMode, - NULL, - &lastRowid, - pkey); - if( rc ) - goto errorReturn; - - if(key_read) - { - unpack_key((char*)buf, pkey, active_index); - } - if(!key_read) /* unpack_key may have turned off key_read */ - { - rc = fetch_row((dsmContext_t *)thd->gemini.context,buf); - } - -errorReturn: - if(!rc) - ; - 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); -} - -void ha_gemini::position(const byte *record) -{ - memcpy(ref,&lastRowid,ref_length); -} - - -void ha_gemini::info(uint flag) -{ - DBUG_ENTER("info"); - - if ((flag & HA_STATUS_VARIABLE)) - { - THD *thd = current_thd; - 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; - } - 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; - } - if ((flag & HA_STATUS_ERRKEY)) - { - errkey=last_dup_key; - } - if ((flag & HA_STATUS_TIME)) - { - ; - } - 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; -} - - -int ha_gemini::extra(enum ha_extra_function operation) -{ - switch (operation) - { - case HA_EXTRA_RESET: - case HA_EXTRA_RESET_STATE: - key_read=0; - using_ignore=0; - break; - case HA_EXTRA_KEYREAD: - key_read=1; // Query satisfied with key - break; - case HA_EXTRA_NO_KEYREAD: - key_read=0; - break; - case HA_EXTRA_IGNORE_DUP_KEY: - using_ignore=1; - break; - case HA_EXTRA_NO_IGNORE_DUP_KEY: - using_ignore=0; - break; - - default: - break; - } - return 0; -} - - -int ha_gemini::reset(void) -{ - key_read=0; // Reset to state after open - return 0; -} - - -/* - As MySQL will execute an external lock for every new table it uses - we can use this to start the transactions. -*/ - -int ha_gemini::external_lock(THD *thd, int lock_type) -{ - dsmStatus_t rc = 0; - LONG txNumber; - - DBUG_ENTER("ha_gemini::external_lock"); - - if (lock_type != F_UNLCK) - { - if (!thd->gemini.lock_count) - { - 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 */ - rc = gemini_connect(thd); - if(rc) - return rc; - } - /* Set need savepoint flag */ - thd->gemini.needSavepoint = 1; - - if(rc) - DBUG_RETURN(rc); - - - if( thd->in_lock_tables || thd->gemini.tx_isolation == ISO_SERIALIZABLE ) - { - 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 */ - { - /* Commit the tx if we're in auto-commit mode */ - if (!(thd->options & OPTION_NOT_AUTO_COMMIT)&& - !(thd->options & OPTION_BEGIN)) - gemini_commit(thd); - } - - DBUG_RETURN(rc); -} - - -THR_LOCK_DATA **ha_gemini::store_lock(THD *thd, THR_LOCK_DATA **to, - enum thr_lock_type lock_type) -{ - if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK) - { - /* If we are not doing a LOCK TABLE, then allow multiple writers */ - if ((lock_type >= TL_WRITE_CONCURRENT_INSERT && - lock_type <= TL_WRITE) && - !thd->in_lock_tables) - lock_type = TL_WRITE_ALLOW_WRITE; - lock.type=lock_type; - } - 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) -{ - THD *thd; - char name_buff[FN_REFLEN]; - char dbname_buff[FN_REFLEN]; - DBUG_ENTER("ha_gemini::create"); - dsmContext_t *pcontext; - dsmStatus_t rc; - dsmArea_t areaNumber; - dsmObject_t tableNumber = 0; - dsmDbkey_t dummy = 0; - unsigned i; - int baseNameLen; - dsmObject_t indexNumber; - - /* separate out the name of the table and the database (a VST must be - ** created in the mysql database) - */ - rc = gemini_parse_table_name(name, dbname_buff, name_buff); - if (rc == 0) - { - /* If the table is a VST, don't create areas or extents */ - if (strcmp(dbname_buff, "mysql") == 0) - { - tableNumber = gemini_is_vst(name_buff); - if (tableNumber) - { - return 0; - } - } - } - - thd = current_thd; - if(thd->gemini.context == NULL) - { - /* Need to get this thread a connection into the database */ - rc = gemini_connect(thd); - if(rc) - return rc; - } - pcontext = (dsmContext_t *)thd->gemini.context; - - if(thd->gemini.needSavepoint || using_ignore) - { - thd->gemini.savepoint++; - rc = dsmTransaction((dsmContext_t *)thd->gemini.context, - &thd->gemini.savepoint, - DSMTXN_SAVE, 0, 0); - if (rc) - DBUG_RETURN(rc); - thd->gemini.needSavepoint = 0; - } - - fn_format(name_buff, name, "", ha_gemini_ext, 2 | 4); - /* Create a storage area */ - rc = dsmAreaNew(pcontext,gemini_blocksize,DSMAREA_TYPE_DATA, - &areaNumber, gemini_recbits, - (dsmText_t *)"gemini_data_area"); - if( rc != 0 ) - { - gemini_msg(pcontext, "dsmAreaNew failed %l",rc); - return(rc); - } - - /* Create an extent */ - /* Don't pass in leading ./ in name_buff */ - rc = dsmExtentCreate(pcontext,areaNumber,1,15,5, - (dsmText_t *)&name_buff[start_of_name]); - if( rc != 0 ) - { - gemini_msg(pcontext, "dsmExtentCreate failed %l",rc); - return(rc); - } - - /* Create the table storage object */ - /* Change slashes in the name to periods */ - for( i = 0; i < strlen(name_buff); i++) - if(name_buff[i] == '/' || name_buff[i] == '\\') - name_buff[i] = '.'; - - /* Get rid of .gmd suffix */ - name_buff[strlen(name_buff) - 4] = '\0'; - - rc = dsmObjectCreate(pcontext, areaNumber, &tableNumber, - DSMOBJECT_MIXTABLE,0,0,0, - (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); - /* Create a storage area */ - rc = dsmAreaNew(pcontext,gemini_blocksize,DSMAREA_TYPE_DATA, - &areaNumber, gemini_recbits, - (dsmText_t *)"gemini_index_area"); - if( rc != 0 ) - { - gemini_msg(pcontext, "dsmAreaNew failed %l",rc); - return(rc); - } - /* Create an extent */ - /* Don't pass in leading ./ in name_buff */ - rc = dsmExtentCreate(pcontext,areaNumber,1,15,5, - (dsmText_t *)&name_buff[start_of_name]); - if( rc != 0 ) - { - gemini_msg(pcontext, "dsmExtentCreate failed %l",rc); - return(rc); - } - - /* Change slashes in the name to periods */ - for( i = 0; i < strlen(name_buff); i++) - if(name_buff[i] == '/' || name_buff[i] == '\\') - name_buff[i] = '.'; - - /* Get rid of .gmi suffix */ - name_buff[strlen(name_buff) - 4] = '\0'; - - baseNameLen = strlen(name_buff); - name_buff[baseNameLen] = '.'; - baseNameLen++; - for( i = 0; i < form->keys; i++) - { - dsmObjectAttr_t indexUnique; - - indexNumber = DSMINDEX_INVALID; - /* Create a storage object record for each index */ - /* Add the index name so the object name is in the form - <db>.<table>.<index_name> */ - strcpy(&name_buff[baseNameLen],table->key_info[i].name); - if(table->key_info[i].flags & HA_NOSAME) - indexUnique = 1; - else - indexUnique = 0; - rc = dsmObjectCreate(pcontext, areaNumber, &indexNumber, - DSMOBJECT_MIXINDEX,indexUnique,tableNumber, - DSMOBJECT_MIXTABLE, - (dsmText_t *)&name_buff[start_of_name], - &dummy,&dummy); - - } - } - /* 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 - the alter table statement to abort because of a lock table overflow - */ - if (thd->lex.sql_command == SQLCOM_CREATE_INDEX || - thd->lex.sql_command == SQLCOM_ALTER_TABLE || - thd->lex.sql_command == SQLCOM_DROP_INDEX) - { - rc = dsmObjectLock(pcontext, - (dsmObject_t)tableNumber,DSMOBJECT_TABLE,0, - DSM_LK_EXCL, 1, 0); - /* and don't commit so we won't release the table on the table number - of the table being altered */ - } - else - { - if(!rc) - rc = gemini_commit(thd); - } - - DBUG_RETURN(rc); -} - -int ha_gemini::delete_table(const char *pname) -{ - THD *thd; - dsmStatus_t rc; - dsmContext_t *pcontext; - unsigned i,nameLen; - dsmArea_t indexArea = 0; - dsmArea_t tableArea = 0; - dsmObjectAttr_t objectAttr; - dsmObject_t associate; - dsmObjectType_t associateType; - dsmDbkey_t block, root; - int need_txn = 0; - dsmObject_t tableNum = 0; - char name_buff[FN_REFLEN]; - char dbname_buff[FN_REFLEN]; - DBUG_ENTER("ha_gemini::delete_table"); - - /* separate out the name of the table and the database (a VST must be - ** located in the mysql database) - */ - rc = gemini_parse_table_name(pname, dbname_buff, name_buff); - if (rc == 0) - { - /* If the table is a VST, there are no areas or extents to delete */ - if (strcmp(dbname_buff, "mysql") == 0) - { - tableNum = gemini_is_vst(name_buff); - if (tableNum) - { - return 0; - } - } - } - - thd = current_thd; - if(thd->gemini.context == NULL) - { - /* Need to get this thread a connection into the database */ - rc = gemini_connect(thd); - if(rc) - { - DBUG_RETURN(rc); - } - } - pcontext = (dsmContext_t *)thd->gemini.context; - - - bzero(name_buff, FN_REFLEN); - - nameLen = strlen(pname); - for( i = start_of_name; i < nameLen; i++) - { - if(pname[i] == '/' || pname[i] == '\\') - name_buff[i-start_of_name] = '.'; - else - name_buff[i-start_of_name] = pname[i]; - } - - rc = dsmObjectNameToNum(pcontext, (dsmText_t *)name_buff, - (dsmObject_t *)&tableNum); - if (rc) - { - gemini_msg(pcontext, "Unable to find table number for %s", name_buff); - rc = gemini_rollback(thd); - if (rc) - { - gemini_msg(pcontext, "Error in rollback %l",rc); - } - DBUG_RETURN(rc); - } - - rc = dsmObjectInfo(pcontext, tableNum, DSMOBJECT_MIXTABLE, tableNum, - &tableArea, &objectAttr, &associateType, &block, &root); - if (rc) - { - gemini_msg(pcontext, "Failed to get area number for table %d, %s, return %l", - tableNum, pname, rc); - rc = gemini_rollback(thd); - if (rc) - { - gemini_msg(pcontext, "Error in rollback %l",rc); - } - } - - indexArea = DSMAREA_INVALID; - - /* Delete the indexes and tables storage objects for with the table */ - rc = dsmObjectDeleteAssociate(pcontext, tableNum, &indexArea); - if (rc) - { - 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) - { - gemini_msg(pcontext, "Error in rollback %l",rc); - } - DBUG_RETURN(rc); - } - - if (indexArea != DSMAREA_INVALID) - { - /* Delete the extents for both Index and Table */ - rc = dsmExtentDelete(pcontext, indexArea); - rc = dsmAreaDelete(pcontext, indexArea); - if (rc) - { - gemini_msg(pcontext, "Error deleting Index Area %l, return %l", indexArea, rc); - - /* roll back txn and return */ - rc = gemini_rollback(thd); - if (rc) - { - gemini_msg(pcontext, "Error in rollback %l",rc); - } - DBUG_RETURN(rc); - } - } - - rc = dsmExtentDelete(pcontext, tableArea); - rc = dsmAreaDelete(pcontext, tableArea); - if (rc) - { - 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) - { - gemini_msg(pcontext, "Error in rollback %l",rc); - } - DBUG_RETURN(rc); - } - - - /* Commit the transaction */ - rc = gemini_commit(thd); - if (rc) - { - gemini_msg(pcontext, "Failed to commit transaction %l",rc); - } - - - /* now remove all the files that need to be removed and - cause a checkpoint so recovery will work */ - rc = dsmExtentUnlink(pcontext); - - DBUG_RETURN(0); -} - - -int ha_gemini::rename_table(const char *pfrom, const char *pto) -{ - THD *thd; - dsmContext_t *pcontext; - dsmStatus_t rc; - char dbname_buff[FN_REFLEN]; - char name_buff[FN_REFLEN]; - char newname_buff[FN_REFLEN]; - char newextname_buff[FN_REFLEN]; - char newidxextname_buff[FN_REFLEN]; - unsigned i, nameLen; - dsmObject_t tableNum; - dsmArea_t indexArea = 0; - dsmArea_t tableArea = 0; - - DBUG_ENTER("ha_gemini::rename_table"); - - /* don't allow rename of VSTs */ - rc = gemini_parse_table_name(pfrom, dbname_buff, name_buff); - if (rc == 0) - { - /* If the table is a VST, don't create areas or extents */ - if (strcmp(dbname_buff, "mysql") == 0) - { - if (gemini_is_vst(name_buff)) - { - return DSM_S_CANT_RENAME_VST; - } - } - } - - thd = current_thd; - if (thd->gemini.context == NULL) - { - /* Need to get this thread a connection into the database */ - rc = gemini_connect(thd); - if (rc) - { - DBUG_RETURN(rc); - } - } - - pcontext = (dsmContext_t *)thd->gemini.context; - - /* change the slashes to dots in the old and new names */ - nameLen = strlen(pfrom); - for( i = start_of_name; i < nameLen; i++) - { - if(pfrom[i] == '/' || pfrom[i] == '\\') - name_buff[i-start_of_name] = '.'; - else - name_buff[i-start_of_name] = pfrom[i]; - } - name_buff[i-start_of_name] = '\0'; - - nameLen = strlen(pto); - for( i = start_of_name; i < nameLen; i++) - { - if(pto[i] == '/' || pto[i] == '\\') - newname_buff[i-start_of_name] = '.'; - else - newname_buff[i-start_of_name] = pto[i]; - } - newname_buff[i-start_of_name] = '\0'; - - /* generate new extent names (for table and index extents) */ - fn_format(newextname_buff, pto, "", ha_gemini_ext, 2 | 4); - fn_format(newidxextname_buff, pto, "", ha_gemini_idx_ext, 2 | 4); - - 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, &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); - } - } - } - - if (!rc && indexArea) - { - 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: - DBUG_RETURN(rc); -} - - -/* - How many seeks it will take to read through the table - This is to be comparable to the number returned by records_in_range so - that we can decide if we should scan the table or use keys. -*/ - -double ha_gemini::scan_time() -{ - return (double)records / - (double)((gemini_blocksize / (double)table->reclength)); -} - -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, *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) - { - /* We don't really need a savepoint here but do it anyway - just to keep the savepoint number correct. */ - thd->gemini.savepoint++; - error = dsmTransaction((dsmContext_t *)thd->gemini.context, - &thd->gemini.savepoint, - DSMTXN_SAVE, 0, 0); - if (error) - return(error); - thd->gemini.needSavepoint = 0; - } - 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 */ - 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; - } - - 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; // 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) - { - indexCount++; - if(!check_opt->quick) - { - /* Fetch row and compare to data produced from key */ - error = fetch_row(thd->gemini.context,buf); - if(!error) - { - 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); - checkStatus = HA_ADMIN_CORRUPT; - errorCount++; - if(errorCount > 1000) - goto error_return; - } - else if(error == DSM_S_RMNOTFND) - { - 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); - if(errorCount > 1000) - goto error_return; - error = 0; - } - } - } - - 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 */ - - if(records != indexCount) - { - /* 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, - table->key_info[i].name); - } - } - else - { - checkStatus = HA_ADMIN_FAILED; - goto error_return; - } - index_end(); - } - if(!check_opt->quick) - { - /* Now scan the table and for each row generate the keys - and find them in the index */ - error = fullCheck(thd, buf); - if(error) - checkStatus = error; - } - // Store the key distribution information - error = saveKeyStats(thd); - -error_return: - 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; - 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; - int errorCount = 0; - int checkStatus = 0; - - lastRowid = 0; - - while(((error = rnd_next( buf)) != HA_ERR_END_OF_FILE) && errorCount <= 1000) - { - if(!error) - { - error = handleIndexEntries(buf,lastRowid,KEY_CHECK); - if(error) - { - /* Error finding an index entry for a row. */ - print_msg(thd,table->real_name,"check","error", - "Unable to find all index entries for row %d", - lastRowid); - errorCount++; - checkStatus = HA_ADMIN_CORRUPT; - error = 0; - } - } - else - { - /* Error reading a row */ - print_msg(thd,table->real_name,"check","error", - "Error reading row %d status = %d", - lastRowid,error); - errorCount++; - checkStatus = HA_ADMIN_CORRUPT; - error = 0; - } - } - - return checkStatus; -} - -int ha_gemini::repair(THD* thd, HA_CHECK_OPT* check_opt) -{ - int error; - dsmRecord_t dsmRecord; - byte *buf; - - if(thd->gemini.needSavepoint) - { - /* We don't really need a savepoint here but do it anyway - just to keep the savepoint number correct. */ - thd->gemini.savepoint++; - error = dsmTransaction((dsmContext_t *)thd->gemini.context, - &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; - } - - - /* Lock the table */ - error = dsmObjectLock((dsmContext_t *)thd->gemini.context, - (dsmObject_t)tableNumber, - 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); - - 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 = (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->rec_buff_length; - while(!error) - { - error = dsmTableScan((dsmContext_t *)thd->gemini.context, - &dsmRecord, DSMFINDNEXT, DSM_LK_NOLOCK, - 1); - if(!error) - { - if (!(error = unpack_row((char *)buf,(char *)dsmRecord.pbuffer))) - { - 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; - } - } - } - } - error = dsmObjectUnlock((dsmContext_t *)thd->gemini.context, - (dsmObject_t)tableNumber, - DSMOBJECT_TABLE,0, - DSM_LK_EXCL,0); - 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); - - 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, - const byte *end_key,uint end_key_len, - enum ha_rkey_function end_search_flag) -{ - int error; - int componentLen; - float pctInrange; - ha_rows rows = 5; - - DBUG_ENTER("records_in_range"); - - error = index_init(keynr); - if(error) - DBUG_RETURN(rows); - - pbracketBase->index = (short)pindexNumbers[keynr]; - pbracketBase->keycomps = 1; - - if(start_key) - { - error = pack_key(keynr, pbracketBase, start_key, start_key_len); - if(start_search_flag == HA_READ_AFTER_KEY) - { - /* A greater than operation */ - error = gemKeyAddLow(pbracketBase->keystr + pbracketBase->keyLen, - &componentLen); - pbracketBase->keyLen += componentLen; - } - } - else - { - error = gemKeyLow(pbracketBase->keystr, &componentLen, - pbracketBase->index); - pbracketBase->keyLen = componentLen; - - } - pbracketBase->keyLen -= FULLKEYHDRSZ; - - if(end_key) - { - error = pack_key(keynr, pbracketLimit, end_key, end_key_len); - if(!error && end_search_flag == HA_READ_AFTER_KEY) - { - error = gemKeyAddHigh(pbracketLimit->keystr + pbracketLimit->keyLen, - &componentLen); - pbracketLimit->keyLen += componentLen; - } - } - else - { - error = gemKeyHigh(pbracketLimit->keystr,&componentLen, - pbracketLimit->index); - pbracketLimit->keyLen = componentLen; - } - - pbracketLimit->keyLen -= FULLKEYHDRSZ; - error = dsmIndexRowsInRange((dsmContext_t *)current_thd->gemini.context, - pbracketBase,pbracketLimit, - tableNumber, - &pctInrange); - if(pctInrange >= 1) - rows = (ha_rows)pctInrange; - else - { - rows = (ha_rows)(records * pctInrange); - if(!rows && pctInrange > 0) - rows = 1; - } - index_end(); - - DBUG_RETURN(rows); -} - - -/* - Pack a row for storage. If the row is of fixed length, just store the - row 'as is'. - If not, we will generate a packed row suitable for storage. - This will only fail if we don't have enough memory to pack the row, which; - 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, - 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; - } - /* Copy null bits */ - memcpy(rec_buff, record, table->null_bytes); - byte *ptr=rec_buff + table->null_bytes; - - for (Field **field=table->field ; *field ; field++) - { -#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; -} - -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. - ** Convert the fields to MySQL format. - */ - if (RM_IS_VST(tableNumber)) - { - int i = 2; /* VST fields are numbered sequentially starting at 2 */ - long longValue; - char *fld; - unsigned long unknown; - - for (Field **field = table->field; *field; field++, i++) - { - switch ((*field)->type()) - { - case FIELD_TYPE_LONG: - case FIELD_TYPE_TINY: - case FIELD_TYPE_SHORT: - case FIELD_TYPE_TIMESTAMP: - case FIELD_TYPE_LONGLONG: - case FIELD_TYPE_INT24: - case FIELD_TYPE_DATE: - case FIELD_TYPE_TIME: - case FIELD_TYPE_DATETIME: - case FIELD_TYPE_YEAR: - case FIELD_TYPE_NEWDATE: - case FIELD_TYPE_ENUM: - case FIELD_TYPE_SET: - recGetLONG((dsmText_t *)prow, i, 0, &longValue, &unknown); - if (unknown) - { - (*field)->set_null(); - } - else - { - (*field)->set_notnull(); - (*field)->store((longlong)longValue); - } - break; - - case FIELD_TYPE_DECIMAL: - case FIELD_TYPE_DOUBLE: - case FIELD_TYPE_TINY_BLOB: - case FIELD_TYPE_MEDIUM_BLOB: - case FIELD_TYPE_LONG_BLOB: - case FIELD_TYPE_BLOB: - case FIELD_TYPE_VAR_STRING: - break; - - case FIELD_TYPE_STRING: - svcByteString_t stringFld; - - fld = (char *)my_malloc((*field)->field_length, MYF(MY_WME)); - stringFld.pbyte = (TEXT *)fld; - stringFld.size = (*field)->field_length; - recGetBYTES((dsmText_t *)prow, i, 0, &stringFld, &unknown); - if (unknown) - { - (*field)->set_null(); - } - else - { - (*field)->set_notnull(); - (*field)->store(fld, (*field)->field_length); - } - my_free(fld, MYF(MY_ALLOW_ZERO_PTR)); - break; - - default: - break; - } - } - } - else - { - memcpy(record,(char*) prow,table->reclength); - } - } - else - { - /* Copy null bits */ - const char *ptr= (const char*) prow; - memcpy(record, ptr, table->null_bytes); - ptr+=table->null_bytes; - - for (Field **field=table->field ; *field ; field++) - { -#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, 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 ( 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((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; -} - -int gemini_parse_table_name(const char *fullname, char *dbname, char *tabname) -{ - char *namestart; - char *nameend; - - /* separate out the name of the table and the database - */ - namestart = (char *)strchr(fullname + start_of_name, '/'); - if (!namestart) - { - /* if on Windows, slashes go the other way */ - namestart = (char *)strchr(fullname + start_of_name, '\\'); - } - nameend = (char *)strchr(fullname + start_of_name, '.'); - /* sometimes fullname has an extension, sometimes it doesn't */ - if (!nameend) - { - nameend = (char *)fullname + strlen(fullname); - } - strncpy(dbname, fullname + start_of_name, - (namestart - fullname) - start_of_name); - dbname[(namestart - fullname) - start_of_name] = '\0'; - strncpy(tabname, namestart + 1, (nameend - namestart) - 1); - tabname[nameend - namestart - 1] = '\0'; - - return 0; -} - -/* PROGRAM: gemini_is_vst - if the name is the name of a VST, return - * its number - * - * RETURNS: Table number if a match is found - * 0 if not a VST - */ -int -gemini_is_vst(const char *pname) /* IN the name */ -{ - int tablenum = 0; - - for (int i = 0; i < vstnumfils; i++) - { - if (strcmp(pname, vstfil[i].filename) == 0) - { - tablenum = vstfil[i].filnum; - break; - } - } - - return tablenum; -} - -static void print_msg(THD *thd, const char *table_name, const char *op_name, - const char *msg_type, const char *fmt, ...) -{ - String* packet = &thd->packet; - packet->length(0); - char msgbuf[256]; - msgbuf[0] = 0; - va_list args; - va_start(args,fmt); - - my_vsnprintf(msgbuf, sizeof(msgbuf), fmt, args); - msgbuf[sizeof(msgbuf) - 1] = 0; // healthy paranoia - - DBUG_PRINT(msg_type,("message: %s",msgbuf)); - - net_store_data(packet, table_name); - net_store_data(packet, op_name); - net_store_data(packet, msg_type); - net_store_data(packet, msgbuf); - if (my_net_write(&thd->net, (char*)thd->packet.ptr(), - thd->packet.length())) - 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 deleted file mode 100644 index 96c0cdd4241..00000000000 --- a/sql/ha_gemini.h +++ /dev/null @@ -1,208 +0,0 @@ -/* Copyright (C) 2000 MySQL AB & NuSphere Corporation - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - - -#ifdef __GNUC__ -#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}; -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 -{ - /* define file as an int for now until we have a real file struct */ - int file; - uint int_option_flag; - int tableNumber; - dsmIndex_t *pindexNumbers; // dsm object numbers for the indexes on this table - dsmRecid_t lastRowid; - uint last_dup_key; - bool fixed_length_row, key_read, using_ignore; - byte *rec_buff; - dsmKey_t *pbracketBase; - 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, - 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, - enum_key_string_options option); - - int handleIndexEntry(const byte * record, dsmRecid_t recid, - enum_key_string_options option,uint keynr); - - int createKeyString(const byte * record, KEY *pkeyinfo, - unsigned char *pkeyBuf, int bufSize, - int *pkeyStringLen, short geminiIndexNumber, - bool *thereIsAnull); - int fullCheck(THD *thd,byte *buf); - - int pack_key( uint keynr, dsmKey_t *pkey, - const byte *key_ptr, uint key_length); - - void unpack_key(char *record, dsmKey_t *key, uint index); - - int key_cmp(uint keynr, const byte * old_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_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_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) - { - } - ~ha_gemini() {} - const char *table_type() const { return "Gemini"; } - const char **bas_ext() const; - ulong option_flag() const { return int_option_flag; } - 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 / 2; } - bool fast_key_read() { return 1;} - bool has_transactions() { return 1;} - - int open(const char *name, int mode, uint test_if_locked); - int close(void); - double scan_time(); - int write_row(byte * buf); - int update_row(const byte * old_data, byte * new_data); - int delete_row(const byte * buf); - int index_init(uint index); - int index_end(); - int index_read(byte * buf, const byte * key, - uint key_len, enum ha_rkey_function find_flag); - int index_read_idx(byte * buf, uint index, const byte * key, - uint key_len, enum ha_rkey_function find_flag); - int index_next(byte * buf); - int index_next_same(byte * buf, const byte *key, uint keylen); - int index_prev(byte * buf); - int index_first(byte * buf); - int index_last(byte * buf); - int rnd_init(bool scan=1); - int rnd_end(); - int rnd_next(byte *buf); - int rnd_pos(byte * buf, byte *pos); - void position(const byte *record); - 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); - ha_rows records_in_range(int inx, - const byte *start_key,uint start_key_len, - 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); - int rename_table(const char* from, const char* to); - THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to, - enum thr_lock_type lock_type); -}; - -#define GEMOPT_FLUSH_LOG 0x00000001 -#define GEMOPT_UNBUFFERED_IO 0x00000002 - -#define GEMINI_RECOVERY_FULL 0x00000001 -#define GEMINI_RECOVERY_NONE 0x00000002 -#define GEMINI_RECOVERY_FORCE 0x00000004 - -#define GEM_OPTID_SPIN_RETRIES 1 - -extern bool gemini_skip; -extern SHOW_COMP_OPTION have_gemini; -extern long gemini_options; -extern long gemini_buffer_cache; -extern long gemini_io_threads; -extern long gemini_log_cluster_size; -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; - -bool gemini_init(void); -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 = BLKSIZE; -const int gemini_recbits = DEFAULT_RECBITS; - -extern "C" void uttrace(void); diff --git a/sql/ha_hash.h b/sql/ha_hash.h deleted file mode 100644 index 80416611406..00000000000 --- a/sql/ha_hash.h +++ /dev/null @@ -1,31 +0,0 @@ - -int ha_hash::create(my_string name, register TABLE *form, - ulonglong auto_increment_value) -{ - register uint i,j; - char buff[FN_REFLEN]; - KEY *pos; - H_KEYDEF keydef[MAX_KEY]; - DBUG_ENTER("cre_hash"); - - pos=form->key_info; - for (i=0; i < form->keys ; i++, pos++) - { - keydef[i].hk_flag= pos->flags & HA_NOSAME; - for (j=0 ; (int7) j < pos->key_parts ; j++) - { - uint flag=pos->key_part[j].key_type; - if (!f_is_packed(flag) && f_packtype(flag) == (int) FIELD_TYPE_DECIMAL && - !(flag & FIELDFLAG_BINARY)) - keydef[i].hk_keyseg[j].key_type= (int) HA_KEYTYPE_TEXT; - else - keydef[i].hk_keyseg[j].key_type= (int) HA_KEYTYPE_BINARY; - keydef[i].hk_keyseg[j].start= pos->key_part[j].offset; - keydef[i].hk_keyseg[j].length= pos->key_part[j].length; - } - keydef[i].hk_keyseg[j].key_type= 0; - } - DBUG_RETURN(h_create(fn_format(buff,name,"","",2+4+16),i, - keydef,form->reclength,form->max_rows,form->min_rows, - 0)); -} /* cre_hash */ diff --git a/sql/ha_heap.cc b/sql/ha_heap.cc index 13dccc2bf64..2edc3b1478e 100644 --- a/sql/ha_heap.cc +++ b/sql/ha_heap.cc @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -33,10 +33,11 @@ const char **ha_heap::bas_ext() const int ha_heap::open(const char *name, int mode, uint test_if_locked) { - uint key,part,parts,mem_per_row=0; + uint key,parts,mem_per_row=0; ulong max_rows; HP_KEYDEF *keydef; HP_KEYSEG *seg; + THD *thd= current_thd; for (key=parts=0 ; key < table->keys ; key++) parts+=table->key_info[key].key_parts; @@ -48,36 +49,51 @@ int ha_heap::open(const char *name, int mode, uint test_if_locked) for (key=0 ; key < table->keys ; key++) { KEY *pos=table->key_info+key; + KEY_PART_INFO *key_part= pos->key_part; + KEY_PART_INFO *key_part_end= key_part+pos->key_parts; + mem_per_row += (pos->key_length + (sizeof(char*) * 2)); - + keydef[key].keysegs=(uint) pos->key_parts; - keydef[key].flag = (pos->flags & HA_NOSAME); + keydef[key].flag = (pos->flags & (HA_NOSAME | HA_NULL_ARE_EQUAL)); keydef[key].seg=seg; - - for (part=0 ; part < pos->key_parts ; part++) + + for (; key_part != key_part_end ; key_part++, seg++) { - uint flag=pos->key_part[part].key_type; + uint flag=key_part->key_type; + Field *field=key_part->field; if (!f_is_packed(flag) && f_packtype(flag) == (int) FIELD_TYPE_DECIMAL && !(flag & FIELDFLAG_BINARY)) seg->type= (int) HA_KEYTYPE_TEXT; else seg->type= (int) HA_KEYTYPE_BINARY; - seg->start=(uint) pos->key_part[part].offset; - seg->length=(uint) pos->key_part[part].length; - seg++; + seg->start=(uint) key_part->offset; + seg->length=(uint) key_part->length; + if (field->null_ptr) + { + seg->null_bit=field->null_bit; + seg->null_pos= (uint) (field->null_ptr- + (uchar*) table->record[0]); + } + else + { + seg->null_bit=0; + seg->null_pos=0; + } } } mem_per_row += MY_ALIGN(table->reclength+1, sizeof(char*)); - max_rows = (ulong) (max_heap_table_size / mem_per_row); + max_rows = (ulong) (thd->variables.max_heap_table_size / mem_per_row); file=heap_open(name,mode, table->keys,keydef, table->reclength, - ((table->max_rows < max_rows && table->max_rows) ? - table->max_rows : max_rows), - table->min_rows); + (ulong) ((table->max_rows < max_rows && table->max_rows) ? + table->max_rows : max_rows), + (ulong) table->min_rows); my_free((gptr) keydef,MYF(0)); - info(HA_STATUS_NO_LOCK | HA_STATUS_CONST | HA_STATUS_VARIABLE); + if (file) + info(HA_STATUS_NO_LOCK | HA_STATUS_CONST | HA_STATUS_VARIABLE); ref_length=sizeof(HEAP_PTR); return (!file ? errno : 0); } @@ -147,7 +163,7 @@ int ha_heap::index_prev(byte * buf) table->status=error ? STATUS_NOT_FOUND: 0; return error; } - + int ha_heap::index_first(byte * buf) { statistic_increment(ha_read_first_count,&LOCK_status); @@ -227,7 +243,7 @@ int ha_heap::delete_all_rows() int ha_heap::external_lock(THD *thd, int lock_type) { return 0; // No external locking -} +} THR_LOCK_DATA **ha_heap::store_lock(THD *thd, THR_LOCK_DATA **to, @@ -247,7 +263,7 @@ THR_LOCK_DATA **ha_heap::store_lock(THD *thd, int ha_heap::delete_table(const char *name) { - int error=heap_delete_all(name); + int error=heap_delete_table(name); return error == ENOENT ? 0 : error; } @@ -272,7 +288,6 @@ ha_rows ha_heap::records_in_range(int inx, return 10; // Good guess } -/* We can just delete the heap on creation */ int ha_heap::create(const char *name, TABLE *form, HA_CREATE_INFO *create_info) diff --git a/sql/ha_heap.h b/sql/ha_heap.h index 6b7e9c6c626..504f5262bf3 100644 --- a/sql/ha_heap.h +++ b/sql/ha_heap.h @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -31,11 +31,25 @@ class ha_heap: public handler ha_heap(TABLE *table): handler(table), file(0) {} ~ha_heap() {} const char *table_type() const { return "HEAP"; } + const char *index_type(uint inx) + { + return ((table->key_info[inx].algorithm == HA_KEY_ALG_BTREE) ? "BTREE" : + "HASH"); + } const char **bas_ext() const; - ulong option_flag() const - { return (HA_READ_RND_SAME | HA_NO_INDEX | HA_ONLY_WHOLE_INDEX | - HA_WRONG_ASCII_ORDER | HA_KEYPOS_TO_RNDPOS | HA_NO_BLOBS | - HA_REC_NOT_IN_SEQ | HA_NO_FULLTEXT_KEY); } + ulong table_flags() const + { + return (HA_READ_RND_SAME | HA_NO_INDEX | HA_KEYPOS_TO_RNDPOS | + HA_NO_BLOBS | HA_NULL_KEY | HA_REC_NOT_IN_SEQ | + HA_NO_AUTO_INCREMENT); + } + ulong index_flags(uint inx) const + { + return ((table->key_info[inx].algorithm == HA_KEY_ALG_BTREE) ? + (HA_READ_NEXT | HA_READ_PREV | HA_READ_ORDER) : + (HA_ONLY_WHOLE_INDEX | HA_WRONG_ASCII_ORDER | + HA_NOT_READ_PREFIX_LAST)); + } uint max_record_length() const { return HA_MAX_REC_LENGTH; } uint max_keys() const { return MAX_KEY; } uint max_key_parts() const { return MAX_REF_PARTS; } diff --git a/sql/ha_innobase.cc b/sql/ha_innodb.cc index 5b2af70f34e..1c6de24ed75 100644 --- a/sql/ha_innobase.cc +++ b/sql/ha_innodb.cc @@ -16,18 +16,20 @@ /* This file defines the InnoDB handler: the interface between MySQL and InnoDB */ - + /* TODO list for the InnoDB handler: - Ask Monty if strings of different languages can exist in the same database. Answer: in 4.1 yes. */ - + #ifdef __GNUC__ #pragma implementation // gcc: Class implementation #endif #include "mysql_priv.h" #include "slave.h" +#include "sql_cache.h" + #ifdef HAVE_INNOBASE_DB #include <m_ctype.h> #include <assert.h> @@ -36,19 +38,14 @@ InnoDB */ #define MAX_ULONG_BIT ((ulong) 1 << (sizeof(ulong)*8-1)) -#include "ha_innobase.h" +#include "ha_innodb.h" -/* We must declare this here because we undef SAFE_MUTEX below */ pthread_mutex_t innobase_mutex; /* Store MySQL definition of 'byte': in Linux it is char while InnoDB uses unsigned char */ typedef byte mysql_byte; -#ifdef SAFE_MUTEX -#undef pthread_mutex_t -#endif - #define INSIDE_HA_INNOBASE_CC /* Include necessary InnoDB headers */ @@ -91,29 +88,24 @@ long innobase_mirrored_log_groups, innobase_log_files_in_group, /* The default values for the following char* start-up parameters are determined in innobase_init below: */ - -/* innobase_data_file_path=ibdata:15,idata2:1,... */ - -char* innobase_data_file_path = NULL; + char* innobase_data_home_dir = NULL; +char* innobase_data_file_path = NULL; char* innobase_log_group_home_dir = NULL; char* innobase_log_arch_dir = NULL; +/* The following has a misleading name: starting from 4.0.5, this also +affects Windows: */ char* innobase_unix_file_flush_method = NULL; /* Below we have boolean-valued start-up parameters, and their default values */ +uint innobase_flush_log_at_trx_commit = 0; my_bool innobase_log_archive = FALSE; my_bool innobase_use_native_aio = FALSE; my_bool innobase_fast_shutdown = TRUE; -/* innodb_flush_log_at_trx_commit can now have 3 values: -0 : write to the log file once per second and flush it to disk; -1 : write to the log file at each commit and flush it to disk; -2 : write to the log file at each commit, but flush to disk only once per -second */ - -long innobase_flush_log_at_trx_commit = 0; +static char *internal_innobase_data_file_path = NULL; /* The following counter is used to convey information to InnoDB about server activity: in selects it is not sensible to call @@ -159,6 +151,19 @@ innobase_release_stat_resources( } /************************************************************************ +Call this function when mysqld passes control to the client. That is to +avoid deadlocks on the adaptive hash S-latch possibly held by thd. For more +documentation, see handler.cc. */ + +void +innobase_release_temporary_latches( +/*===============================*/ + void* innobase_tid) +{ + innobase_release_stat_resources((trx_t*)innobase_tid); +} + +/************************************************************************ Increments innobase_active_counter and every INNOBASE_WAKE_INTERVALth time calls srv_active_wake_master_thread. This function should be used when a single database operation may introduce a small need for @@ -239,7 +244,7 @@ convert_error_code_to_mysql( } else if (error == (int) DB_COL_APPEARS_TWICE_IN_INDEX) { - return(HA_ERR_WRONG_TABLE_DEF); + return(HA_ERR_CRASHED); } else if (error == (int) DB_OUT_OF_FILE_SPACE) { @@ -256,6 +261,10 @@ convert_error_code_to_mysql( } else if (error == (int) DB_TOO_BIG_RECORD) { return(HA_ERR_TO_BIG_ROW); + + } else if (error == (int) DB_CORRUPTION) { + + return(HA_ERR_CRASHED); } else { return(-1); // Unknown error } @@ -345,11 +354,12 @@ check_trx_exists( trx = (trx_t*) thd->transaction.all.innobase_tid; if (trx == NULL) { - ut_a(thd != NULL); + DBUG_ASSERT(thd != NULL); trx = trx_allocate_for_mysql(); trx->mysql_thd = thd; - + trx->mysql_query_str = &((*thd).query); + thd->transaction.all.innobase_tid = trx; /* The execution of a single SQL statement is denoted by @@ -409,9 +419,180 @@ ha_innobase::update_thd( return(0); } -#ifdef notdefined -/* The code here appears for documentational purposes only. Not used -or tested yet. Will be used in 4.1. */ + +/* BACKGROUND INFO: HOW THE MYSQL QUERY CACHE WORKS WITH INNODB + ------------------------------------------------------------ + +1) The use of the query cache for TBL is disabled when there is an +uncommitted change to TBL. + +2) When a change to TBL commits, InnoDB stores the current value of +its global trx id counter, let us denote it by INV_TRX_ID, to the table object +in the InnoDB data dictionary, and does only allow such transactions whose +id >= INV_TRX_ID to use the query cache. + +3) When InnoDB does an INSERT/DELETE/UPDATE to a table TBL, or an implicit +modification because an ON DELETE CASCADE, we invalidate the MySQL query cache +of TBL immediately. + +How this is implemented inside InnoDB: + +1) Since every modification always sets an IX type table lock on the InnoDB +table, it is easy to check if there can be uncommitted modifications for a +table: just check if there are locks in the lock list of the table. + +2) When a transaction inside InnoDB commits, it reads the global trx id +counter and stores the value INV_TRX_ID to the tables on which it had a lock. + +3) If there is an implicit table change from ON DELETE CASCADE or SET NULL, +InnoDB calls an invalidate method for the MySQL query cache for that table. + +How this is implemented inside sql_cache.cc: + +1) The query cache for an InnoDB table TBL is invalidated immediately at an +INSERT/UPDATE/DELETE, just like in the case of MyISAM. No need to delay +invalidation to the transaction commit. + +2) To store or retrieve a value from the query cache of an InnoDB table TBL, +any query must first ask InnoDB's permission. We must pass the thd as a +parameter because InnoDB will look at the trx id, if any, associated with +that thd. + +3) Use of the query cache for InnoDB tables is now allowed also when +AUTOCOMMIT==0 or we are inside BEGIN ... COMMIT. Thus transactions no longer +put restrictions on the use of the query cache. +*/ + +/********************************************************************** +The MySQL query cache uses this to check from InnoDB if the query cache at +the moment is allowed to operate on an InnoDB table. The SQL query must +be a non-locking SELECT. + +The query cache is allowed to operate on certain query only if this function +returns TRUE for all tables in the query. + +If thd is not in the autocommit state, this function also starts a new +transaction for thd if there is no active trx yet, and assigns a consistent +read view to it if there is no read view yet. */ + +my_bool +innobase_query_caching_of_table_permitted( +/*======================================*/ + /* out: TRUE if permitted, FALSE if not; + note that the value FALSE does not mean + we should invalidate the query cache: + invalidation is called explicitly */ + THD* thd, /* in: thd of the user who is trying to + store a result to the query cache or + retrieve it */ + char* full_name, /* in: concatenation of database name, + the null character '\0', and the table + name */ + uint full_name_len) /* in: length of the full name, i.e. + len(dbname) + len(tablename) + 1 */ +{ + ibool is_autocommit; + trx_t* trx; + char* ptr; + char norm_name[1000]; + + ut_a(full_name_len < 999); + + if (thd->variables.tx_isolation == ISO_SERIALIZABLE) { + /* In the SERIALIZABLE mode we add LOCK IN SHARE MODE to every + plain SELECT */ + + return((my_bool)FALSE); + } + + trx = (trx_t*) thd->transaction.all.innobase_tid; + + if (trx == NULL) { + trx = check_trx_exists(thd); + } + + innobase_release_stat_resources(trx); + + if (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) { + + is_autocommit = TRUE; + } else { + is_autocommit = FALSE; + + } + + if (is_autocommit && trx->conc_state == TRX_NOT_STARTED) { + /* We are going to retrieve the query result from the + query cache. This cannot be a store operation because then + we would have started the trx already. + + We can imagine we instantaneously serialize + this consistent read trx to the current trx id counter. + If trx2 would have changed the tables of a query + result stored in the cache, and trx2 would have already + committed, making the result obsolete, then trx2 would have + already invalidated the cache. Thus we can trust the result + in the cache is ok for this query. */ + + return((my_bool)TRUE); + } + + /* Normalize the table name to InnoDB format */ + + memcpy(norm_name, full_name, full_name_len); + + norm_name[strlen(norm_name)] = '/'; /* InnoDB uses '/' as the + separator between db and table */ + norm_name[full_name_len] = '\0'; +#ifdef __WIN__ + /* Put to lower case */ + + ptr = norm_name; + + while (*ptr != '\0') { + *ptr = tolower(*ptr); + ptr++; + } +#endif + if (row_search_check_if_query_cache_permitted(trx, norm_name)) { + + printf("Query cache for %s permitted\n", norm_name); + + return((my_bool)TRUE); + } + + printf("Query cache for %s NOT permitted\n", norm_name); + + return((my_bool)FALSE); +} + +extern "C" { +/********************************************************************* +Invalidates the MySQL query cache for the table. +NOTE that the exact prototype of this function has to be in +/innobase/row/row0ins.c! */ + +void +innobase_invalidate_query_cache( +/*============================*/ + trx_t* trx, /* in: transaction which modifies the table */ + char* full_name, /* in: concatenation of database name, null + char '\0', table name, null char'\0'; + NOTE that in Windows this is always + in LOWER CASE! */ + ulint full_name_len) /* in: full name length where also the null + chars count */ +{ + /* Argument TRUE below means we are using transactions */ +#ifdef HAVE_QUERY_CACHE + query_cache.invalidate((THD*)(trx->mysql_thd), + (const char*)full_name, + (uint32)full_name_len, + TRUE); +#endif +} +} + /********************************************************************* Call this when you have opened a new table handle in HANDLER, before you call index_read_idx() etc. Actually, we can let the cursor stay open even @@ -425,9 +606,6 @@ ha_innobase::init_table_handle_for_HANDLER(void) { row_prebuilt_t* prebuilt; - ut_a(0); /* the code has not been used or tested yet; to prevent - inadvertent usage we assert an error here */ - /* If current thd does not yet have a trx struct, create one. If the current handle does not yet have a prebuilt struct, create one. Update the trx pointers in the prebuilt struct. Normally @@ -467,7 +645,6 @@ ha_innobase::init_table_handle_for_HANDLER(void) prebuilt->read_just_key = FALSE; } -#endif /************************************************************************* Opens an InnoDB database. */ @@ -477,18 +654,32 @@ innobase_init(void) /*===============*/ /* out: TRUE if error */ { - static char current_dir[3]; + static char current_dir[3]; // Set if using current lib int err; bool ret; - + char *default_path; + DBUG_ENTER("innobase_init"); - os_innodb_umask = (ulint)my_umask; + os_innodb_umask = (ulint)my_umask; - /* Use current_dir if no paths are set */ - current_dir[0] = FN_CURLIB; - current_dir[1] = FN_LIBCHAR; - current_dir[2] = 0; + /* First calculate the default path for innodb_data_home_dir etc., + in case the user has not given any value. + + Note that when using the embedded server, the datadirectory is not + necessarily the current directory of this program. */ + + if (mysql_embedded) { + default_path = mysql_real_data_home; + } else { + /* It's better to use current lib, to keep paths short */ + current_dir[0] = FN_CURLIB; + current_dir[1] = FN_LIBCHAR; + current_dir[2] = 0; + default_path = current_dir; + } + + ut_a(default_path); if (specialflag & SPECIAL_NO_PRIOR) { srv_set_thread_priorities = FALSE; @@ -496,34 +687,33 @@ innobase_init(void) srv_set_thread_priorities = TRUE; srv_query_thread_priority = QUERY_PRIOR; } - + /* Set InnoDB initialization parameters according to the values read from MySQL .cnf file */ - if (!innobase_data_file_path) { - fprintf(stderr, - "Cannot initialize InnoDB as 'innodb_data_file_path' is not set.\n" - "If you do not want to use transactional InnoDB tables, add a line\n" - "skip-innodb\n" - "to the [mysqld] section of init parameters in your my.cnf\n" - "or my.ini. If you want to use InnoDB tables, add to the [mysqld]\n" - "section, for example,\n" - "innodb_data_file_path = ibdata1:10M:autoextend\n" - "But to get good performance you should adjust for your hardware\n" - "the InnoDB startup options listed in section 2 at\n" - "http://www.innodb.com/ibman.html\n"); + /*--------------- Data files -------------------------*/ - innodb_skip=1; - DBUG_RETURN(FALSE); /* Continue without InnoDB */ - } + /* The default dir for data files is the datadir of MySQL */ srv_data_home = (innobase_data_home_dir ? innobase_data_home_dir : - current_dir); - srv_arch_dir = (innobase_log_arch_dir ? innobase_log_arch_dir : - current_dir); + default_path); - ret = (bool) - srv_parse_data_file_paths_and_sizes(innobase_data_file_path, + /* Set default InnoDB data file size to 10 MB and let it be + auto-extending. Thus users can use InnoDB in >= 4.0 without having + to specify any startup options. */ + + if (!innobase_data_file_path) { + innobase_data_file_path = (char*) "ibdata1:10M:autoextend"; + } + + /* Since InnoDB edits the argument in the next call, we make another + copy of it: */ + + internal_innobase_data_file_path = my_strdup(innobase_data_file_path, + MYF(MY_WME)); + + ret = (bool) srv_parse_data_file_paths_and_sizes( + internal_innobase_data_file_path, &srv_data_file_names, &srv_data_file_sizes, &srv_data_file_is_raw_partition, @@ -531,12 +721,26 @@ innobase_init(void) &srv_auto_extend_last_data_file, &srv_last_file_size_max); if (ret == FALSE) { - sql_print_error("InnoDB: syntax error in innodb_data_file_path"); - DBUG_RETURN(TRUE); + sql_print_error( + "InnoDB: syntax error in innodb_data_file_path"); + DBUG_RETURN(TRUE); } - if (!innobase_log_group_home_dir) - innobase_log_group_home_dir = current_dir; + /* -------------- Log files ---------------------------*/ + + /* The default dir for log files is the datadir of MySQL */ + + if (!innobase_log_group_home_dir) { + innobase_log_group_home_dir = default_path; + } + + /* Since innodb_log_arch_dir has no relevance under MySQL, + starting from 4.0.6 we always set it the same as + innodb_log_group_home_dir: */ + + innobase_log_arch_dir = innobase_log_group_home_dir; + + srv_arch_dir = innobase_log_arch_dir; ret = (bool) srv_parse_log_group_home_dirs(innobase_log_group_home_dir, @@ -550,9 +754,9 @@ innobase_init(void) DBUG_RETURN(TRUE); } - srv_unix_file_flush_method_str = (innobase_unix_file_flush_method ? - innobase_unix_file_flush_method : - (char*)"fdatasync"); + /* --------------------------------------------------*/ + + srv_file_flush_method_str = innobase_unix_file_flush_method; srv_n_log_groups = (ulint) innobase_mirrored_log_groups; srv_n_log_files = (ulint) innobase_log_files_in_group; @@ -562,8 +766,6 @@ innobase_init(void) srv_log_buffer_size = (ulint) innobase_log_buffer_size; srv_flush_log_at_trx_commit = (ulint) innobase_flush_log_at_trx_commit; - srv_use_native_aio = 0; - srv_pool_size = (ulint) innobase_buffer_pool_size; srv_mem_pool_size = (ulint) innobase_additional_mem_pool_size; @@ -576,16 +778,27 @@ innobase_init(void) srv_fast_shutdown = (ibool) innobase_fast_shutdown; + srv_print_verbose_log = mysql_embedded ? 0 : 1; + if (strcmp(default_charset_info->name, "latin1") == 0) { + /* Store the character ordering table to InnoDB. For non-latin1 charsets we use the MySQL comparison functions, and consequently we do not need to know the ordering internally in InnoDB. */ - + memcpy(srv_latin1_ordering, default_charset_info->sort_order, 256); } + /* Since we in this module access directly the fields of a trx + struct, and due to different headers and flags it might happen that + mutex_t has a different size in this module and in InnoDB + modules, we check at run time that the size is the same in + these compilation modules. */ + + srv_sizeof_trx_t_in_ha_innodb_cc = sizeof(trx_t); + err = innobase_start_or_create_for_mysql(); if (err != DB_SUCCESS) { @@ -629,6 +842,8 @@ innobase_end(void) err = innobase_shutdown_for_mysql(); hash_free(&innobase_open_tables); + my_free(internal_innobase_data_file_path,MYF(MY_ALLOW_ZERO_PTR)); + pthread_mutex_destroy(&innobase_mutex); if (err != DB_SUCCESS) { @@ -675,17 +890,20 @@ innobase_commit_low( /*================*/ trx_t* trx) /* in: transaction handle */ { - if (current_thd->slave_thread) { - - /* Update the replication position info inside InnoDB */ - - trx->mysql_master_log_file_name = glob_mi.log_file_name; - trx->mysql_master_log_pos = (ib_longlong) - (glob_mi.pos + glob_mi.event_len - + glob_mi.pending); - } - - trx_commit_for_mysql(trx); + if (current_thd->slave_thread) { + /* Update the replication position info inside InnoDB */ +#ifdef NEED_TO_BE_FIXED + trx->mysql_relay_log_file_name = active_mi->rli.log_file_name; + trx->mysql_relay_log_pos = active_mi->rli.relay_log_pos; +#endif + trx->mysql_master_log_file_name + = active_mi->rli.master_log_name; + trx->mysql_master_log_pos = ((ib_longlong) + (active_mi->rli.master_log_pos + + active_mi->rli.event_len + + active_mi->rli.pending)); + } + trx_commit_for_mysql(trx); } /********************************************************************* @@ -697,7 +915,8 @@ innobase_commit( /* out: 0 or error number */ THD* thd, /* in: MySQL thread handle of the user for whom the transaction should be committed */ - void* trx_handle)/* in: InnoDB trx handle or NULL: NULL means + void* trx_handle)/* in: InnoDB trx handle or + &innodb_dummy_stmt_trx_handle: the latter means that the current SQL statement ended, and we should mark the start of a new statement with a savepoint */ { @@ -721,11 +940,11 @@ innobase_commit( if (trx_handle != (void*)&innodb_dummy_stmt_trx_handle) { innobase_commit_low(trx); + thd->transaction.all.innodb_active_trans=0; } /* Release possible statement level resources */ innobase_release_stat_resources(trx); - trx_mark_sql_stat_end(trx); #ifndef DBUG_OFF @@ -778,7 +997,9 @@ innobase_rollback( /* out: 0 or error number */ THD* thd, /* in: handle to the MySQL thread of the user whose transaction should be rolled back */ - void* trx_handle)/* in: InnoDB trx handle or a dummy stmt handle */ + void* trx_handle)/* in: InnoDB trx handle or a dummy stmt handle; + the latter means we roll back the latest SQL + statement */ { int error = 0; trx_t* trx; @@ -802,6 +1023,7 @@ innobase_rollback( if (trx_handle != (void*)&innodb_dummy_stmt_trx_handle) { error = trx_rollback_for_mysql(trx); + thd->transaction.all.innodb_active_trans=0; } else { error = trx_rollback_last_sql_stat_for_mysql(trx); } @@ -976,8 +1198,8 @@ ha_innobase::open( ib_table = dict_table_get_and_increment_handle_count( norm_name, NULL); if (NULL == ib_table) { - - sql_print_error("InnoDB error:\n" + ut_print_timestamp(stderr); + fprintf(stderr, " InnoDB error:\n" "Cannot find table %s from the internal data dictionary\n" "of InnoDB though the .frm file for the table exists. Maybe you\n" "have deleted and recreated InnoDB data files but have forgotten\n" @@ -1044,16 +1266,23 @@ ha_innobase::open( ->clust_index_was_generated = TRUE; ref_length = DATA_ROW_ID_LEN; - - /* If we automatically created the clustered index, then - MySQL does not know about it, and MySQL must NOT be aware - of the index used on scan, to make it avoid checking if we - update the column of the index. That is why we assert below - that key_used_on_scan is the undefined value MAX_KEY. - The column is the row id in the automatical generation case, - and it will never be updated anyway. */ - DBUG_ASSERT(key_used_on_scan == MAX_KEY); + /* + If we automatically created the clustered index, then + MySQL does not know about it, and MySQL must NOT be aware + of the index used on scan, to make it avoid checking if we + update the column of the index. That is why we assert below + that key_used_on_scan is the undefined value MAX_KEY. + The column is the row id in the automatical generation case, + and it will never be updated anyway. + */ + + if (key_used_on_scan != MAX_KEY) { + fprintf(stderr, +"InnoDB: Warning: table %s key_used_on_scan is %lu even though there is no\n" +"InnoDB: primary key inside InnoDB.\n", + name, (ulint)key_used_on_scan); + } } auto_inc_counter_for_this_stat = 0; @@ -1212,11 +1441,11 @@ innobase_mysql_cmp( ret = my_sortncmp((const char*) a, a_length, (const char*) b, b_length); if (ret < 0) { - return(-1); + return(-1); } else if (ret > 0) { - return(1); + return(1); } else { - return(0); + return(0); } default: assert(0); @@ -1337,12 +1566,14 @@ ha_innobase::store_key_val_for_row( buff += key_part->length; } - /* We have to zero-fill the 'ref' buffer so that MySQL is able to - use a simple memcmp to compare two key values to determine if they are - equal */ + /* + We have to zero-fill the 'ref' buffer so that MySQL is able to + use a simple memcmp to compare two key values to determine if they + are equal + */ + bzero(buff, (ref_length- (uint) (buff - buff_start))); - bzero(buff, (ref_length - (uint) (buff - buff_start))); - DBUG_RETURN(ref_length); + DBUG_RETURN((uint)(buff - buff_start)); } /****************************************************************** @@ -1377,7 +1608,7 @@ build_template( if (prebuilt->read_just_key) { /* MySQL has instructed us that it is enough to fetch the columns in the key */ - + fetch_all_in_key = TRUE; } else { /* We are building a temporary table: fetch all @@ -1386,7 +1617,7 @@ build_template( we use below to detect required columns does not reveal that. Actually, it might be enough to fetch only all in the key also in this case! */ - + templ_type = ROW_MYSQL_WHOLE_ROW; } } @@ -1518,7 +1749,10 @@ ha_innobase::write_row( int error; longlong auto_inc; longlong dummy; - + ibool incremented_auto_inc_for_stat = FALSE; + ibool incremented_auto_inc_counter = FALSE; + ibool skip_auto_inc_decr; + DBUG_ENTER("ha_innobase::write_row"); ut_a(prebuilt->trx == @@ -1567,7 +1801,7 @@ ha_innobase::write_row( /* Fetch the value the user possibly has set in the autoincrement field */ - + auto_inc = table->next_number_field->val_int(); /* In replication and also otherwise the auto-inc column @@ -1588,6 +1822,7 @@ ha_innobase::write_row( assign sequential values from the counter. */ auto_inc_counter_for_this_stat++; + incremented_auto_inc_for_stat = TRUE; auto_inc = auto_inc_counter_for_this_stat; @@ -1595,7 +1830,7 @@ ha_innobase::write_row( auto-inc column */ user_thd->next_insert_id = auto_inc; } - + if (auto_inc != 0) { /* This call will calculate the max of the current value and the value supplied by the user and @@ -1613,12 +1848,12 @@ ha_innobase::write_row( srv_conc_exit_innodb(prebuilt->trx); if (error != DB_SUCCESS) { - + error = convert_error_code_to_mysql(error, - user_thd); + user_thd); goto func_exit; } - + dict_table_autoinc_update(prebuilt->table, auto_inc); } else { srv_conc_enter_innodb(prebuilt->trx); @@ -1628,15 +1863,20 @@ ha_innobase::write_row( error = row_lock_table_autoinc_for_mysql( prebuilt); if (error != DB_SUCCESS) { - srv_conc_exit_innodb(prebuilt->trx); - + srv_conc_exit_innodb(prebuilt->trx); + error = convert_error_code_to_mysql( error, user_thd); goto func_exit; } } + /* The following call gets the value of the auto-inc + counter of the table and increments it by 1 */ + auto_inc = dict_table_autoinc_get(prebuilt->table); + incremented_auto_inc_counter = TRUE; + srv_conc_exit_innodb(prebuilt->trx); /* We can give the new value for MySQL to place in @@ -1673,6 +1913,40 @@ ha_innobase::write_row( srv_conc_exit_innodb(prebuilt->trx); + if (error != DB_SUCCESS) { + /* If the insert did not succeed we restore the value of + the auto-inc counter we used; note that this behavior was + introduced only in version 4.0.4. + NOTE that a REPLACE command handles a duplicate key error + itself, and we must not decrement the autoinc counter + if we are performing a REPLACE statement. + NOTE 2: if there was an error, for example a deadlock, + which caused InnoDB to roll back the whole transaction + already in the call of row_insert_for_mysql(), we may no + longer have the AUTO-INC lock, and cannot decrement + the counter here. */ + + skip_auto_inc_decr = FALSE; + + if (error == DB_DUPLICATE_KEY + && (user_thd->lex.sql_command == SQLCOM_REPLACE + || user_thd->lex.sql_command + == SQLCOM_REPLACE_SELECT)) { + + skip_auto_inc_decr= TRUE; + } + + if (!skip_auto_inc_decr && incremented_auto_inc_counter + && prebuilt->trx->auto_inc_lock) { + dict_table_autoinc_decrement(prebuilt->table); + } + + if (!skip_auto_inc_decr && incremented_auto_inc_for_stat + && prebuilt->trx->auto_inc_lock) { + auto_inc_counter_for_this_stat--; + } + } + prebuilt->trx->ignore_duplicates_in_insert = FALSE; error = convert_error_code_to_mysql(error, user_thd); @@ -2000,11 +2274,21 @@ convert_search_mode_to_innobase( case HA_READ_AFTER_KEY: return(PAGE_CUR_G); case HA_READ_BEFORE_KEY: return(PAGE_CUR_L); case HA_READ_PREFIX: return(PAGE_CUR_GE); - case HA_READ_PREFIX_LAST: return(PAGE_CUR_LE); - /* HA_READ_PREFIX_LAST does not yet work in InnoDB! */ - /* the above PREFIX flags mean that the last - field in the key value may just be a prefix - of the complete fixed length field */ + case HA_READ_PREFIX_LAST: return(PAGE_CUR_LE); + /* In MySQL-4.0 HA_READ_PREFIX and HA_READ_PREFIX_LAST always + pass a complete-field prefix of a key value as the search + tuple. I.e., it is not allowed that the last field would + just contain n first bytes of the full field value. + MySQL uses a 'padding' trick to convert LIKE 'abc%' + type queries so that it can use as a search tuple + a complete-field-prefix of a key value. Thus, the InnoDB + search mode PAGE_CUR_LE_OR_EXTENDS is never used. + TODO: when/if MySQL starts to use also partial-field + prefixes, we have to deal with stripping of spaces + and comparison of non-latin1 char type fields in + innobase_mysql_cmp() to get PAGE_CUR_LE_OR_EXTENDS to + work correctly. */ + default: assert(0); } @@ -2076,7 +2360,10 @@ ha_innobase::index_read( start or end of index; this can also contain an InnoDB row id, in which case key_len is the InnoDB - row id length */ + row id length; the key value can + also be a prefix of a full key value, + and the last column can be a prefix + of a full column */ uint key_len,/* in: key value length */ enum ha_rkey_function find_flag)/* in: search flags from my_base.h */ { @@ -2166,6 +2453,24 @@ ha_innobase::index_read( DBUG_RETURN(error); } +/*********************************************************************** +The following functions works like index_read, but it find the last +row with the current key value or prefix. */ + +int +ha_innobase::index_read_last( +/*=========================*/ + /* out: 0, HA_ERR_KEY_NOT_FOUND, or an + error code */ + mysql_byte* buf, /* out: fetched row */ + const mysql_byte* key_ptr, /* in: key value, or a prefix of a full + key value */ + uint key_len) /* in: length of the key val or prefix + in bytes */ +{ + return(index_read(buf, key_ptr, key_len, HA_READ_PREFIX_LAST)); +} + /************************************************************************ Changes the active index of a handle. */ @@ -2177,44 +2482,46 @@ ha_innobase::change_active_index( index, even if it was internally generated by InnoDB */ { - row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; - KEY* key=0; + row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; + KEY* key=0; + statistic_increment(ha_read_key_count, &LOCK_status); + DBUG_ENTER("change_active_index"); - statistic_increment(ha_read_key_count, &LOCK_status); + ut_a(prebuilt->trx == + (trx_t*) current_thd->transaction.all.innobase_tid); - DBUG_ENTER("change_active_index"); + active_index = keynr; - active_index = keynr; + if (keynr != MAX_KEY && table->keys > 0) { + key = table->key_info + active_index; - if (keynr != MAX_KEY && table->keys > 0) { - key = table->key_info + active_index; + prebuilt->index = dict_table_get_index_noninline( + prebuilt->table, + key->name); + } else { + prebuilt->index = dict_table_get_first_index_noninline( + prebuilt->table); + } - prebuilt->index = dict_table_get_index_noninline( - prebuilt->table, key->name); - } else { - prebuilt->index = dict_table_get_first_index_noninline( - prebuilt->table); - } + if (!prebuilt->index) { + sql_print_error("Innodb could not find key n:o %u with name %s from dict cache for table %s", keynr, key ? key->name : "NULL", prebuilt->table->name); + DBUG_RETURN(1); + } - if (!prebuilt->index) { - sql_print_error("Innodb could not find key n:o %u with name %s from dict cache for table %s", keynr, key ? key->name : "NULL", prebuilt->table->name); - DBUG_RETURN(1); - } - - assert(prebuilt->search_tuple != 0); + assert(prebuilt->search_tuple != 0); - dtuple_set_n_fields(prebuilt->search_tuple, prebuilt->index->n_fields); + dtuple_set_n_fields(prebuilt->search_tuple, prebuilt->index->n_fields); - dict_index_copy_types(prebuilt->search_tuple, prebuilt->index, - prebuilt->index->n_fields); + dict_index_copy_types(prebuilt->search_tuple, prebuilt->index, + prebuilt->index->n_fields); - /* Maybe MySQL changes the active index for a handle also - during some queries, we do not know: then it is safest to build - the template such that all columns will be fetched */ + /* Maybe MySQL changes the active index for a handle also + during some queries, we do not know: then it is safest to build + the template such that all columns will be fetched. */ - build_template(prebuilt, user_thd, table, ROW_MYSQL_WHOLE_ROW); + build_template(prebuilt, NULL, table, ROW_MYSQL_WHOLE_ROW); - DBUG_RETURN(0); + DBUG_RETURN(0); } /************************************************************************** @@ -2265,10 +2572,10 @@ ha_innobase::general_fetch( DBUG_ENTER("general_fetch"); ut_a(prebuilt->trx == - (trx_t*) current_thd->transaction.all.innobase_tid); + (trx_t*) current_thd->transaction.all.innobase_tid); srv_conc_enter_innodb(prebuilt->trx); - + ret = row_search_for_mysql((byte*)buf, 0, prebuilt, match_mode, direction); srv_conc_exit_innodb(prebuilt->trx); @@ -2404,7 +2711,7 @@ ha_innobase::rnd_init( bool scan) /* in: ???????? */ { int err; - + row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; if (prebuilt->clust_index_was_generated) { @@ -2476,8 +2783,9 @@ ha_innobase::rnd_pos( row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; int error; uint keynr = active_index; - DBUG_ENTER("rnd_pos"); + DBUG_DUMP("key", (char*) pos, ref_length); + statistic_increment(ha_read_rnd_count, &LOCK_status); ut_a(prebuilt->trx == @@ -2498,7 +2806,7 @@ ha_innobase::rnd_pos( DBUG_PRINT("error",("Got error: %ld",error)); DBUG_RETURN(error); } - + /* Note that we assume the length of the row reference is fixed for the table, and it is == ref_length */ @@ -2507,7 +2815,6 @@ ha_innobase::rnd_pos( { DBUG_PRINT("error",("Got error: %ld",error)); } - change_active_index(keynr); DBUG_RETURN(error); @@ -2549,10 +2856,15 @@ ha_innobase::position( /* Since we do not store len to the buffer 'ref', we must assume that len is always fixed for this table. The following assertion checks this. */ - - ut_a(len == ref_length); + + if (len != ref_length) { + fprintf(stderr, + "InnoDB: Error: stored ref len is %lu, but table ref len is %lu\n", + (ulint)len, (ulint)ref_length); + } } + /********************************************************************* Creates a table definition to an InnoDB database. */ static @@ -2712,6 +3024,7 @@ ha_innobase::create( { int error; dict_table_t* innobase_table; + trx_t* parent_trx; trx_t* trx; int primary_key_no; uint i; @@ -2723,6 +3036,16 @@ ha_innobase::create( DBUG_ASSERT(thd != NULL); + /* Get the transaction associated with the current thd, or create one + if not yet created */ + + parent_trx = check_trx_exists(current_thd); + + /* In case MySQL calls this in the middle of a SELECT query, release + possible adaptive hash latch to avoid deadlocks of threads */ + + trx_search_latch_release_if_reserved(parent_trx); + trx = trx_allocate_for_mysql(); if (thd->options & OPTION_NO_FOREIGN_KEY_CHECKS) { @@ -2733,15 +3056,21 @@ ha_innobase::create( trx->check_unique_secondary = FALSE; } + if (lower_case_table_names) { + srv_lower_case_table_names = TRUE; + } else { + srv_lower_case_table_names = FALSE; + } + fn_format(name2, name, "", "",2); // Remove the .frm extension normalize_table_name(norm_name, name2); - /* Latch the InnoDB data dictionary exclusive so that no deadlocks + /* Latch the InnoDB data dictionary exclusively so that no deadlocks or lock waits can happen in it during a table create operation. - (Drop table etc. do this latching in row0mysql.c.) */ + Drop table etc. do this latching in row0mysql.c. */ - row_mysql_lock_data_dictionary(); + row_mysql_lock_data_dictionary(trx); /* Create the table definition in InnoDB */ @@ -2750,7 +3079,7 @@ ha_innobase::create( if (error) { innobase_commit_low(trx); - row_mysql_unlock_data_dictionary(); + row_mysql_unlock_data_dictionary(trx); trx_free_for_mysql(trx); @@ -2780,7 +3109,7 @@ ha_innobase::create( if (error) { innobase_commit_low(trx); - row_mysql_unlock_data_dictionary(); + row_mysql_unlock_data_dictionary(trx); trx_free_for_mysql(trx); @@ -2795,7 +3124,7 @@ ha_innobase::create( (uint) primary_key_no))) { innobase_commit_low(trx); - row_mysql_unlock_data_dictionary(); + row_mysql_unlock_data_dictionary(trx); trx_free_for_mysql(trx); @@ -2809,40 +3138,43 @@ ha_innobase::create( if ((error = create_index(trx, form, norm_name, i))) { - innobase_commit_low(trx); + innobase_commit_low(trx); - row_mysql_unlock_data_dictionary(); + row_mysql_unlock_data_dictionary(trx); - trx_free_for_mysql(trx); + trx_free_for_mysql(trx); DBUG_RETURN(error); } } } - error = row_table_add_foreign_constraints(trx, - create_info->create_statement, norm_name); + if (current_thd->query != NULL) { + + error = row_table_add_foreign_constraints(trx, + current_thd->query, norm_name); - error = convert_error_code_to_mysql(error, NULL); + error = convert_error_code_to_mysql(error, NULL); - if (error) { - innobase_commit_low(trx); + if (error) { + innobase_commit_low(trx); - row_mysql_unlock_data_dictionary(); + row_mysql_unlock_data_dictionary(trx); - trx_free_for_mysql(trx); + trx_free_for_mysql(trx); - DBUG_RETURN(error); + DBUG_RETURN(error); + } } innobase_commit_low(trx); - row_mysql_unlock_data_dictionary(); + row_mysql_unlock_data_dictionary(trx); /* Flush the log to reduce probability that the .frm files and the InnoDB data dictionary get out-of-sync if the user runs with innodb_flush_log_at_trx_commit = 0 */ - + log_flush_up_to(ut_dulint_max, LOG_WAIT_ONE_GROUP); innobase_table = dict_table_get(norm_name, NULL); @@ -2874,11 +3206,28 @@ ha_innobase::delete_table( { ulint name_len; int error; + trx_t* parent_trx; trx_t* trx; char norm_name[1000]; DBUG_ENTER("ha_innobase::delete_table"); + /* Get the transaction associated with the current thd, or create one + if not yet created */ + + parent_trx = check_trx_exists(current_thd); + + /* In case MySQL calls this in the middle of a SELECT query, release + possible adaptive hash latch to avoid deadlocks of threads */ + + trx_search_latch_release_if_reserved(parent_trx); + + if (lower_case_table_names) { + srv_lower_case_table_names = TRUE; + } else { + srv_lower_case_table_names = FALSE; + } + trx = trx_allocate_for_mysql(); name_len = strlen(name); @@ -2892,12 +3241,12 @@ ha_innobase::delete_table( /* Drop the table in InnoDB */ - error = row_drop_table_for_mysql(norm_name, trx, FALSE); + error = row_drop_table_for_mysql(norm_name, trx); /* Flush the log to reduce probability that the .frm files and the InnoDB data dictionary get out-of-sync if the user runs with innodb_flush_log_at_trx_commit = 0 */ - + log_flush_up_to(ut_dulint_max, LOG_WAIT_ONE_GROUP); /* Tell the InnoDB server that there might be work for @@ -2927,13 +3276,24 @@ innobase_drop_database( the database name is 'test' */ { ulint len = 0; + trx_t* parent_trx; trx_t* trx; char* ptr; int error; char namebuf[10000]; + + /* Get the transaction associated with the current thd, or create one + if not yet created */ + parent_trx = check_trx_exists(current_thd); + + /* In case MySQL calls this in the middle of a SELECT query, release + possible adaptive hash latch to avoid deadlocks of threads */ + + trx_search_latch_release_if_reserved(parent_trx); + ptr = strend(path) - 2; - + while (ptr >= path && *ptr != '\\' && *ptr != '/') { ptr--; len++; @@ -2944,7 +3304,6 @@ innobase_drop_database( memcpy(namebuf, ptr, len); namebuf[len] = '/'; namebuf[len + 1] = '\0'; - #ifdef __WIN__ casedn_str(namebuf); #endif @@ -2955,7 +3314,7 @@ innobase_drop_database( /* Flush the log to reduce probability that the .frm files and the InnoDB data dictionary get out-of-sync if the user runs with innodb_flush_log_at_trx_commit = 0 */ - + log_flush_up_to(ut_dulint_max, LOG_WAIT_ONE_GROUP); /* Tell the InnoDB server that there might be work for @@ -2984,12 +3343,29 @@ ha_innobase::rename_table( ulint name_len1; ulint name_len2; int error; + trx_t* parent_trx; trx_t* trx; char norm_from[1000]; char norm_to[1000]; DBUG_ENTER("ha_innobase::rename_table"); + /* Get the transaction associated with the current thd, or create one + if not yet created */ + + parent_trx = check_trx_exists(current_thd); + + /* In case MySQL calls this in the middle of a SELECT query, release + possible adaptive hash latch to avoid deadlocks of threads */ + + trx_search_latch_release_if_reserved(parent_trx); + + if (lower_case_table_names) { + srv_lower_case_table_names = TRUE; + } else { + srv_lower_case_table_names = FALSE; + } + trx = trx_allocate_for_mysql(); name_len1 = strlen(from); @@ -3008,7 +3384,7 @@ ha_innobase::rename_table( /* Flush the log to reduce probability that the .frm files and the InnoDB data dictionary get out-of-sync if the user runs with innodb_flush_log_at_trx_commit = 0 */ - + log_flush_up_to(ut_dulint_max, LOG_WAIT_ONE_GROUP); /* Tell the InnoDB server that there might be work for @@ -3030,8 +3406,8 @@ Estimates the number of index records in a range. */ ha_rows ha_innobase::records_in_range( /*==========================*/ - /* out: estimated number of rows, - currently 32-bit int or uint */ + /* out: estimated number of + rows */ int keynr, /* in: index number */ const mysql_byte* start_key, /* in: start key value of the range, may also be empty */ @@ -3062,10 +3438,19 @@ ha_innobase::records_in_range( DBUG_ENTER("records_in_range"); - update_thd(current_thd); + /* We do not know if MySQL can call this function before calling + external_lock(). To be safe, update the thd of the current table + handle. */ + + update_thd(current_thd); + + prebuilt->trx->op_info = (char*)"estimating records in index range"; + + /* In case MySQL calls this in the middle of a SELECT query, release + possible adaptive hash latch to avoid deadlocks of threads */ trx_search_latch_release_if_reserved(prebuilt->trx); - + active_index = keynr; key = table->key_info + active_index; @@ -3098,6 +3483,8 @@ ha_innobase::records_in_range( my_free((char*) key_val_buff2, MYF(0)); + prebuilt->trx->op_info = (char*)""; + DBUG_RETURN((ha_rows) n_rows); } @@ -3116,17 +3503,27 @@ ha_innobase::estimate_number_of_rows(void) row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; dict_index_t* index; ulonglong estimate; - ulonglong data_file_length; + ulonglong local_data_file_length; DBUG_ENTER("estimate_number_of_rows"); - update_thd(current_thd); + /* We do not know if MySQL can call this function before calling + external_lock(). To be safe, update the thd of the current table + handle. */ + + update_thd(current_thd); + + prebuilt->trx->op_info = (char*) + "calculating upper bound for table rows"; + + /* In case MySQL calls this in the middle of a SELECT query, release + possible adaptive hash latch to avoid deadlocks of threads */ trx_search_latch_release_if_reserved(prebuilt->trx); index = dict_table_get_first_index_noninline(prebuilt->table); - - data_file_length = ((ulonglong) index->stat_n_leaf_pages) + + local_data_file_length = ((ulonglong) index->stat_n_leaf_pages) * UNIV_PAGE_SIZE; /* Calculate a minimum length for a clustered index record and from @@ -3135,8 +3532,10 @@ ha_innobase::estimate_number_of_rows(void) by a threshold factor, we must add a safety factor 2 in front of the formula below. */ - estimate = 2 * data_file_length / dict_index_calc_min_rec_len(index); - + estimate = 2 * local_data_file_length / dict_index_calc_min_rec_len(index); + + prebuilt->trx->op_info = (char*)""; + DBUG_RETURN((ha_rows) estimate); } @@ -3172,23 +3571,46 @@ ha_innobase::info( row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; dict_table_t* ib_table; dict_index_t* index; - ulong rec_per_key; + ha_rows rec_per_key; ulong j; ulong i; DBUG_ENTER("info"); - update_thd(current_thd); + /* If we are forcing recovery at a high level, we will suppress + statistics calculation on tables, because that may crash the + server if an index is badly corrupted. */ + + if (srv_force_recovery >= SRV_FORCE_NO_IBUF_MERGE) { + + return; + } + + /* We do not know if MySQL can call this function before calling + external_lock(). To be safe, update the thd of the current table + handle. */ + + update_thd(current_thd); + + /* In case MySQL calls this in the middle of a SELECT query, release + possible adaptive hash latch to avoid deadlocks of threads */ + + prebuilt->trx->op_info = (char*)"returning various info to MySQL"; trx_search_latch_release_if_reserved(prebuilt->trx); - + ib_table = prebuilt->table; if (flag & HA_STATUS_TIME) { /* In sql_show we call with this flag: update then statistics so that they are up-to-date */ + prebuilt->trx->op_info = (char*)"updating table statistics"; + dict_update_statistics(ib_table); + + prebuilt->trx->op_info = (char*) + "returning various info to MySQL"; } if (flag & HA_STATUS_VARIABLE) { @@ -3224,7 +3646,7 @@ ha_innobase::info( rec_per_key = records; } else { - rec_per_key = (ulong)(records / + rec_per_key = (ha_rows)(records / index->stat_n_diff_key_vals[j + 1]); } @@ -3238,21 +3660,16 @@ ha_innobase::info( if (rec_per_key == 0) { rec_per_key = 1; } - - table->key_info[i].rec_per_key[j] - = rec_per_key; + + table->key_info[i].rec_per_key[j]= + rec_per_key >= ~(ulong) 0 ? ~(ulong) 0 : + rec_per_key; } - + index = dict_table_get_next_index_noninline(index); } } - /* The trx struct in InnoDB contains a pthread mutex embedded: - in the debug version of MySQL that it replaced by a 'safe mutex' - which is of a different size. We have to use a function to access - trx fields. Otherwise trx->error_info will be a random - pointer and cause a seg fault. */ - if (flag & HA_STATUS_ERRKEY) { ut_a(prebuilt->trx && prebuilt->trx->magic_n == TRX_MAGIC_N); @@ -3261,6 +3678,8 @@ ha_innobase::info( trx_get_error_info(prebuilt->trx)); } + prebuilt->trx->op_info = (char*)""; + DBUG_VOID_RETURN; } @@ -3280,7 +3699,7 @@ ha_innobase::check( { row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; ulint ret; - + ut_a(prebuilt->trx && prebuilt->trx->magic_n == TRX_MAGIC_N); ut_a(prebuilt->trx == (trx_t*) current_thd->transaction.all.innobase_tid); @@ -3297,7 +3716,7 @@ ha_innobase::check( if (ret == DB_SUCCESS) { return(HA_ADMIN_OK); } - + return(HA_ADMIN_CORRUPT); } @@ -3315,14 +3734,25 @@ ha_innobase::update_table_comment( { row_prebuilt_t* prebuilt = (row_prebuilt_t*)innobase_prebuilt; uint length = strlen(comment); - char* str = my_malloc(length + 550, MYF(0)); + char* str = my_malloc(length + 16500, MYF(0)); char* pos; - /* Warning: since it is not sure that MySQL calls external_lock - before calling this function, the trx field in prebuilt can be - obsolete! */ + /* We do not know if MySQL can call this function before calling + external_lock(). To be safe, update the thd of the current table + handle. */ + + update_thd(current_thd); + + prebuilt->trx->op_info = (char*)"returning table comment"; + + /* In case MySQL calls this in the middle of a SELECT query, release + possible adaptive hash latch to avoid deadlocks of threads */ + + trx_search_latch_release_if_reserved(prebuilt->trx); if (!str) { + prebuilt->trx->op_info = (char*)""; + return((char*)comment); } @@ -3337,13 +3767,17 @@ ha_innobase::update_table_comment( (pos,"InnoDB free: %lu kB", (ulong) innobase_get_free_space())); - /* We assume 450 - length bytes of space to print info */ - - if (length < 450) { - dict_print_info_on_foreign_keys(FALSE, pos, 450 - length, + /* We assume 16000 - length bytes of space to print info; the limit + 16000 bytes is arbitrary, and MySQL could handle at least 64000 + bytes */ + + if (length < 16000) { + dict_print_info_on_foreign_keys(FALSE, pos, 16000 - length, prebuilt->table); } - + + prebuilt->trx->op_info = (char*)""; + return(str); } @@ -3359,20 +3793,30 @@ ha_innobase::get_foreign_key_create_info(void) { row_prebuilt_t* prebuilt = (row_prebuilt_t*)innobase_prebuilt; char* str; - - if (prebuilt == NULL) { - fprintf(stderr, -"InnoDB: Error: cannot get create info for foreign keys\n"); - return(NULL); - } + ut_a(prebuilt != NULL); + + /* We do not know if MySQL can call this function before calling + external_lock(). To be safe, update the thd of the current table + handle. */ + + update_thd(current_thd); + + prebuilt->trx->op_info = (char*)"getting info on foreign keys"; + /* In case MySQL calls this in the middle of a SELECT query, release + possible adaptive hash latch to avoid deadlocks of threads */ + + trx_search_latch_release_if_reserved(prebuilt->trx); + str = (char*)ut_malloc(10000); str[0] = '\0'; dict_print_info_on_foreign_keys(TRUE, str, 9000, prebuilt->table); + prebuilt->trx->op_info = (char*)""; + return(str); } @@ -3437,51 +3881,78 @@ ha_innobase::reset(void) return(0); } - /********************************************************************** -When we create a temporary table inside MySQL LOCK TABLES, MySQL will -not call external_lock for the temporary table when it uses it. Instead, -it will call this function. */ +Inside LOCK TABLES MySQL will not call external_lock() between SQL +statements. It will call this function at the start of each SQL statement. +Note also a spacial case: if a temporary table is created inside LOCK +TABLES, MySQL has not called external_lock() at all on that table. */ int ha_innobase::start_stmt( /*====================*/ /* out: 0 or error code */ - THD* thd) /* in: handle to the user thread */ + THD* thd) /* in: handle to the user thread */ { - row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; - trx_t* trx; + row_prebuilt_t* prebuilt = (row_prebuilt_t*) innobase_prebuilt; + trx_t* trx; - update_thd(thd); + update_thd(thd); - trx = prebuilt->trx; + trx = prebuilt->trx; - innobase_release_stat_resources(trx); - trx_mark_sql_stat_end(trx); + innobase_release_stat_resources(trx); + trx_mark_sql_stat_end(trx); - auto_inc_counter_for_this_stat = 0; - prebuilt->sql_stat_start = TRUE; - prebuilt->hint_no_need_to_fetch_extra_cols = TRUE; - prebuilt->read_just_key = 0; + if (trx->isolation_level <= TRX_ISO_READ_COMMITTED + && trx->read_view) { + /* At low transaction isolation levels we let + each consistent read set its own snapshot */ - if (!prebuilt->mysql_has_locked) { - /* This handle is for a temporary table created inside - this same LOCK TABLES; since MySQL does NOT call external_lock - in this case, we must use x-row locks inside InnoDB to be - prepared for an update of a row */ + read_view_close_for_mysql(trx); + } - prebuilt->select_lock_type = LOCK_X; - } + auto_inc_counter_for_this_stat = 0; + prebuilt->sql_stat_start = TRUE; + prebuilt->hint_no_need_to_fetch_extra_cols = TRUE; + prebuilt->read_just_key = 0; + + if (!prebuilt->mysql_has_locked) { + /* This handle is for a temporary table created inside + this same LOCK TABLES; since MySQL does NOT call external_lock + in this case, we must use x-row locks inside InnoDB to be + prepared for an update of a row */ + + prebuilt->select_lock_type = LOCK_X; + } - thd->transaction.all.innodb_active_trans = 1; + thd->transaction.all.innodb_active_trans = 1; - return(0); + return(0); } /********************************************************************** +Maps a MySQL trx isolation level code to the InnoDB isolation level code */ +inline +ulint +innobase_map_isolation_level( +/*=========================*/ + /* out: InnoDB isolation level */ + enum_tx_isolation iso) /* in: MySQL isolation level code */ +{ + switch(iso) { + case ISO_REPEATABLE_READ: return(TRX_ISO_REPEATABLE_READ); + case ISO_READ_COMMITTED: return(TRX_ISO_READ_COMMITTED); + case ISO_SERIALIZABLE: return(TRX_ISO_SERIALIZABLE); + case ISO_READ_UNCOMMITTED: return(TRX_ISO_READ_UNCOMMITTED); + default: ut_a(0); return(0); + } +} + +/********************************************************************** As MySQL will execute an external lock for every new table it uses when it -starts to process an SQL statement, we can use this function to store the -pointer to the THD in the handle. We will also use this function to communicate +starts to process an SQL statement (an exception is when MySQL calls +start_stmt for the handle) we can use this function to store the pointer to +the THD in the handle. We will also use this function to communicate to InnoDB that a new SQL statement has started and that we must store a savepoint to our transaction handle, so that we are able to roll back the SQL statement in case of an error. */ @@ -3489,6 +3960,7 @@ the SQL statement in case of an error. */ int ha_innobase::external_lock( /*=======================*/ + /* out: 0 or error code */ THD* thd, /* in: handle to the user thread */ int lock_type) /* in: lock type */ { @@ -3497,6 +3969,7 @@ ha_innobase::external_lock( trx_t* trx; DBUG_ENTER("ha_innobase::external_lock"); + DBUG_PRINT("enter",("lock_type: %d", lock_type)); update_thd(thd); @@ -3523,7 +3996,11 @@ ha_innobase::external_lock( trx->n_mysql_tables_in_use++; prebuilt->mysql_has_locked = TRUE; - if (thd->tx_isolation == ISO_SERIALIZABLE + trx->isolation_level = innobase_map_isolation_level( + (enum_tx_isolation) + thd->variables.tx_isolation); + + if (trx->isolation_level == TRX_ISO_SERIALIZABLE && prebuilt->select_lock_type == LOCK_NONE) { /* To get serializable execution we let InnoDB @@ -3546,16 +4023,24 @@ ha_innobase::external_lock( trx->mysql_n_tables_locked = 0; - /* Here we release the search latch, auto_inc_lock, - and InnoDB thread FIFO ticket if they were reserved. */ + /* Here we release the search latch and InnoDB + thread FIFO ticket if they were reserved. */ innobase_release_stat_resources(trx); + if (trx->isolation_level <= TRX_ISO_READ_COMMITTED + && trx->read_view) { + + /* At low transaction isolation levels we let + each consistent read set its own snapshot */ + + read_view_close_for_mysql(trx); + } + if (!(thd->options - & (OPTION_NOT_AUTO_COMMIT | OPTION_BEGIN))) { + & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))) { innobase_commit(thd, trx); - thd->transaction.all.innodb_active_trans=0; } } } @@ -3576,27 +4061,29 @@ innodb_show_status( char* buf; DBUG_ENTER("innodb_show_status"); - + if (innodb_skip) { + my_message(ER_NOT_SUPPORTED_YET, + "Cannot call SHOW INNODB STATUS because skip-innodb is defined", + MYF(0)); + DBUG_RETURN(-1); + } - fprintf(stderr, - "Cannot call SHOW INNODB STATUS because skip-innodb is defined\n"); - - DBUG_RETURN(-1); - } - - /* We let the InnoDB Monitor to output at most 100 kB of text, add + /* We let the InnoDB Monitor to output at most 200 kB of text, add a safety margin of 10 kB for buffer overruns */ - buf = (char*)ut_malloc(110 * 1024); + buf = (char*)ut_malloc(210 * 1024); - srv_sprintf_innodb_monitor(buf, 100 * 1024); + srv_sprintf_innodb_monitor(buf, 200 * 1024); List<Item> field_list; field_list.push_back(new Item_empty_string("Status", strlen(buf))); if(send_fields(thd, field_list, 1)) { + + ut_free(buf); + DBUG_RETURN(-1); } @@ -3674,7 +4161,13 @@ static void free_share(INNOBASE_SHARE *share) } /********************************************************************* -Stores a MySQL lock into a 'lock' field in a handle. */ +Converts a MySQL table lock stored in the 'lock' field of the handle to +a proper type before storing pointer to the lock into an array of pointers. +MySQL also calls this if it wants to reset some table locks to a not-locked +state during the processing of an SQL query. An example is that during a +SELECT the read lock is released early on the 'const' tables where we only +fetch one row. MySQL does not call this when it releases all locks at the +end of an SQL statement. */ THR_LOCK_DATA** ha_innobase::store_lock( @@ -3701,7 +4194,12 @@ ha_innobase::store_lock( prebuilt->select_lock_type = LOCK_S; } else if (lock_type != TL_IGNORE) { - /* We set possible LOCK_X value in external_lock, not yet + + /* In ha_berkeley.cc there is a comment that MySQL + may in exceptional cases call this with TL_IGNORE also + when it is NOT going to release the lock. */ + + /* We set possible LOCK_X value in external_lock, not yet here even if this would be SELECT ... FOR UPDATE */ prebuilt->select_lock_type = LOCK_NONE; @@ -3727,7 +4225,7 @@ ha_innobase::store_lock( if (lock_type == TL_READ_NO_INSERT && !thd->in_lock_tables) { lock_type = TL_READ; } - + lock.type=lock_type; } @@ -3863,3 +4361,4 @@ ha_innobase::get_auto_increment() } #endif /* HAVE_INNOBASE_DB */ + diff --git a/sql/ha_innobase.h b/sql/ha_innodb.h index 4fd42d08390..8031fa0aa29 100644 --- a/sql/ha_innobase.h +++ b/sql/ha_innodb.h @@ -1,7 +1,4 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - && Innobase Oy - - -This file is modified from ha_berkeley.h of MySQL distribution- +/* Copyright (C) 2000 MySQL AB && Innobase Oy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,13 +14,17 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* + This file is based on ha_berkeley.h of MySQL distribution + + This file defines the Innodb handler: the interface between MySQL and + Innodb +*/ + #ifdef __GNUC__ #pragma interface /* gcc class implementation */ #endif -/* This file defines the Innobase handler: the interface between MySQL and -Innobase */ - typedef struct st_innobase_share { THR_LOCK lock; pthread_mutex_t mutex; @@ -32,11 +33,11 @@ typedef struct st_innobase_share { } INNOBASE_SHARE; -/* The class defining a handle to an Innobase table */ +/* The class defining a handle to an Innodb table */ class ha_innobase: public handler { void* innobase_prebuilt; /* (row_prebuilt_t*) prebuilt - struct in Innobase, used to save + struct in Innodb, used to save CPU */ THD* user_thd; /* the thread handle of the user currently using the handle; this is @@ -50,8 +51,8 @@ class ha_innobase: public handler byte* upd_buff; /* buffer used in updates */ byte* key_val_buff; /* buffer used in converting search key values from MySQL format - to Innobase format */ - ulong int_option_flag; + to Innodb format */ + ulong int_table_flags; uint primary_key; uint last_dup_key; ulong start_of_scan; /* this is set to 1 when we are @@ -73,16 +74,15 @@ class ha_innobase: public handler /* Init values for the class: */ public: ha_innobase(TABLE *table): handler(table), - int_option_flag(HA_READ_NEXT | HA_READ_PREV | HA_READ_ORDER | - HA_REC_NOT_IN_SEQ | + int_table_flags(HA_REC_NOT_IN_SEQ | HA_KEYPOS_TO_RNDPOS | HA_LASTKEY_ORDER | - HA_HAVE_KEY_READ_ONLY | HA_READ_NOT_EXACT_KEY | - HA_LONGLONG_KEYS | HA_NULL_KEY | - HA_NOT_EXACT_COUNT | HA_NO_FULLTEXT_KEY | + HA_NULL_KEY | HA_CAN_SQL_HANDLER | + HA_NOT_EXACT_COUNT | HA_NO_WRITE_DELAYED | HA_PRIMARY_KEY_IN_READ_INDEX | HA_DROP_BEFORE_CREATE | - HA_NO_PREFIX_CHAR_KEYS), + HA_NO_PREFIX_CHAR_KEYS | + HA_TABLE_SCAN_ON_INDEX), last_dup_key((uint) -1), start_of_scan(0) { @@ -90,8 +90,14 @@ class ha_innobase: public handler ~ha_innobase() {} const char* table_type() const { return("InnoDB");} + const char *index_type(uint key_number) { return "BTREE"; } const char** bas_ext() const; - ulong option_flag() const { return int_option_flag; } + ulong table_flags() const { return int_table_flags; } + ulong index_flags(uint idx) const + { + return (HA_READ_NEXT | HA_READ_PREV | HA_READ_ORDER | + HA_KEY_READ_ONLY); + } uint max_record_length() const { return HA_MAX_REC_LENGTH; } uint max_keys() const { return MAX_KEY; } uint max_key_parts() const { return MAX_REF_PARTS; } @@ -120,9 +126,10 @@ class ha_innobase: public handler int index_init(uint index); int index_end(); int index_read(byte * buf, const byte * key, - uint key_len, enum ha_rkey_function find_flag); + uint key_len, enum ha_rkey_function find_flag); int index_read_idx(byte * buf, uint index, const byte * key, - uint key_len, enum ha_rkey_function find_flag); + uint key_len, enum ha_rkey_function find_flag); + int index_read_last(byte * buf, const byte * key, uint key_len); int index_next(byte * buf); int index_next_same(byte * buf, const byte *key, uint keylen); int index_prev(byte * buf); @@ -159,14 +166,13 @@ class ha_innobase: public handler void free_foreign_key_create_info(char* str); THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to, enum thr_lock_type lock_type); - /* void init_table_handle_for_HANDLER(); Not tested or used yet, code - included for documentational purposes only */ + void init_table_handle_for_HANDLER(); longlong get_auto_increment(); }; extern bool innodb_skip; -extern SHOW_COMP_OPTION have_innodb; extern uint innobase_init_flags, innobase_lock_type; +extern uint innobase_flush_log_at_trx_commit; extern ulong innobase_cache_size; extern char *innobase_home, *innobase_tmpdir, *innobase_logdir; extern long innobase_lock_scan_time; @@ -178,7 +184,6 @@ extern long innobase_force_recovery, innobase_thread_concurrency; extern char *innobase_data_home_dir, *innobase_data_file_path; extern char *innobase_log_group_home_dir, *innobase_log_arch_dir; extern char *innobase_unix_file_flush_method; -extern long innobase_flush_log_at_trx_commit; /* The following variables have to be my_bool for SHOW VARIABLES to work */ extern my_bool innobase_log_archive, innobase_use_native_aio, innobase_fast_shutdown; @@ -201,3 +206,6 @@ int innobase_close_connection(THD *thd); int innobase_drop_database(char *path); int innodb_show_status(THD* thd); +my_bool innobase_query_caching_of_table_permitted(THD* thd, char* full_name, + uint full_name_len); +void innobase_release_temporary_latches(void* innobase_tid); diff --git a/sql/ha_isam.cc b/sql/ha_isam.cc index ac37d09e6b4..7371628890f 100644 --- a/sql/ha_isam.cc +++ b/sql/ha_isam.cc @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -20,6 +20,7 @@ #endif #include "mysql_priv.h" +#ifdef HAVE_ISAM #include <m_ctype.h> #include <myisampack.h> #include "ha_isam.h" @@ -34,7 +35,7 @@ *****************************************************************************/ const char **ha_isam::bas_ext() const -{ static const char *ext[]= { ".ISD",".ISM", NullS }; return ext; } +{ static const char *ext[]= { ".ISM",".ISD", NullS }; return ext; } int ha_isam::open(const char *name, int mode, uint test_if_locked) @@ -51,7 +52,7 @@ int ha_isam::open(const char *name, int mode, uint test_if_locked) if (!(test_if_locked & HA_OPEN_WAIT_IF_LOCKED)) (void) nisam_extra(file,HA_EXTRA_WAIT_LOCK); if (!table->db_record_offset) - int_option_flag|=HA_REC_NOT_IN_SEQ; + int_table_flags|=HA_REC_NOT_IN_SEQ; return (0); } @@ -108,6 +109,15 @@ int ha_isam::index_read_idx(byte * buf, uint index, const byte * key, return !error ? 0 : my_errno ? my_errno : -1; } +int ha_isam::index_read_last(byte * buf, const byte * key, uint key_len) +{ + statistic_increment(ha_read_key_count,&LOCK_status); + int error=nisam_rkey(file, buf, active_index, key, key_len, + HA_READ_PREFIX_LAST); + table->status=error ? STATUS_NOT_FOUND: 0; + return !error ? 0 : my_errno ? my_errno : -1; +} + int ha_isam::index_next(byte * buf) { statistic_increment(ha_read_next_count,&LOCK_status); @@ -123,7 +133,7 @@ int ha_isam::index_prev(byte * buf) table->status=error ? STATUS_NOT_FOUND: 0; return !error ? 0 : my_errno ? my_errno : HA_ERR_END_OF_FILE; } - + int ha_isam::index_first(byte * buf) { statistic_increment(ha_read_first_count,&LOCK_status); @@ -234,8 +244,10 @@ int ha_isam::reset(void) int ha_isam::external_lock(THD *thd, int lock_type) { - return nisam_lock_database(file,lock_type); -} + if (!table->tmp_table) + return nisam_lock_database(file,lock_type); + return 0; +} THR_LOCK_DATA **ha_isam::store_lock(THD *thd, @@ -315,7 +327,7 @@ int ha_isam::create(const char *name, register TABLE *form, { /* skip null fields */ if (!(temp_length= (*field)->pack_length())) - continue; /* Skipp null-fields */ + continue; /* Skip null-fields */ if (! found || fieldpos < minpos || (fieldpos == minpos && temp_length < length)) { @@ -343,15 +355,15 @@ int ha_isam::create(const char *name, register TABLE *form, else if (!(options & HA_OPTION_PACK_RECORD)) recinfo_pos->base.type= (int) FIELD_NORMAL; else if (found->zero_pack()) - recinfo_pos->base.type= (int) FIELD_SKIPP_ZERO; + recinfo_pos->base.type= (int) FIELD_SKIP_ZERO; else recinfo_pos->base.type= (int) ((length <= 3 || (found->flags & ZEROFILL_FLAG)) ? FIELD_NORMAL : found->type() == FIELD_TYPE_STRING || found->type() == FIELD_TYPE_VAR_STRING ? - FIELD_SKIPP_ENDSPACE : - FIELD_SKIPP_PRESPACE); + FIELD_SKIP_ENDSPACE : + FIELD_SKIP_PRESPACE); recinfo_pos++ ->base.length=(uint16) length; recpos=minpos+length; DBUG_PRINT("loop",("length: %d type: %d", @@ -368,7 +380,8 @@ int ha_isam::create(const char *name, register TABLE *form, } recinfo_pos->base.type= (int) FIELD_LAST; /* End of fieldinfo */ error=nisam_create(fn_format(buff,name,"","",2+4+16),form->keys,keydef, - recinfo,form->max_rows,form->min_rows,0,0,0L); + recinfo,(ulong) form->max_rows, (ulong) form->min_rows, + 0, 0, 0L); my_free((gptr) recinfo,MYF(0)); DBUG_RETURN(error); @@ -388,3 +401,4 @@ ha_rows ha_isam::records_in_range(int inx, end_key,end_key_len, end_search_flag); } +#endif /* HAVE_ISAM */ diff --git a/sql/ha_isam.h b/sql/ha_isam.h index 5e01edcf889..82a243ef5c0 100644 --- a/sql/ha_isam.h +++ b/sql/ha_isam.h @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -26,20 +26,21 @@ class ha_isam: public handler { N_INFO *file; - uint int_option_flag; + /* We need this as table_flags() may change after open() */ + ulong int_table_flags; public: - ha_isam(TABLE *table): handler(table), file(0), - int_option_flag(HA_READ_NEXT | HA_READ_PREV | HA_READ_RND_SAME | - HA_KEYPOS_TO_RNDPOS | HA_READ_ORDER | HA_LASTKEY_ORDER | - HA_HAVE_KEY_READ_ONLY | HA_READ_NOT_EXACT_KEY | - HA_LONGLONG_KEYS | HA_KEY_READ_WRONG_STR | HA_DUPP_POS | - HA_NOT_DELETE_WITH_CACHE | HA_NO_FULLTEXT_KEY) - {} + ha_isam(TABLE *table) + :handler(table), file(0), + int_table_flags(HA_READ_RND_SAME | HA_KEYPOS_TO_RNDPOS | HA_LASTKEY_ORDER | + HA_KEY_READ_WRONG_STR | HA_DUPP_POS | + HA_NOT_DELETE_WITH_CACHE) + {} ~ha_isam() {} const char *table_type() const { return "ISAM"; } + const char *index_type(uint key_number) { return "BTREE"; } const char **bas_ext() const; - ulong option_flag() const { return int_option_flag; } + ulong table_flags() const { return int_table_flags; } uint max_record_length() const { return HA_MAX_REC_LENGTH; } uint max_keys() const { return N_MAXKEY; } uint max_key_parts() const { return N_MAXKEY_SEG; } @@ -56,6 +57,7 @@ class ha_isam: public handler uint key_len, enum ha_rkey_function find_flag); int index_read_idx(byte * buf, uint idx, const byte * key, uint key_len, enum ha_rkey_function find_flag); + int index_read_last(byte * buf, const byte * key, uint key_len); int index_next(byte * buf); int index_prev(byte * buf); int index_first(byte * buf); @@ -78,11 +80,4 @@ class ha_isam: public handler int create(const char *name, TABLE *form, HA_CREATE_INFO *create_info); THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to, enum thr_lock_type lock_type); - }; - - - - - - diff --git a/sql/ha_isammrg.cc b/sql/ha_isammrg.cc index dd2e4e2f723..94e394e7665 100644 --- a/sql/ha_isammrg.cc +++ b/sql/ha_isammrg.cc @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -20,11 +20,12 @@ #endif #include "mysql_priv.h" +#ifdef HAVE_ISAM #include <m_ctype.h> #ifndef MASTER -#include "../srclib/merge/mrgdef.h" +#include "../srclib/merge/mrg_def.h" #else -#include "../merge/mrgdef.h" +#include "../merge/mrg_def.h" #endif #include "ha_isammrg.h" @@ -109,7 +110,7 @@ int ha_isammrg::index_prev(byte * buf) { return (my_errno=HA_ERR_WRONG_COMMAND); } - + int ha_isammrg::index_first(byte * buf) { return (my_errno=HA_ERR_WRONG_COMMAND); @@ -178,7 +179,7 @@ int ha_isammrg::reset(void) int ha_isammrg::external_lock(THD *thd, int lock_type) { return !mrg_lock_database(file,lock_type) ? 0 : my_errno ? my_errno : -1; -} +} uint ha_isammrg::lock_count(void) const { @@ -189,13 +190,15 @@ THR_LOCK_DATA **ha_isammrg::store_lock(THD *thd, THR_LOCK_DATA **to, enum thr_lock_type lock_type) { - MRG_TABLE *table; + MRG_TABLE *open_table; - for (table=file->open_tables ; table != file->end_table ; table++) + for (open_table=file->open_tables ; + open_table != file->end_table ; + open_table++) { - *(to++)= &table->table->lock; - if (lock_type != TL_IGNORE && table->table->lock.type == TL_UNLOCK) - table->table->lock.type=lock_type; + *(to++)= &open_table->table->lock; + if (lock_type != TL_IGNORE && open_table->table->lock.type == TL_UNLOCK) + open_table->table->lock.type=lock_type; } return to; } @@ -208,3 +211,4 @@ int ha_isammrg::create(const char *name, register TABLE *form, char buff[FN_REFLEN]; return mrg_create(fn_format(buff,name,"","",2+4+16),0); } +#endif /* HAVE_ISAM */ diff --git a/sql/ha_isammrg.h b/sql/ha_isammrg.h index c8eb7dd9f69..c936a15164a 100644 --- a/sql/ha_isammrg.h +++ b/sql/ha_isammrg.h @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -32,8 +32,10 @@ class ha_isammrg: public handler ~ha_isammrg() {} const char *table_type() const { return "MRG_ISAM"; } const char **bas_ext() const; - ulong option_flag() const { return HA_READ_RND_SAME | HA_KEYPOS_TO_RNDPOS - | HA_REC_NOT_IN_SEQ | HA_NO_FULLTEXT_KEY;} + ulong table_flags() const { return (HA_READ_RND_SAME | HA_KEYPOS_TO_RNDPOS | + HA_REC_NOT_IN_SEQ); } + ulong index_flags(uint idx) const { return HA_NOT_READ_PREFIX_LAST; } + uint max_record_length() const { return HA_MAX_REC_LENGTH; } uint max_keys() const { return 0; } uint max_key_parts() const { return 0; } diff --git a/sql/ha_myisam.cc b/sql/ha_myisam.cc index 31a8d3c7109..6e055f57c83 100644 --- a/sql/ha_myisam.cc +++ b/sql/ha_myisam.cc @@ -30,13 +30,12 @@ #include "../myisam/myisamdef.h" #endif -ulong myisam_sort_buffer_size; ulong myisam_recover_options= HA_RECOVER_NONE; /* bits in myisam_recover_options */ const char *myisam_recover_names[] = { "DEFAULT", "BACKUP", "FORCE", "QUICK", NullS}; -TYPELIB myisam_recover_typelib= {array_elements(myisam_recover_names),"", +TYPELIB myisam_recover_typelib= {array_elements(myisam_recover_names)-1,"", myisam_recover_names}; @@ -45,6 +44,7 @@ TYPELIB myisam_recover_typelib= {array_elements(myisam_recover_names),"", *****************************************************************************/ // collect errors printed by mi_check routines + static void mi_check_print_msg(MI_CHECK *param, const char* msg_type, const char *fmt, va_list args) { @@ -66,8 +66,7 @@ static void mi_check_print_msg(MI_CHECK *param, const char* msg_type, sql_print_error(msgbuf); return; } - if (param->testflag & (T_CREATE_MISSING_KEYS | T_SAFE_REPAIR | - T_AUTO_REPAIR)) + if (param->testflag & (T_CREATE_MISSING_KEYS | T_SAFE_REPAIR | T_AUTO_REPAIR)) { my_message(ER_NOT_KEYFILE,msgbuf,MYF(MY_WME)); return; @@ -80,9 +79,8 @@ static void mi_check_print_msg(MI_CHECK *param, const char* msg_type, net_store_data(packet, msgbuf); if (my_net_write(&thd->net, (char*)thd->packet.ptr(), thd->packet.length())) - fprintf(stderr, - "Failed on my_net_write, writing to stderr instead: %s\n", - msgbuf); + sql_print_error("Failed on my_net_write, writing to stderr instead: %s\n", + msgbuf); return; } @@ -119,18 +117,25 @@ void mi_check_print_warning(MI_CHECK *param, const char *fmt,...) } const char **ha_myisam::bas_ext() const -{ static const char *ext[]= { ".MYD",".MYI", NullS }; return ext; } +{ static const char *ext[]= { ".MYI",".MYD", NullS }; return ext; } +const char *ha_myisam::index_type(uint key_number) +{ + return ((table->key_info[key_number].flags & HA_FULLTEXT) ? + "FULLTEXT" : + "BTREE"); +} + int ha_myisam::net_read_dump(NET* net) { int data_fd = file->dfile; int error = 0; my_seek(data_fd, 0L, MY_SEEK_SET, MYF(MY_WME)); - for(;;) + for (;;) { - uint packet_len = my_net_read(net); + ulong packet_len = my_net_read(net); if (!packet_len) break ; // end of file if (packet_len == packet_error) @@ -139,7 +144,7 @@ int ha_myisam::net_read_dump(NET* net) error= -1; goto err; } - if (my_write(data_fd, (byte*)net->read_pos, packet_len, + if (my_write(data_fd, (byte*)net->read_pos, (uint) packet_len, MYF(MY_WME|MY_FNABP))) { error = errno; @@ -165,7 +170,7 @@ int ha_myisam::dump(THD* thd, int fd) int error = 0; my_seek(data_fd, 0L, MY_SEEK_SET, MYF(MY_WME)); - for(; bytes_to_read > 0;) + for (; bytes_to_read > 0;) { uint bytes = my_read(data_fd, buf, blocksize, MYF(MY_WME)); if (bytes == MY_FILE_ERROR) @@ -212,12 +217,12 @@ int ha_myisam::open(const char *name, int mode, uint test_if_locked) return (my_errno ? my_errno : -1); if (test_if_locked & (HA_OPEN_IGNORE_IF_LOCKED | HA_OPEN_TMP_TABLE)) - VOID(mi_extra(file,HA_EXTRA_NO_WAIT_LOCK)); + VOID(mi_extra(file, HA_EXTRA_NO_WAIT_LOCK, 0)); info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST); if (!(test_if_locked & HA_OPEN_WAIT_IF_LOCKED)) - VOID(mi_extra(file,HA_EXTRA_WAIT_LOCK)); + VOID(mi_extra(file, HA_EXTRA_WAIT_LOCK, 0)); if (!table->db_record_offset) - int_option_flag|=HA_REC_NOT_IN_SEQ; + int_table_flags|=HA_REC_NOT_IN_SEQ; return (0); } @@ -283,7 +288,7 @@ int ha_myisam::check(THD* thd, HA_CHECK_OPT* check_opt) error = chk_key(¶m, file); if (!error) { - if ((!check_opt->quick && + if ((!(param.testflag & T_QUICK) && ((share->options & (HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD)) || (param.testflag & (T_EXTEND | T_MEDIUM)))) || @@ -324,7 +329,6 @@ int ha_myisam::check(THD* thd, HA_CHECK_OPT* check_opt) mi_mark_crashed(file); file->update |= HA_STATE_CHANGED | HA_STATE_ROW_CHANGED; } - check_opt->retry_without_quick=param.retry_without_quick; thd->proc_info=old_proc_info; return error ? HA_ADMIN_CORRUPT : HA_ADMIN_OK; @@ -374,14 +378,14 @@ int ha_myisam::restore(THD* thd, HA_CHECK_OPT *check_opt) char* backup_dir = thd->lex.backup_dir; char src_path[FN_REFLEN], dst_path[FN_REFLEN]; char* table_name = table->real_name; + int error; + const char* errmsg; DBUG_ENTER("restore"); - if (!fn_format(src_path, table_name, backup_dir, MI_NAME_DEXT, 4 + 64)) + if (fn_format_relative_to_data_home(src_path, table_name, backup_dir, + MI_NAME_DEXT)) DBUG_RETURN(HA_ADMIN_INVALID); - int error = 0; - const char* errmsg = ""; - if (my_copy(src_path, fn_format(dst_path, table->path, "", MI_NAME_DEXT, 4), MYF(MY_WME))) { @@ -391,8 +395,7 @@ int ha_myisam::restore(THD* thd, HA_CHECK_OPT *check_opt) } tmp_check_opt.init(); - tmp_check_opt.quick = 1; - tmp_check_opt.flags |= T_VERY_SILENT | T_CALC_CHECKSUM; + tmp_check_opt.flags |= T_VERY_SILENT | T_CALC_CHECKSUM | T_QUICK; DBUG_RETURN(repair(thd, &tmp_check_opt)); err: @@ -404,7 +407,7 @@ int ha_myisam::restore(THD* thd, HA_CHECK_OPT *check_opt) param.db_name = table->table_cache_key; param.table_name = table->table_name; param.testflag = 0; - mi_check_print_error(¶m,errmsg, errno ); + mi_check_print_error(¶m,errmsg, my_errno); DBUG_RETURN(error); } } @@ -415,41 +418,47 @@ int ha_myisam::backup(THD* thd, HA_CHECK_OPT *check_opt) 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; - const char* errmsg = ""; - - 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; - goto err; - } - - if (my_copy(fn_format(src_path, table->path,"", reg_ext, 4), - dst_path, - MYF(MY_WME | MY_HOLD_ORIGINAL_MODES ))) + int error; + const char *errmsg; + DBUG_ENTER("ha_myisam::backup"); + + if (fn_format_relative_to_data_home(dst_path, table_name, backup_dir, + reg_ext)) + { + errmsg = "Failed in fn_format() for .frm file: errno = %d"; + error = HA_ADMIN_INVALID; + goto err; + } + + if (my_copy(fn_format(src_path, table->path,"", reg_ext, MY_UNPACK_FILENAME), + dst_path, + MYF(MY_WME | MY_HOLD_ORIGINAL_MODES))) { error = HA_ADMIN_FAILED; errmsg = "Failed copying .frm file: errno = %d"; goto err; } - if (!fn_format(dst_path, table_name, backup_dir, MI_NAME_DEXT, 4 + 64)) - { - errmsg = "Failed in fn_format() for .MYD file: errno = %d"; - error = HA_ADMIN_INVALID; - goto err; - } + /* Change extension */ + if (!fn_format(dst_path, dst_path, "", MI_NAME_DEXT, + MY_REPLACE_EXT | MY_UNPACK_FILENAME | MY_SAFE_PATH)) + { + errmsg = "Failed in fn_format() for .MYD file: errno = %d"; + error = HA_ADMIN_INVALID; + goto err; + } - if (my_copy(fn_format(src_path, table->path,"", MI_NAME_DEXT, 4), + if (my_copy(fn_format(src_path, table->path,"", MI_NAME_DEXT, + MY_UNPACK_FILENAME), dst_path, - MYF(MY_WME | MY_HOLD_ORIGINAL_MODES )) ) - { - errmsg = "Failed copying .MYD file: errno = %d"; - error= HA_ADMIN_FAILED; - goto err; - } - return HA_ADMIN_OK; + MYF(MY_WME | MY_HOLD_ORIGINAL_MODES))) + { + errmsg = "Failed copying .MYD file: errno = %d"; + error= HA_ADMIN_FAILED; + goto err; + } + DBUG_RETURN(HA_ADMIN_OK); + err: { MI_CHECK param; @@ -459,8 +468,8 @@ int ha_myisam::backup(THD* thd, HA_CHECK_OPT *check_opt) param.db_name = table->table_cache_key; param.table_name = table->table_name; param.testflag = 0; - mi_check_print_error(¶m,errmsg, errno ); - return error; + mi_check_print_error(¶m,errmsg, my_errno); + DBUG_RETURN(error); } } @@ -476,28 +485,27 @@ int ha_myisam::repair(THD* thd, HA_CHECK_OPT *check_opt) myisamchk_init(¶m); param.thd = thd; param.op_name = (char*) "repair"; - param.testflag = ((check_opt->flags & ~(T_EXTEND)) | + param.testflag = ((check_opt->flags & ~(T_EXTEND)) | T_SILENT | T_FORCE_CREATE | (check_opt->flags & T_EXTEND ? T_REP : T_REP_BY_SORT)); - if (check_opt->quick) - param.opt_rep_quick++; param.sort_buffer_length= check_opt->sort_buffer_size; start_records=file->state->records; while ((error=repair(thd,param,0)) && param.retry_repair) { param.retry_repair=0; - if (param.retry_without_quick && param.opt_rep_quick) + if (test_all_bits(param.testflag, + (uint) (T_RETRY_WITHOUT_QUICK | T_QUICK))) { - param.opt_rep_quick=0; - sql_print_error("Warning: Retrying repair of: '%s' without quick", + param.testflag&= ~T_RETRY_WITHOUT_QUICK; + sql_print_error("Note: Retrying repair of: '%s' without quick", table->path); continue; } - param.opt_rep_quick=0; // Safety + param.testflag&= ~T_QUICK; if ((param.testflag & T_REP_BY_SORT)) { param.testflag= (param.testflag & ~T_REP_BY_SORT) | T_REP; - sql_print_error("Warning: Retrying repair of: '%s' with keycache", + sql_print_error("Note: Retrying repair of: '%s' with keycache", table->path); continue; } @@ -507,7 +515,7 @@ int ha_myisam::repair(THD* thd, HA_CHECK_OPT *check_opt) !(check_opt->flags & T_VERY_SILENT)) { char llbuff[22],llbuff2[22]; - sql_print_error("Warning: Found %s of %s rows when repairing '%s'", + sql_print_error("Note: Found %s of %s rows when repairing '%s'", llstr(file->state->records, llbuff), llstr(start_records, llbuff2), table->path); @@ -525,8 +533,6 @@ int ha_myisam::optimize(THD* thd, HA_CHECK_OPT *check_opt) param.op_name = (char*) "optimize"; param.testflag = (check_opt->flags | T_SILENT | T_FORCE_CREATE | T_REP_BY_SORT | T_STATISTICS | T_SORT_INDEX); - if (check_opt->quick) - param.opt_rep_quick++; param.sort_buffer_length= check_opt->sort_buffer_size; return repair(thd,param,1); } @@ -537,8 +543,8 @@ int ha_myisam::repair(THD *thd, MI_CHECK ¶m, bool optimize) int error=0; uint local_testflag=param.testflag; bool optimize_done= !optimize, statistics_done=0; - char fixed_name[FN_REFLEN]; const char *old_proc_info=thd->proc_info; + char fixed_name[FN_REFLEN]; MYISAM_SHARE* share = file->s; ha_rows rows= file->state->records; DBUG_ENTER("ha_myisam::repair"); @@ -550,8 +556,7 @@ int ha_myisam::repair(THD *thd, MI_CHECK ¶m, bool optimize) param.thd=thd; param.tmpdir=mysql_tmpdir; param.out_flag=0; - VOID(fn_format(fixed_name,file->filename,"",MI_NAME_IEXT, - 4+ (param.opt_follow_links ? 16 : 0))); + strmov(fixed_name,file->filename); // Don't lock tables if we have used LOCK TABLE if (!thd->locked_tables && mi_lock_database(file,F_WRLCK)) @@ -562,10 +567,10 @@ int ha_myisam::repair(THD *thd, MI_CHECK ¶m, bool optimize) if (!optimize || ((file->state->del || share->state.split != file->state->records) && - (!param.opt_rep_quick || + (!(param.testflag & T_QUICK) || !(share->state.changed & STATE_NOT_OPTIMIZED_KEYS)))) { - ulonglong key_map= ((local_testflag & T_CREATE_MISSING_KEYS) ? + ulonglong key_map= ((local_testflag & T_CREATE_MISSING_KEYS) ? ((ulonglong) 1L << share->base.keys)-1 : share->state.key_map); uint testflag=param.testflag; @@ -576,13 +581,15 @@ int ha_myisam::repair(THD *thd, MI_CHECK ¶m, bool optimize) param.testflag|= T_STATISTICS; // We get this for free thd->proc_info="Repair by sorting"; statistics_done=1; - error = mi_repair_by_sort(¶m, file, fixed_name, param.opt_rep_quick); + error = mi_repair_by_sort(¶m, file, fixed_name, + param.testflag & T_QUICK); } else { thd->proc_info="Repair with keycache"; param.testflag &= ~T_REP_BY_SORT; - error= mi_repair(¶m, file, fixed_name, param.opt_rep_quick); + error= mi_repair(¶m, file, fixed_name, + param.testflag & T_QUICK); } param.testflag=testflag; optimize_done=1; @@ -621,7 +628,7 @@ int ha_myisam::repair(THD *thd, MI_CHECK ¶m, bool optimize) the following 'if', thought conceptually wrong, is a useful optimization nevertheless. */ - if (file->state != &file->s->state.state); + if (file->state != &file->s->state.state) file->s->state.state = *file->state; if (file->s->base.auto_key) update_auto_increment_key(¶m, file, 1); @@ -654,12 +661,49 @@ int ha_myisam::repair(THD *thd, MI_CHECK ¶m, bool optimize) } -/* Deactive all not unique index that can be recreated fast */ +/* + Deactive all not unique index that can be recreated fast + + SYNOPSIS + deactivate_non_unique_index() + rows Rows to be inserted + 0 if we don't know + HA_POS_ERROR if we want to disable all keys +*/ void ha_myisam::deactivate_non_unique_index(ha_rows rows) { - if (!(specialflag & SPECIAL_SAFE_MODE)) - mi_disable_non_unique_index(file,rows); + MYISAM_SHARE* share = file->s; + if (share->state.key_map == ((ulonglong) 1L << share->base.keys)-1) + { + if (!(specialflag & SPECIAL_SAFE_MODE)) + { + if (rows == HA_POS_ERROR) + mi_extra(file, HA_EXTRA_NO_KEYS, 0); + else + { + /* + Only disable old index if the table was empty and we are inserting + a lot of rows. + We should not do this for only a few rows as this is slower and + we don't want to update the key statistics based of only a few rows. + */ + if (file->state->records == 0 && + (!rows || rows >= MI_MIN_ROWS_TO_USE_BULK_INSERT)) + mi_disable_non_unique_index(file,rows); + else + { + mi_init_bulk_insert(file, + current_thd->variables.bulk_insert_buff_size, + rows); + table->bulk_insert= 1; + } + } + } + enable_activate_all_index=1; + } + else + enable_activate_all_index=0; } @@ -669,21 +713,26 @@ bool ha_myisam::activate_all_index(THD *thd) MI_CHECK param; MYISAM_SHARE* share = file->s; DBUG_ENTER("activate_all_index"); - if (share->state.key_map != set_bits(ulonglong, share->base.keys)) + + mi_end_bulk_insert(file); + table->bulk_insert= 0; + if (enable_activate_all_index && + share->state.key_map != set_bits(ulonglong, share->base.keys)) { const char *save_proc_info=thd->proc_info; thd->proc_info="Creating index"; myisamchk_init(¶m); param.op_name = (char*) "recreating_index"; - param.testflag = (T_SILENT | T_REP_BY_SORT | - T_CREATE_MISSING_KEYS | T_TRUST_HEADER); + param.testflag = (T_SILENT | T_REP_BY_SORT | T_QUICK | + T_CREATE_MISSING_KEYS); param.myf_rw&= ~MY_WAIT_IF_FULL; - param.sort_buffer_length= myisam_sort_buffer_size; - param.opt_rep_quick++; // Don't copy data file + param.sort_buffer_length= thd->variables.myisam_sort_buff_size; param.tmpdir=mysql_tmpdir; error=repair(thd,param,0) != HA_ADMIN_OK; thd->proc_info=save_proc_info; } + else + enable_activate_all_index=1; DBUG_RETURN(error); } @@ -699,16 +748,16 @@ bool ha_myisam::check_and_repair(THD *thd) check_opt.flags= T_MEDIUM | T_AUTO_REPAIR; // Don't use quick if deleted rows if (!file->state->del && (myisam_recover_options & HA_RECOVER_QUICK)) - check_opt.quick=1; + check_opt.flags|=T_QUICK; sql_print_error("Warning: Checking table: '%s'",table->path); if ((marked_crashed=mi_is_crashed(file)) || check(thd, &check_opt)) { sql_print_error("Warning: Recovering table: '%s'",table->path); - check_opt.quick= !check_opt.retry_without_quick && !marked_crashed; - check_opt.flags=(((myisam_recover_options & HA_RECOVER_BACKUP) ? - T_BACKUP_DATA : 0) | - (!(myisam_recover_options & HA_RECOVER_FORCE) ? - T_SAFE_REPAIR : 0)) | T_AUTO_REPAIR; + check_opt.flags= + ((myisam_recover_options & HA_RECOVER_BACKUP ? T_BACKUP_DATA : 0) | + (marked_crashed ? 0 : T_QUICK) | + (myisam_recover_options & HA_RECOVER_FORCE ? 0 : T_SAFE_REPAIR) | + T_AUTO_REPAIR); if (repair(thd, &check_opt)) error=1; } @@ -753,6 +802,14 @@ int ha_myisam::index_read_idx(byte * buf, uint index, const byte * key, return error; } +int ha_myisam::index_read_last(byte * buf, const byte * key, uint key_len) +{ + statistic_increment(ha_read_key_count,&LOCK_status); + int error=mi_rkey(file,buf,active_index, key, key_len, HA_READ_PREFIX_LAST); + table->status=error ? STATUS_NOT_FOUND: 0; + return error; +} + int ha_myisam::index_next(byte * buf) { statistic_increment(ha_read_next_count,&LOCK_status); @@ -800,8 +857,7 @@ int ha_myisam::rnd_init(bool scan) { if (scan) return mi_scan_init(file); - else - return mi_extra(file,HA_EXTRA_RESET); + return mi_extra(file, HA_EXTRA_RESET, 0); } int ha_myisam::rnd_next(byte *buf) @@ -834,6 +890,8 @@ void ha_myisam::position(const byte* record) void ha_myisam::info(uint flag) { MI_ISAMINFO info; + char name_buff[FN_REFLEN]; + (void) mi_status(file,&info,flag); if (flag & HA_STATUS_VARIABLE) { @@ -854,15 +912,28 @@ void ha_myisam::info(uint flag) ref_length=info.reflength; table->db_options_in_use = info.options; block_size=myisam_block_size; - table->keys_in_use &= info.key_map; + table->keys_in_use&= info.key_map; + table->keys_for_keyread&= info.key_map; table->db_record_offset=info.record_offset; if (table->key_parts) memcpy((char*) table->key_info[0].rec_per_key, (char*) info.rec_per_key, - sizeof(ulong)*table->key_parts); + sizeof(table->key_info[0].rec_per_key)*table->key_parts); raid_type=info.raid_type; raid_chunks=info.raid_chunks; raid_chunksize=info.raid_chunksize; + + /* + Set data_file_name and index_file_name to point at the symlink value + if table is symlinked (Ie; Real name is not same as generated name) + */ + data_file_name=index_file_name=0; + fn_format(name_buff, file->filename, "", MI_NAME_DEXT, 2); + if (strcmp(name_buff, info.data_file_name)) + data_file_name=info.data_file_name; + strmov(fn_ext(name_buff),MI_NAME_IEXT); + if (strcmp(name_buff, info.index_file_name)) + index_file_name=info.index_file_name; } if (flag & HA_STATUS_ERRKEY) { @@ -878,16 +949,25 @@ void ha_myisam::info(uint flag) int ha_myisam::extra(enum ha_extra_function operation) { - if (((specialflag & SPECIAL_SAFE_MODE) || (test_flags & TEST_NO_EXTRA)) && - (operation == HA_EXTRA_WRITE_CACHE || - operation == HA_EXTRA_KEYREAD)) + if ((specialflag & SPECIAL_SAFE_MODE) && operation == HA_EXTRA_KEYREAD) return 0; - return mi_extra(file,operation); + return mi_extra(file, operation, 0); } + +/* To be used with WRITE_CACHE and EXTRA_CACHE */ + +int ha_myisam::extra_opt(enum ha_extra_function operation, ulong cache_size) +{ + if ((specialflag & SPECIAL_SAFE_MODE) && operation == HA_EXTRA_WRITE_CACHE) + return 0; + return mi_extra(file, operation, (void*) &cache_size); +} + + int ha_myisam::reset(void) { - return mi_extra(file,HA_EXTRA_RESET); + return mi_extra(file, HA_EXTRA_RESET, 0); } int ha_myisam::delete_all_rows() @@ -902,7 +982,9 @@ int ha_myisam::delete_table(const char *name) int ha_myisam::external_lock(THD *thd, int lock_type) { - return mi_lock_database(file,lock_type); + if (!table->tmp_table) + return mi_lock_database(file,lock_type); + return 0; } @@ -929,35 +1011,37 @@ void ha_myisam::update_create_info(HA_CREATE_INFO *create_info) create_info->raid_chunks= raid_chunks; create_info->raid_chunksize= raid_chunksize; } + create_info->data_file_name=data_file_name; + create_info->index_file_name=index_file_name; } -int ha_myisam::create(const char *name, register TABLE *form, +int ha_myisam::create(const char *name, register TABLE *table_arg, HA_CREATE_INFO *info) { int error; uint i,j,recpos,minpos,fieldpos,temp_length,length; - bool found_auto_increment=0; + bool found_auto_increment=0, found_real_auto_increment=0; enum ha_base_keytype type; char buff[FN_REFLEN]; KEY *pos; MI_KEYDEF *keydef; MI_COLUMNDEF *recinfo,*recinfo_pos; MI_KEYSEG *keyseg; - uint options=form->db_options_in_use; + uint options=table_arg->db_options_in_use; DBUG_ENTER("ha_myisam::create"); type=HA_KEYTYPE_BINARY; // Keep compiler happy if (!(my_multi_malloc(MYF(MY_WME), - &recinfo,(form->fields*2+2)*sizeof(MI_COLUMNDEF), - &keydef, form->keys*sizeof(MI_KEYDEF), + &recinfo,(table_arg->fields*2+2)*sizeof(MI_COLUMNDEF), + &keydef, table_arg->keys*sizeof(MI_KEYDEF), &keyseg, - ((form->key_parts + form->keys) * sizeof(MI_KEYSEG)), + ((table_arg->key_parts + table_arg->keys) * sizeof(MI_KEYSEG)), 0))) DBUG_RETURN(1); - pos=form->key_info; - for (i=0; i < form->keys ; i++, pos++) + pos=table_arg->key_info; + for (i=0; i < table_arg->keys ; i++, pos++) { keydef[i].flag= (pos->flags & (HA_NOSAME | HA_FULLTEXT)); keydef[i].seg=keyseg; @@ -1000,44 +1084,44 @@ int ha_myisam::create(const char *name, register TABLE *form, { keydef[i].seg[j].null_bit=field->null_bit; keydef[i].seg[j].null_pos= (uint) (field->null_ptr- - (uchar*) form->record[0]); + (uchar*) table_arg->record[0]); } else { keydef[i].seg[j].null_bit=0; keydef[i].seg[j].null_pos=0; } - if (j == 0 && field->flags & AUTO_INCREMENT_FLAG && - !found_auto_increment) + if (field->flags & AUTO_INCREMENT_FLAG && !found_auto_increment) { keydef[i].flag|=HA_AUTO_KEY; found_auto_increment=1; + found_real_auto_increment=(j==0); } if (field->type() == FIELD_TYPE_BLOB) { keydef[i].seg[j].flag|=HA_BLOB_PART; /* save number of bytes used to pack length */ keydef[i].seg[j].bit_start= (uint) (field->pack_length() - - form->blob_ptr_size); + table_arg->blob_ptr_size); } } keyseg+=pos->key_parts; } recpos=0; recinfo_pos=recinfo; - while (recpos < (uint) form->reclength) + while (recpos < (uint) table_arg->reclength) { Field **field,*found=0; - minpos=form->reclength; length=0; + minpos=table_arg->reclength; length=0; - for (field=form->field ; *field ; field++) + for (field=table_arg->field ; *field ; field++) { if ((fieldpos=(*field)->offset()) >= recpos && fieldpos <= minpos) { /* skip null fields */ if (!(temp_length= (*field)->pack_length())) - continue; /* Skipp null-fields */ + continue; /* Skip null-fields */ if (! found || fieldpos < minpos || (fieldpos == minpos && temp_length < length)) { @@ -1063,20 +1147,20 @@ int ha_myisam::create(const char *name, register TABLE *form, else if (!(options & HA_OPTION_PACK_RECORD)) recinfo_pos->type= (int) FIELD_NORMAL; else if (found->zero_pack()) - recinfo_pos->type= (int) FIELD_SKIPP_ZERO; + recinfo_pos->type= (int) FIELD_SKIP_ZERO; else recinfo_pos->type= (int) ((length <= 3 || (found->flags & ZEROFILL_FLAG)) ? FIELD_NORMAL : found->type() == FIELD_TYPE_STRING || found->type() == FIELD_TYPE_VAR_STRING ? - FIELD_SKIPP_ENDSPACE : - FIELD_SKIPP_PRESPACE); + FIELD_SKIP_ENDSPACE : + FIELD_SKIP_PRESPACE); if (found->null_ptr) { recinfo_pos->null_bit=found->null_bit; recinfo_pos->null_pos= (uint) (found->null_ptr- - (uchar*) form->record[0]); + (uchar*) table_arg->record[0]); } else { @@ -1091,18 +1175,24 @@ int ha_myisam::create(const char *name, register TABLE *form, } MI_CREATE_INFO create_info; bzero((char*) &create_info,sizeof(create_info)); - create_info.max_rows=form->max_rows; - create_info.reloc_rows=form->min_rows; + create_info.max_rows=table_arg->max_rows; + create_info.reloc_rows=table_arg->min_rows; + create_info.with_auto_increment=found_real_auto_increment; create_info.auto_increment=(info->auto_increment_value ? info->auto_increment_value -1 : (ulonglong) 0); - create_info.data_file_length=(ulonglong) form->max_rows*form->avg_row_length; + create_info.data_file_length= ((ulonglong) table_arg->max_rows * + table_arg->avg_row_length); create_info.raid_type=info->raid_type; - create_info.raid_chunks=info->raid_chunks ? info->raid_chunks : RAID_DEFAULT_CHUNKS; - create_info.raid_chunksize=info->raid_chunksize ? info->raid_chunksize : RAID_DEFAULT_CHUNKSIZE; - - error=mi_create(fn_format(buff,name,"","",2+4+16), - form->keys,keydef, + create_info.raid_chunks= (info->raid_chunks ? info->raid_chunks : + RAID_DEFAULT_CHUNKS); + create_info.raid_chunksize=(info->raid_chunksize ? info->raid_chunksize : + RAID_DEFAULT_CHUNKSIZE); + create_info.data_file_name= info->data_file_name; + create_info.index_file_name=info->index_file_name; + + error=mi_create(fn_format(buff,name,"","",2+4), + table_arg->keys,keydef, (uint) (recinfo_pos-recinfo), recinfo, 0, (MI_UNIQUEDEF*) 0, &create_info, @@ -1131,9 +1221,12 @@ longlong ha_myisam::get_auto_increment() return auto_increment_value; } + if (table->bulk_insert) + mi_flush_bulk_insert(file, table->next_number_index); + longlong nr; int error; - byte key[MAX_KEY_LENGTH]; + byte key[MI_MAX_KEY_LENGTH]; (void) extra(HA_EXTRA_KEYREAD); key_copy(key,table,table->next_number_index, table->next_number_key_offset); @@ -1172,9 +1265,8 @@ int ha_myisam::ft_read(byte * buf) thread_safe_increment(ha_read_next_count,&LOCK_status); // why ? - error=ft_read_next((FT_DOCLIST *) ft_handler,(char*) buf); + error=ft_handler->please->read_next(ft_handler,(char*) buf); table->status=error ? STATUS_NOT_FOUND: 0; return error; } - diff --git a/sql/ha_myisam.h b/sql/ha_myisam.h index 6451e2b80ee..215608f8f0a 100644 --- a/sql/ha_myisam.h +++ b/sql/ha_myisam.h @@ -24,11 +24,11 @@ #include <myisam.h> #include <ft_global.h> -#define HA_RECOVER_NONE 0 // No automatic recover -#define HA_RECOVER_DEFAULT 1 // Automatic recover active -#define HA_RECOVER_BACKUP 2 // Make a backupfile on recover -#define HA_RECOVER_FORCE 4 // Recover even if we loose rows -#define HA_RECOVER_QUICK 8 // Don't check rows in data file +#define HA_RECOVER_NONE 0 /* No automatic recover */ +#define HA_RECOVER_DEFAULT 1 /* Automatic recover active */ +#define HA_RECOVER_BACKUP 2 /* Make a backupfile on recover */ +#define HA_RECOVER_FORCE 4 /* Recover even if we loose rows */ +#define HA_RECOVER_QUICK 8 /* Don't check rows in data file */ extern ulong myisam_sort_buffer_size; extern TYPELIB myisam_recover_typelib; @@ -37,25 +37,33 @@ extern ulong myisam_recover_options; class ha_myisam: public handler { MI_INFO *file; - uint int_option_flag; + uint int_table_flags; + char *data_file_name, *index_file_name; + bool enable_activate_all_index; int repair(THD *thd, MI_CHECK ¶m, bool optimize); public: ha_myisam(TABLE *table): handler(table), file(0), - int_option_flag(HA_READ_NEXT | HA_READ_PREV | HA_READ_RND_SAME | - HA_KEYPOS_TO_RNDPOS | HA_READ_ORDER | HA_LASTKEY_ORDER | - HA_HAVE_KEY_READ_ONLY | HA_READ_NOT_EXACT_KEY | - HA_LONGLONG_KEYS | HA_NULL_KEY | - HA_DUPP_POS | HA_BLOB_KEY | HA_AUTO_PART_KEY) + int_table_flags(HA_READ_RND_SAME | HA_KEYPOS_TO_RNDPOS | HA_LASTKEY_ORDER | + HA_NULL_KEY | HA_CAN_FULLTEXT | HA_CAN_SQL_HANDLER | + HA_DUPP_POS | HA_BLOB_KEY | HA_AUTO_PART_KEY), + enable_activate_all_index(1) {} ~ha_myisam() {} const char *table_type() const { return "MyISAM"; } + const char *index_type(uint key_number); const char **bas_ext() const; - ulong option_flag() const { return int_option_flag; } + ulong table_flags() const { return int_table_flags; } + ulong index_flags(uint inx) const + { + ulong flags=(HA_READ_NEXT | HA_READ_PREV | HA_READ_ORDER); + return (flags | ((table->key_info[inx].algorithm == HA_KEY_ALG_FULLTEXT) ? + 0 : HA_KEY_READ_ONLY)); + } uint max_record_length() const { return HA_MAX_REC_LENGTH; } uint max_keys() const { return MI_MAX_KEY; } uint max_key_parts() const { return MAX_REF_PARTS; } - uint max_key_length() const { return MAX_KEY_LENGTH; } + uint max_key_length() const { return MI_MAX_KEY_LENGTH; } int open(const char *name, int mode, uint test_if_locked); int close(void); @@ -66,6 +74,7 @@ class ha_myisam: public handler uint key_len, enum ha_rkey_function find_flag); int index_read_idx(byte * buf, uint idx, const byte * key, uint key_len, enum ha_rkey_function find_flag); + int index_read_last(byte * buf, const byte * key, uint key_len); int index_next(byte * buf); int index_prev(byte * buf); int index_first(byte * buf); @@ -73,9 +82,15 @@ class ha_myisam: public handler int index_next_same(byte *buf, const byte *key, uint keylen); int index_end() { ft_handler=NULL; return 0; } int ft_init() - { if(!ft_handler) return 1; ft_reinit_search(ft_handler); return 0; } - void *ft_init_ext(uint inx,const byte *key, uint keylen, bool presort) - { return ft_init_search(file,inx,(byte*) key,keylen,presort); } + { + if (!ft_handler) + return 1; + ft_handler->please->reinit_search(ft_handler); + return 0; + } + FT_INFO *ft_init_ext(uint mode, uint inx,const byte *key, uint keylen, + bool presort) + { return ft_init_search(mode, file,inx,(byte*) key,keylen,presort); } int ft_read(byte *buf); int rnd_init(bool scan=1); int rnd_next(byte *buf); @@ -85,6 +100,7 @@ class ha_myisam: public handler my_off_t row_position() { return mi_position(file); } void info(uint); int extra(enum ha_extra_function operation); + int extra_opt(enum ha_extra_function operation, ulong cache_size); int reset(void); int external_lock(THD *thd, int lock_type); int delete_all_rows(void); diff --git a/sql/ha_myisammrg.cc b/sql/ha_myisammrg.cc index e9b6a048264..616248b2cf3 100644 --- a/sql/ha_myisammrg.cc +++ b/sql/ha_myisammrg.cc @@ -23,9 +23,9 @@ #include <m_ctype.h> #include "ha_myisammrg.h" #ifndef MASTER -#include "../srclib/myisammrg/mymrgdef.h" +#include "../srclib/myisammrg/myrg_def.h" #else -#include "../myisammrg/mymrgdef.h" +#include "../myisammrg/myrg_def.h" #endif /***************************************************************************** @@ -38,25 +38,38 @@ const char **ha_myisammrg::bas_ext() const int ha_myisammrg::open(const char *name, int mode, uint test_if_locked) { char name_buff[FN_REFLEN]; + DBUG_PRINT("info", ("ha_myisammrg::open")); if (!(file=myrg_open(fn_format(name_buff,name,"","",2 | 4), mode, test_if_locked))) + { + DBUG_PRINT("info", ("ha_myisammrg::open exit %d", my_errno)); return (my_errno ? my_errno : -1); - + } + DBUG_PRINT("info", ("ha_myisammrg::open myrg_extrafunc...")) + myrg_extrafunc(file, query_cache_invalidate_by_MyISAM_filename_ref); if (!(test_if_locked == HA_OPEN_WAIT_IF_LOCKED || test_if_locked == HA_OPEN_ABORT_IF_LOCKED)) - myrg_extra(file,HA_EXTRA_NO_WAIT_LOCK); + myrg_extra(file,HA_EXTRA_NO_WAIT_LOCK,0); info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST); if (!(test_if_locked & HA_OPEN_WAIT_IF_LOCKED)) - myrg_extra(file,HA_EXTRA_WAIT_LOCK); + myrg_extra(file,HA_EXTRA_WAIT_LOCK,0); + if (table->reclength != mean_rec_length && mean_rec_length) { DBUG_PRINT("error",("reclength: %d mean_rec_length: %d", table->reclength, mean_rec_length)); - myrg_close(file); - file=0; - return my_errno=HA_ERR_WRONG_TABLE_DEF; + goto err; } +#if !defined(BIG_TABLES) || SIZEOF_OFF_T == 4 + /* Merge table has more than 2G rows */ + if (table->crashed) + goto err; +#endif return (0); +err: + myrg_close(file); + file=0; + return (my_errno= HA_ERR_WRONG_MRG_TABLE_DEF); } int ha_myisammrg::close(void) @@ -66,7 +79,12 @@ int ha_myisammrg::close(void) int ha_myisammrg::write_row(byte * buf) { - return (my_errno=HA_ERR_WRONG_COMMAND); + statistic_increment(ha_write_count,&LOCK_status); + if (table->time_stamp) + update_timestamp(buf+table->time_stamp-1); + if (table->next_number_field && buf == table->record[0]) + update_auto_increment(); + return myrg_write(file,buf); } int ha_myisammrg::update_row(const byte * old_data, byte * new_data) @@ -101,6 +119,15 @@ int ha_myisammrg::index_read_idx(byte * buf, uint index, const byte * key, return error; } +int ha_myisammrg::index_read_last(byte * buf, const byte * key, uint key_len) +{ + statistic_increment(ha_read_key_count,&LOCK_status); + int error=myrg_rkey(file,buf,active_index, key, key_len, + HA_READ_PREFIX_LAST); + table->status=error ? STATUS_NOT_FOUND: 0; + return error; +} + int ha_myisammrg::index_next(byte * buf) { statistic_increment(ha_read_next_count,&LOCK_status); @@ -135,7 +162,7 @@ int ha_myisammrg::index_last(byte * buf) int ha_myisammrg::rnd_init(bool scan) { - return myrg_extra(file,HA_EXTRA_RESET); + return myrg_extra(file,HA_EXTRA_RESET,0); } int ha_myisammrg::rnd_next(byte *buf) @@ -160,13 +187,35 @@ void ha_myisammrg::position(const byte *record) ha_store_ptr(ref, ref_length, (my_off_t) position); } +ha_rows ha_myisammrg::records_in_range(int inx, + const byte *start_key,uint start_key_len, + enum ha_rkey_function start_search_flag, + const byte *end_key,uint end_key_len, + enum ha_rkey_function end_search_flag) +{ + return (ha_rows) myrg_records_in_range(file, + inx, + start_key,start_key_len, + start_search_flag, + end_key,end_key_len, + end_search_flag); +} void ha_myisammrg::info(uint flag) { MYMERGE_INFO info; (void) myrg_status(file,&info,flag); + /* + The following fails if one has not compiled MySQL with -DBIG_TABLES + and one has more than 2^32 rows in the merge tables. + */ records = (ha_rows) info.records; deleted = (ha_rows) info.deleted; +#if !defined(BIG_TABLES) || SIZEOF_OFF_T == 4 + if ((info.records >= (ulonglong) 1 << 32) || + (info.deleted >= (ulonglong) 1 << 32)) + table->crashed=1; +#endif data_file_length=info.data_file_length; errkey = info.errkey; table->keys_in_use= set_bits(key_map, table->keys); @@ -180,17 +229,40 @@ void ha_myisammrg::info(uint flag) #else ref_length=4; // Can't be > than my_off_t #endif + if (flag & HA_STATUS_CONST) + { + if (table->key_parts && info.rec_per_key) + memcpy((char*) table->key_info[0].rec_per_key, + (char*) info.rec_per_key, + sizeof(table->key_info[0].rec_per_key)*table->key_parts); + } } int ha_myisammrg::extra(enum ha_extra_function operation) { - return myrg_extra(file,operation); + /* As this is just a mapping, we don't have to force the underlying + tables to be closed */ + if (operation == HA_EXTRA_FORCE_REOPEN || + operation == HA_EXTRA_PREPARE_FOR_DELETE) + return 0; + return myrg_extra(file,operation,0); +} + + +/* To be used with WRITE_CACHE, EXTRA_CACHE and BULK_INSERT_BEGIN */ + +int ha_myisammrg::extra_opt(enum ha_extra_function operation, ulong cache_size) +{ + if ((specialflag & SPECIAL_SAFE_MODE) && operation == HA_EXTRA_WRITE_CACHE) + return 0; + return myrg_extra(file, operation, (void*) &cache_size); } + int ha_myisammrg::reset(void) { - return myrg_extra(file,HA_EXTRA_RESET); + return myrg_extra(file,HA_EXTRA_RESET,0); } int ha_myisammrg::external_lock(THD *thd, int lock_type) @@ -208,30 +280,35 @@ THR_LOCK_DATA **ha_myisammrg::store_lock(THD *thd, THR_LOCK_DATA **to, enum thr_lock_type lock_type) { - MYRG_TABLE *table; + MYRG_TABLE *open_table; - for (table=file->open_tables ; table != file->end_table ; table++) + for (open_table=file->open_tables ; + open_table != file->end_table ; + open_table++) { - *(to++)= &table->table->lock; - if (lock_type != TL_IGNORE && table->table->lock.type == TL_UNLOCK) - table->table->lock.type=lock_type; + *(to++)= &open_table->table->lock; + if (lock_type != TL_IGNORE && open_table->table->lock.type == TL_UNLOCK) + open_table->table->lock.type=lock_type; } return to; } void ha_myisammrg::update_create_info(HA_CREATE_INFO *create_info) { + // [phi] auto_increment stuff is missing (but currently not needed) DBUG_ENTER("ha_myisammrg::update_create_info"); if (!(create_info->used_fields & HA_CREATE_USED_UNION)) { - MYRG_TABLE *table; + MYRG_TABLE *open_table; THD *thd=current_thd; create_info->merge_list.next= &create_info->merge_list.first; create_info->merge_list.elements=0; - for (table=file->open_tables ; table != file->end_table ; table++) + for (open_table=file->open_tables ; + open_table != file->end_table ; + open_table++) { - char *name=table->table->s->filename; + char *name=open_table->table->filename; char buff[FN_REFLEN]; TABLE_LIST *ptr; if (!(ptr = (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST)))) @@ -245,6 +322,10 @@ void ha_myisammrg::update_create_info(HA_CREATE_INFO *create_info) } *create_info->merge_list.next=0; } + if (!(create_info->used_fields & HA_CREATE_USED_INSERT_METHOD)) + { + create_info->merge_insert_method = file->merge_insert_method; + } DBUG_VOID_RETURN; err: @@ -288,20 +369,29 @@ int ha_myisammrg::create(const char *name, register TABLE *form, } *pos=0; DBUG_RETURN(myrg_create(fn_format(buff,name,"","",2+4+16), - (const char **) table_names, (my_bool) 0)); + (const char **) table_names, + create_info->merge_insert_method, + (my_bool) 0)); } void ha_myisammrg::append_create_info(String *packet) { char buff[FN_REFLEN]; + if (file->merge_insert_method != MERGE_INSERT_DISABLED) + { + packet->append(" INSERT_METHOD=",15); + packet->append(get_type(&merge_insert_method,file->merge_insert_method-1)); + } packet->append(" UNION=(",8); - MYRG_TABLE *table,*first; + MYRG_TABLE *open_table,*first; - for (first=table=file->open_tables ; table != file->end_table ; table++) + for (first=open_table=file->open_tables ; + open_table != file->end_table ; + open_table++) { - char *name=table->table->s->filename; + char *name= open_table->table->filename; fn_format(buff,name,"","",3); - if (table != first) + if (open_table != first) packet->append(','); packet->append(buff,(uint) strlen(buff)); } diff --git a/sql/ha_myisammrg.h b/sql/ha_myisammrg.h index b97baa0703c..440b51e660f 100644 --- a/sql/ha_myisammrg.h +++ b/sql/ha_myisammrg.h @@ -32,13 +32,18 @@ class ha_myisammrg: public handler ~ha_myisammrg() {} const char *table_type() const { return "MRG_MyISAM"; } const char **bas_ext() const; - ulong option_flag() const - { return (HA_REC_NOT_IN_SEQ | HA_READ_NEXT | - HA_READ_PREV | HA_READ_RND_SAME | - HA_HAVE_KEY_READ_ONLY | HA_NO_FULLTEXT_KEY | - HA_KEYPOS_TO_RNDPOS | HA_READ_ORDER | - HA_LASTKEY_ORDER | HA_READ_NOT_EXACT_KEY | - HA_LONGLONG_KEYS | HA_NULL_KEY | HA_BLOB_KEY); } + ulong table_flags() const + { + return (HA_REC_NOT_IN_SEQ | HA_READ_RND_SAME | HA_AUTO_PART_KEY | + HA_KEYPOS_TO_RNDPOS | HA_LASTKEY_ORDER | + HA_NULL_KEY | HA_BLOB_KEY); + } + ulong index_flags(uint inx) const + { + ulong flags=(HA_READ_NEXT | HA_READ_PREV | HA_READ_ORDER); + return (flags | ((table->key_info[inx].algorithm == HA_KEY_ALG_FULLTEXT) ? + 0 : HA_KEY_READ_ONLY)); + } uint max_record_length() const { return HA_MAX_REC_LENGTH; } uint max_keys() const { return MI_MAX_KEY; } uint max_key_parts() const { return MAX_REF_PARTS; } @@ -55,6 +60,7 @@ class ha_myisammrg: public handler uint key_len, enum ha_rkey_function find_flag); int index_read_idx(byte * buf, uint idx, const byte * key, uint key_len, enum ha_rkey_function find_flag); + int index_read_last(byte * buf, const byte * key, uint key_len); int index_next(byte * buf); int index_prev(byte * buf); int index_first(byte * buf); @@ -63,9 +69,15 @@ class ha_myisammrg: public handler int rnd_next(byte *buf); int rnd_pos(byte * buf, byte *pos); void position(const byte *record); + ha_rows records_in_range(int inx, + const byte *start_key,uint start_key_len, + enum ha_rkey_function start_search_flag, + const byte *end_key,uint end_key_len, + enum ha_rkey_function end_search_flag); my_off_t row_position() { return myrg_position(file); } void info(uint); int extra(enum ha_extra_function operation); + int extra_opt(enum ha_extra_function operation, ulong cache_size); int reset(void); int external_lock(THD *thd, int lock_type); uint lock_count(void) const; @@ -74,4 +86,5 @@ class ha_myisammrg: public handler enum thr_lock_type lock_type); void update_create_info(HA_CREATE_INFO *create_info); void append_create_info(String *packet); + MYRG_INFO *myrg_info() { return file; } }; diff --git a/sql/handler.cc b/sql/handler.cc index 564d91aa887..ebae0d5e901 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -25,7 +25,7 @@ #include "ha_heap.h" #include "ha_myisam.h" #include "ha_myisammrg.h" -#ifndef NO_ISAM +#ifdef HAVE_ISAM #include "ha_isam.h" #include "ha_isammrg.h" #endif @@ -33,10 +33,7 @@ #include "ha_berkeley.h" #endif #ifdef HAVE_INNOBASE_DB -#include "ha_innobase.h" -#endif -#ifdef HAVE_GEMINI_DB -#include "ha_gemini.h" +#include "ha_innodb.h" #endif #include <myisampack.h> #include <errno.h> @@ -48,6 +45,7 @@ static int NEAR_F delete_file(const char *name,const char *ext,int extflag); ulong ha_read_count, ha_write_count, ha_delete_count, ha_update_count, ha_read_key_count, ha_read_next_count, ha_read_prev_count, ha_read_first_count, ha_read_last_count, + ha_commit_count, ha_rollback_count, ha_read_rnd_count, ha_read_rnd_next_count; const char *ha_table_type[] = { @@ -55,8 +53,10 @@ const char *ha_table_type[] = { "MRG_ISAM","MYISAM", "MRG_MYISAM", "BDB", "INNODB", "GEMINI", "?", "?",NullS }; -TYPELIB ha_table_typelib= {array_elements(ha_table_type)-4,"", - ha_table_type+1}; +TYPELIB ha_table_typelib= +{ + array_elements(ha_table_type)-3, "", ha_table_type +}; const char *ha_row_type[] = { "", "FIXED", "DYNAMIC", "COMPRESSED","?","?","?" @@ -78,21 +78,15 @@ enum db_type ha_checktype(enum db_type database_type) return(berkeley_skip ? DB_TYPE_MYISAM : database_type); #endif #ifdef HAVE_INNOBASE_DB - case DB_TYPE_INNOBASE: + case DB_TYPE_INNODB: return(innodb_skip ? DB_TYPE_MYISAM : database_type); #endif -#ifdef HAVE_GEMINI_DB - case DB_TYPE_GEMINI: - return(gemini_skip ? DB_TYPE_MYISAM : database_type); -#endif #ifndef NO_HASH case DB_TYPE_HASH: #endif -#ifndef NO_MERGE - case DB_TYPE_MRG_ISAM: -#endif -#ifndef NO_ISAM +#ifdef HAVE_ISAM case DB_TYPE_ISAM: + case DB_TYPE_MRG_ISAM: #endif case DB_TYPE_HEAP: case DB_TYPE_MYISAM: @@ -111,11 +105,9 @@ handler *get_new_handler(TABLE *table, enum db_type db_type) #ifndef NO_HASH return new ha_hash(table); #endif -#ifndef NO_MERGE +#ifdef HAVE_ISAM case DB_TYPE_MRG_ISAM: return new ha_isammrg(table); -#endif -#ifndef NO_ISAM case DB_TYPE_ISAM: return new ha_isam(table); #endif @@ -124,13 +116,9 @@ handler *get_new_handler(TABLE *table, enum db_type db_type) return new ha_berkeley(table); #endif #ifdef HAVE_INNOBASE_DB - case DB_TYPE_INNOBASE: + case DB_TYPE_INNODB: return new ha_innobase(table); #endif -#ifdef HAVE_GEMINI_DB - case DB_TYPE_GEMINI: - return new ha_gemini(table); -#endif case DB_TYPE_HEAP: return new ha_heap(table); case DB_TYPE_MYISAM: @@ -166,17 +154,6 @@ int ha_init() have_innodb=SHOW_OPTION_DISABLED; } #endif -#ifdef HAVE_GEMINI_DB - if (!gemini_skip) - { - if (gemini_init()) - return -1; - if (!gemini_skip) // If we couldn't use handler - opt_using_transactions=1; - else - have_gemini=SHOW_OPTION_DISABLED; - } -#endif return 0; } @@ -186,14 +163,14 @@ int ha_init() int ha_panic(enum ha_panic_function flag) { int error=0; -#ifndef NO_MERGE - error|=mrg_panic(flag); -#endif #ifndef NO_HASH error|=h_panic(flag); /* fix hash */ #endif - error|=heap_panic(flag); +#ifdef HAVE_ISAM + error|=mrg_panic(flag); error|=nisam_panic(flag); +#endif + error|=heap_panic(flag); error|=mi_panic(flag); error|=myrg_panic(flag); #ifdef HAVE_BERKELEY_DB @@ -204,10 +181,6 @@ int ha_panic(enum ha_panic_function flag) if (!innodb_skip) error|=innobase_end(); #endif -#ifdef HAVE_GEMINI_DB - if (!gemini_skip) - error|=gemini_end(); -#endif return error; } /* ha_panic */ @@ -225,12 +198,6 @@ void ha_close_connection(THD* thd) if (!innodb_skip) innobase_close_connection(thd); #endif -#ifdef HAVE_GEMINI_DB - if (!gemini_skip && thd->gemini.context) - { - gemini_disconnect(thd); - } -#endif /* HAVE_GEMINI_DB */ } /* @@ -251,8 +218,8 @@ int ha_autocommit_or_rollback(THD *thd, int error) } else (void) ha_rollback_stmt(thd); - - thd->tx_isolation=thd->session_tx_isolation; + + thd->variables.tx_isolation=thd->session_tx_isolation; } #endif DBUG_RETURN(error); @@ -271,8 +238,10 @@ int ha_autocommit_or_rollback(THD *thd, int error) handler must be the same as in the binlog. arguments: + thd: the thread handle of the current connection log_file_name: latest binlog file name end_offset: the offset in the binlog file up to which we wrote + return value: 0 if success, 1 if error */ int ha_report_binlog_offset_and_commit(THD *thd, @@ -299,6 +268,34 @@ int ha_report_binlog_offset_and_commit(THD *thd, return error; } +/* + This function should be called when MySQL sends rows of a SELECT result set + or the EOF mark to the client. It releases a possible adaptive hash index + S-latch held by thd in InnoDB and also releases a possible InnoDB query + FIFO ticket to enter InnoDB. To save CPU time, InnoDB allows a thd to + keep them over several calls of the InnoDB handler interface when a join + is executed. But when we let the control to pass to the client they have + to be released because if the application program uses mysql_use_result(), + it may deadlock on the S-latch if the application on another connection + performs another SQL query. In MySQL-4.1 this is even more important because + there a connection can have several SELECT queries open at the same time. + + arguments: + thd: the thread handle of the current connection + return value: always 0 +*/ + +int ha_release_temporary_latches(THD *thd) +{ +#ifdef HAVE_INNOBASE_DB + THD_TRANS *trans; + trans = &thd->transaction.all; + if (trans->innobase_tid) + innobase_release_temporary_latches(trans->innobase_tid); +#endif + return 0; +} + int ha_commit_trans(THD *thd, THD_TRANS* trans) { int error=0; @@ -306,40 +303,13 @@ int ha_commit_trans(THD *thd, THD_TRANS* trans) #ifdef USING_TRANSACTIONS if (opt_using_transactions) { + bool operation_done= 0; + bool transaction_commited= 0; + /* Update the binary log if we have cached some queries */ if (trans == &thd->transaction.all && mysql_bin_log.is_open() && my_b_tell(&thd->transaction.trans_log)) { - /* We write the command "COMMIT" as the last SQL command in the - binlog segment cached for this transaction */ - - int save_query_length = thd->query_length; - - thd->query_length = 6; /* length of 'COMMIT'; note that we may come - here because a DROP TABLE, for instance, - makes an implicit commit, and then - thd->query is not 'COMMIT'! */ - - Query_log_event qinfo(thd, "COMMIT", TRUE); - - /* When we come here, and the user wrapped the transaction into - BEGIN and COMMIT, then qinfo got above the field cache_stmt - erroneously set to 0. Let us set it to 1: */ - - qinfo.cache_stmt = 1; - - /* Write the 'COMMIT' entry to the cache */ - - if (mysql_bin_log.write(&qinfo)) { - my_error(ER_ERROR_DURING_COMMIT, MYF(0), 5000); - error=1; - } - - thd->query_length = save_query_length; - - /* Now we write the binlog segment cached for this transaction to - the real binlog */ - mysql_bin_log.write(thd, &thd->transaction.trans_log); reinit_io_cache(&thd->transaction.trans_log, WRITE_CACHE, (my_off_t) 0, 0, 1); @@ -353,6 +323,9 @@ int ha_commit_trans(THD *thd, THD_TRANS* trans) my_error(ER_ERROR_DURING_COMMIT, MYF(0), error); error=1; } + else + if (!(thd->options & OPTION_BEGIN)) + transaction_commited= 1; trans->bdb_tid=0; } #endif @@ -365,25 +338,23 @@ int ha_commit_trans(THD *thd, THD_TRANS* trans) error=1; } trans->innodb_active_trans=0; + if (trans == &thd->transaction.all) + operation_done= transaction_commited= 1; + } #endif -#ifdef HAVE_GEMINI_DB - /* Commit the transaction in behalf of the commit statement - or if we're in auto-commit mode */ - if((trans == &thd->transaction.all) || - (!(thd->options & (OPTION_NOT_AUTO_COMMIT | OPTION_BEGIN)))) - { - error=gemini_commit(thd); - if (error) - { - my_error(ER_ERROR_DURING_COMMIT, MYF(0), error); - error=1; - } - } -#endif +#ifdef HAVE_QUERY_CACHE + if (transaction_commited && thd->transaction.changed_tables) + query_cache.invalidate(thd->transaction.changed_tables); +#endif /*HAVE_QUERY_CACHE*/ if (error && trans == &thd->transaction.all && mysql_bin_log.is_open()) sql_print_error("Error: Got error during commit; Binlog is not up to date!"); - thd->tx_isolation=thd->session_tx_isolation; + thd->variables.tx_isolation=thd->session_tx_isolation; + if (operation_done) + { + statistic_increment(ha_commit_count,&LOCK_status); + thd->transaction.cleanup(); + } } #endif // using transactions DBUG_RETURN(error); @@ -397,6 +368,7 @@ int ha_rollback_trans(THD *thd, THD_TRANS *trans) #ifdef USING_TRANSACTIONS if (opt_using_transactions) { + bool operation_done=0; #ifdef HAVE_BERKELEY_DB if (trans->bdb_tid) { @@ -406,6 +378,7 @@ int ha_rollback_trans(THD *thd, THD_TRANS *trans) error=1; } trans->bdb_tid=0; + operation_done=1; } #endif #ifdef HAVE_INNOBASE_DB @@ -417,41 +390,24 @@ int ha_rollback_trans(THD *thd, THD_TRANS *trans) error=1; } trans->innodb_active_trans=0; - } -#endif -#ifdef HAVE_GEMINI_DB - if((trans == &thd->transaction.stmt) && - (thd->options & (OPTION_NOT_AUTO_COMMIT | OPTION_BEGIN))) - error = gemini_rollback_to_savepoint(thd); - else - error=gemini_rollback(thd); - if (error) - { - my_error(ER_ERROR_DURING_ROLLBACK, MYF(0), error); - error=1; + operation_done=1; } #endif if (trans == &thd->transaction.all) reinit_io_cache(&thd->transaction.trans_log, WRITE_CACHE, (my_off_t) 0, 0, 1); thd->transaction.trans_log.end_of_file= max_binlog_cache_size; - thd->tx_isolation=thd->session_tx_isolation; + thd->variables.tx_isolation=thd->session_tx_isolation; + if (operation_done) + { + statistic_increment(ha_rollback_count,&LOCK_status); + thd->transaction.cleanup(); + } } #endif /* USING_TRANSACTIONS */ DBUG_RETURN(error); } -void ha_set_spin_retries(uint retries) -{ -#ifdef HAVE_GEMINI_DB - if (!gemini_skip) - { - gemini_set_option_long(GEM_OPTID_SPIN_RETRIES, retries); - } -#endif /* HAVE_GEMINI_DB */ -} - - bool ha_flush_logs() { bool result=0; @@ -480,7 +436,7 @@ int ha_delete_table(enum db_type table_type, const char *path) delete file; return error; } - + void ha_store_ptr(byte *buff, uint pack_length, my_off_t pos) { switch (pack_length) { @@ -547,7 +503,8 @@ int handler::ha_open(const char *name, int mode, int test_if_locked) int error; DBUG_ENTER("handler::open"); DBUG_PRINT("enter",("name: %s db_type: %d db_stat: %d mode: %d lock_test: %d", - name, table->db_type, table->db_stat, mode, test_if_locked)); + name, table->db_type, table->db_stat, mode, + test_if_locked)); if ((error=open(name,mode,test_if_locked))) { @@ -567,9 +524,8 @@ int handler::ha_open(const char *name, int mode, int test_if_locked) { if (table->db_options_in_use & HA_OPTION_READ_ONLY_DATA) table->db_stat|=HA_READ_ONLY; - } - if (!error) - { + (void) extra(HA_EXTRA_NO_READCHECK); // Not needed in SQL + if (!alloc_root_inited(&table->mem_root)) // If temporary table ref=(byte*) sql_alloc(ALIGN_SIZE(ref_length)*2); else @@ -615,17 +571,37 @@ int handler::analyze(THD* thd, HA_CHECK_OPT* check_opt) return HA_ADMIN_NOT_IMPLEMENTED; } - /* Read first row from a table */ +/* + Read first row (only) from a table + This is never called for InnoDB or BDB tables, as these table types + has the HA_NOT_EXACT_COUNT set. +*/ -int handler::rnd_first(byte * buf) +int handler::read_first_row(byte * buf, uint primary_key) { register int error; - DBUG_ENTER("handler::rnd_first"); + DBUG_ENTER("handler::read_first_row"); statistic_increment(ha_read_first_count,&LOCK_status); - (void) rnd_init(); - while ((error= rnd_next(buf)) == HA_ERR_RECORD_DELETED) ; - (void) rnd_end(); + + /* + If there is very few deleted rows in the table, find the first row by + scanning the table. + */ + if (deleted < 10 || primary_key >= MAX_KEY || + !(index_flags(primary_key) & HA_READ_ORDER)) + { + (void) rnd_init(); + while ((error= rnd_next(buf)) == HA_ERR_RECORD_DELETED) ; + (void) rnd_end(); + } + else + { + /* Find the first row through the primary key */ + (void) index_init(primary_key); + error=index_first(buf); + (void) index_end(); + } DBUG_RETURN(error); } @@ -641,7 +617,7 @@ int handler::restart_rnd_next(byte *buf, byte *pos) } - /* Set a timestamp in record */ +/* Set a timestamp in record */ void handler::update_timestamp(byte *record) { @@ -657,9 +633,10 @@ void handler::update_timestamp(byte *record) return; } - /* Updates field with field_type NEXT_NUMBER according to following: - ** if field = 0 change field to the next free key in database. - */ +/* + Updates field with field_type NEXT_NUMBER according to following: + if field = 0 change field to the next free key in database. +*/ void handler::update_auto_increment() { @@ -687,16 +664,29 @@ longlong handler::get_auto_increment() { longlong nr; int error; + (void) extra(HA_EXTRA_KEYREAD); index_init(table->next_number_index); - error=index_last(table->record[1]); + if (!table->next_number_key_offset) + { // Autoincrement at key-start + error=index_last(table->record[1]); + } + else + { + byte key[MAX_KEY_LENGTH]; + key_copy(key,table,table->next_number_index, + table->next_number_key_offset); + error=index_read(table->record[1], key, table->next_number_key_offset, + HA_READ_PREFIX_LAST); + } + if (error) nr=1; else nr=(longlong) table->next_number_field-> val_int_offset(table->rec_buff_length)+1; - (void) extra(HA_EXTRA_NO_KEYREAD); index_end(); + (void) extra(HA_EXTRA_NO_KEYREAD); return nr; } @@ -720,7 +710,7 @@ void handler::print_error(int error, myf errflag) case HA_ERR_END_OF_FILE: textno=ER_KEY_NOT_FOUND; break; - case HA_ERR_WRONG_TABLE_DEF: + case HA_ERR_WRONG_MRG_TABLE_DEF: textno=ER_WRONG_MRG_TABLE; break; case HA_ERR_FOUND_DUPP_KEY: @@ -842,22 +832,6 @@ int handler::rename_table(const char * from, const char * to) DBUG_RETURN(0); } -int ha_commit_rename(THD *thd) -{ - int error=0; -#ifdef HAVE_GEMINI_DB - /* Gemini needs to commit the rename; otherwise a rollback will change - ** the table names back internally but the physical files will still - ** have the new names. - */ - if (ha_commit_stmt(thd)) - error= -1; - if (ha_commit(thd)) - error= -1; -#endif - return error; -} - /* Tell the handler to turn on or off logging to the handler's recovery log */ @@ -866,14 +840,6 @@ 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); } @@ -893,8 +859,10 @@ int handler::index_next_same(byte *buf, const byte *key, uint keylen) /* - The following is only needed if we would like to use the database - for internal temporary tables + This is called to delete all rows in a table + If the handler don't support this, then this function will + return HA_ERR_WRONG_COMMAND and MySQL will delete the rows one + by one. */ int handler::delete_all_rows() @@ -922,19 +890,21 @@ int ha_create_table(const char *name, HA_CREATE_INFO *create_info, if (update_create_info) { update_create_info_from_table(create_info, &table); - if (table.file->option_flag() & HA_DROP_BEFORE_CREATE) + if (table.file->table_flags() & HA_DROP_BEFORE_CREATE) table.file->delete_table(name); // Needed for BDB tables } error=table.file->create(name,&table,create_info); VOID(closefrm(&table)); - if (error) { - if (table.db_type == DB_TYPE_INNOBASE) { + if (error) + { + if (table.db_type == DB_TYPE_INNODB) + { /* Creation of InnoDB table cannot fail because of an OS error: put error as the number */ my_error(ER_CANT_CREATE_TABLE,MYF(ME_BELL+ME_WAITTANG),name,error); - } else { - my_error(ER_CANT_CREATE_TABLE,MYF(ME_BELL+ME_WAITTANG),name,my_errno); } + else + my_error(ER_CANT_CREATE_TABLE,MYF(ME_BELL+ME_WAITTANG),name,my_errno); } DBUG_RETURN(error != 0); } @@ -944,13 +914,25 @@ int ha_create_table(const char *name, HA_CREATE_INFO *create_info, void ha_key_cache(void) { if (keybuff_size) - (void) init_key_cache(keybuff_size,0); -} /* ha_key_cache */ + (void) init_key_cache((ulong) keybuff_size); +} + + +void ha_resize_key_cache(void) +{ + (void) resize_key_cache((ulong) keybuff_size); +} static int NEAR_F delete_file(const char *name,const char *ext,int extflag) { char buff[FN_REFLEN]; VOID(fn_format(buff,name,"",ext,extflag | 4)); - return(my_delete(buff,MYF(MY_WME))); + return(my_delete_with_symlink(buff,MYF(MY_WME))); +} + +void st_ha_check_opt::init() +{ + flags= sql_flags= 0; + sort_buffer_size = current_thd->variables.myisam_sort_buff_size; } diff --git a/sql/handler.h b/sql/handler.h index 89c19993238..8f1d00f64b5 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -21,11 +21,13 @@ #pragma interface /* gcc class implementation */ #endif +#include <ft_global.h> + #ifndef NO_HASH #define NO_HASH /* Not yet implemented */ #endif -#if defined(HAVE_BERKELEY_DB) || defined(HAVE_INNOBASE_DB) || defined(HAVE_GEMINI_DB) +#if defined(HAVE_BERKELEY_DB) || defined(HAVE_INNOBASE_DB) #define USING_TRANSACTIONS #endif @@ -39,30 +41,18 @@ #define HA_ADMIN_INTERNAL_ERROR -4 #define HA_ADMIN_INVALID -5 -/* Bits in bas_flag to show what database can do */ - -#define HA_READ_NEXT 1 /* Read next record with same key */ -#define HA_READ_PREV 2 /* Read prev. record with same key */ -#define HA_READ_ORDER 4 /* Read through record-keys in order */ -#define HA_READ_RND_SAME 8 /* Read RND-record to KEY-record +/* Bits in table_flags() to show what database can do */ +#define HA_READ_RND_SAME 1 /* Read RND-record to KEY-record (To update with RND-read) */ -#define HA_KEYPOS_TO_RNDPOS 16 /* ha_info gives pos to record */ -#define HA_LASTKEY_ORDER 32 /* Next record gives next record - according last record read (even - if database is updated after read) */ -#define HA_REC_NOT_IN_SEQ 64 /* ha_info don't return recnumber; +#define HA_KEYPOS_TO_RNDPOS 2 /* ha_info gives pos to record */ +#define HA_TABLE_SCAN_ON_INDEX 4 /* No separate data/index file */ +#define HA_REC_NOT_IN_SEQ 8 /* ha_info don't return recnumber; It returns a position to ha_r_rnd */ -#define HA_ONLY_WHOLE_INDEX 128 /* Can't use part key searches */ -#define HA_RSAME_NO_INDEX 256 /* RSAME can't restore index */ -#define HA_WRONG_ASCII_ORDER 512 /* Can't use sorting through key */ -#define HA_HAVE_KEY_READ_ONLY 1024 /* Can read only keys (no record) */ -#define HA_READ_NOT_EXACT_KEY 2048 /* Can read record after/before key */ -#define HA_NO_INDEX 4096 /* No index needed for next/prev */ -#define HA_LONGLONG_KEYS 8192 /* Can have longlong as key */ -#define HA_KEY_READ_WRONG_STR 16384 /* keyread returns converted strings */ -#define HA_NULL_KEY 32768 /* One can have keys with NULL */ -#define HA_DUPP_POS 65536 /* ha_position() gives dupp row */ -#define HA_NO_BLOBS 131072 /* Doesn't support blobs */ +#define HA_NO_INDEX 32 /* No index needed for next/prev */ +#define HA_KEY_READ_WRONG_STR 64 /* keyread returns converted strings */ +#define HA_NULL_KEY 128 /* One can have keys with NULL */ +#define HA_DUPP_POS 256 /* ha_position() gives dupp row */ +#define HA_NO_BLOBS 512 /* Doesn't support blobs */ #define HA_BLOB_KEY (HA_NO_BLOBS*2) /* key on blob */ #define HA_AUTO_PART_KEY (HA_BLOB_KEY*2) #define HA_REQUIRE_PRIMARY_KEY (HA_AUTO_PART_KEY*2) @@ -74,26 +64,45 @@ #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 an implicit HA_ABORT_IF_LOCKED */ +#define HA_CAN_FULLTEXT (HA_NO_PREFIX_CHAR_KEYS*2) +#define HA_CAN_SQL_HANDLER (HA_CAN_FULLTEXT*2) +#define HA_NO_AUTO_INCREMENT (HA_CAN_SQL_HANDLER*2) + +/* + Next record gives next record according last record read (even + if database is updated after read). Not used at this point. +*/ +#define HA_LASTKEY_ORDER (HA_NO_AUTO_INCREMENT*2) + + +/* bits in index_flags(index_number) for what you can do with index */ +#define HA_WRONG_ASCII_ORDER 1 /* Can't use sorting through key */ +#define HA_READ_NEXT 2 /* Read next record with same key */ +#define HA_READ_PREV 4 /* Read prev. record with same key */ +#define HA_READ_ORDER 8 /* Read through record-keys in order */ +#define HA_ONLY_WHOLE_INDEX 16 /* Can't use part key searches */ +#define HA_NOT_READ_PREFIX_LAST 32 /* No support for index_read_last() */ +#define HA_KEY_READ_ONLY 64 /* Support HA_EXTRA_KEYREAD */ + +/* + Parameters for open() (in register form->filestat) + HA_GET_INFO does an implicit HA_ABORT_IF_LOCKED +*/ #define HA_OPEN_KEYFILE 1 #define HA_OPEN_RNDFILE 2 #define HA_GET_INDEX 4 #define HA_GET_INFO 8 /* do a ha_info() after open */ #define HA_READ_ONLY 16 /* File opened as readonly */ -#define HA_TRY_READ_ONLY 32 /* Try readonly if can't */ - /* open with read and write */ +/* Try readonly if can't open with read and write */ +#define HA_TRY_READ_ONLY 32 #define HA_WAIT_IF_LOCKED 64 /* Wait if locked on open */ #define HA_ABORT_IF_LOCKED 128 /* skip if locked on open.*/ #define HA_BLOCK_LOCK 256 /* unlock when reading some records */ #define HA_OPEN_TEMPORARY 512 - /* Error on write which is recoverable (Key exist) */ - -#define HA_WRITE_SKIPP 121 /* Duplicate key on write */ + /* Errors on write which is recoverable (Key exist) */ +#define HA_WRITE_SKIP 121 /* Duplicate key on write */ #define HA_READ_CHECK 123 /* Update with is recoverable */ #define HA_CANT_DO_THAT 131 /* Databasehandler can't do it */ @@ -111,23 +120,27 @@ enum db_type { DB_TYPE_UNKNOWN=0,DB_TYPE_DIAB_ISAM=1, DB_TYPE_HASH,DB_TYPE_MISAM,DB_TYPE_PISAM, DB_TYPE_RMS_ISAM, DB_TYPE_HEAP, DB_TYPE_ISAM, DB_TYPE_MRG_ISAM, DB_TYPE_MYISAM, DB_TYPE_MRG_MYISAM, - DB_TYPE_BERKELEY_DB, DB_TYPE_INNOBASE, DB_TYPE_GEMINI, + DB_TYPE_BERKELEY_DB, DB_TYPE_INNODB, DB_TYPE_GEMINI, DB_TYPE_DEFAULT }; -enum row_type { ROW_TYPE_DEFAULT, ROW_TYPE_FIXED, ROW_TYPE_DYNAMIC, - ROW_TYPE_COMPRESSED }; +enum row_type { ROW_TYPE_NOT_USED=-1, ROW_TYPE_DEFAULT, ROW_TYPE_FIXED, + ROW_TYPE_DYNAMIC, ROW_TYPE_COMPRESSED}; /* struct to hold information about the table that should be created */ /* Bits in used_fields */ -#define HA_CREATE_USED_AUTO 1 -#define HA_CREATE_USED_RAID 2 -#define HA_CREATE_USED_UNION 4 +#define HA_CREATE_USED_AUTO 1 +#define HA_CREATE_USED_RAID 2 +#define HA_CREATE_USED_UNION 4 +#define HA_CREATE_USED_INSERT_METHOD 8 +#define HA_CREATE_USED_MIN_ROWS 16 +#define HA_CREATE_USED_MAX_ROWS 32 +#define HA_CREATE_USED_AVG_ROW_LENGTH 64 +#define HA_CREATE_USED_PACK_KEYS 128 typedef struct st_thd_trans { void *bdb_tid; void *innobase_tid; - void *gemini_tid; bool innodb_active_trans; } THD_TRANS; @@ -143,13 +156,14 @@ typedef struct st_ha_create_information ulonglong max_rows,min_rows; ulonglong auto_increment_value; char *comment,*password; - char *create_statement; + char *data_file_name, *index_file_name; uint options; /* OR of HA_CREATE_ options */ uint raid_type,raid_chunks; ulong raid_chunksize; bool if_not_exists; ulong used_fields; SQL_LIST merge_list; + uint merge_insert_method; } HA_CREATE_INFO; @@ -157,21 +171,13 @@ typedef struct st_ha_create_information struct st_table; typedef struct st_table TABLE; -extern ulong myisam_sort_buffer_size; typedef struct st_ha_check_opt { ulong sort_buffer_size; - uint flags; - bool quick; - bool changed_files; - bool optimize; - bool retry_without_quick; - inline void init() - { - flags= 0; quick= optimize= retry_without_quick=0; - sort_buffer_size = myisam_sort_buffer_size; - } + uint flags; /* isam layer flags (e.g. for myisamchk) */ + uint sql_flags; /* sql layer flags - for something myisamchk cannot do */ + void init(); } HA_CHECK_OPT; class handler :public Sql_alloc @@ -201,7 +207,7 @@ public: time_t check_time; time_t update_time; ulong mean_rec_length; /* physical reclength */ - void *ft_handler; + FT_INFO *ft_handler; bool auto_increment_column_changed; handler(TABLE *table_arg) : table(table_arg),active_index(MAX_REF_PARTS), @@ -221,12 +227,13 @@ public: void change_table_ptr(TABLE *table_arg) { table=table_arg; } virtual double scan_time() { return ulonglong2double(data_file_length) / IO_SIZE + 1; } - virtual double read_time(ha_rows rows) { return rows; } + virtual double read_time(ha_rows rows) { return rows2double(rows); } virtual bool fast_key_read() { return 0;} virtual key_map keys_to_use_for_scanning() { return 0; } virtual bool has_transactions(){ return 0;} virtual uint extra_rec_buf_length() { return 0; } virtual ha_rows estimate_number_of_rows() { return records+EXTRA_RECORDS; } + virtual const char *index_type(uint key_number) { return "";} virtual int index_init(uint idx) { active_index=idx; return 0;} virtual int index_end() {return 0; } @@ -238,7 +245,7 @@ public: virtual int update_row(const byte * old_data, byte * new_data)=0; virtual int delete_row(const byte * buf)=0; virtual int index_read(byte * buf, const byte * key, - uint key_len, enum ha_rkey_function find_flag)=0; + uint key_len, enum ha_rkey_function find_flag)=0; virtual int index_read_idx(byte * buf, uint index, const byte * key, uint key_len, enum ha_rkey_function find_flag)=0; virtual int index_next(byte * buf)=0; @@ -246,17 +253,21 @@ public: virtual int index_first(byte * buf)=0; virtual int index_last(byte * buf)=0; virtual int index_next_same(byte *buf, const byte *key, uint keylen); + virtual int index_read_last(byte * buf, const byte * key, uint key_len) + { + return (my_errno=HA_ERR_WRONG_COMMAND); + } virtual int ft_init() { return -1; } - virtual void *ft_init_ext(uint inx,const byte *key, uint keylen, + virtual FT_INFO *ft_init_ext(uint mode,uint inx,const byte *key, uint keylen, bool presort) - { return (void *)NULL; } + { return NULL; } virtual int ft_read(byte *buf) { return -1; } virtual int rnd_init(bool scan=1)=0; virtual int rnd_end() { return 0; } virtual int rnd_next(byte *buf)=0; virtual int rnd_pos(byte * buf, byte *pos)=0; - virtual int rnd_first(byte *buf); + virtual int read_first_row(byte *buf, uint primary_key); virtual int restart_rnd_next(byte *buf, byte *pos); virtual ha_rows records_in_range(int inx, const byte *start_key,uint start_key_len, @@ -268,6 +279,10 @@ public: virtual my_off_t row_position() { return HA_OFFSET_ERROR; } virtual void info(uint)=0; virtual int extra(enum ha_extra_function operation)=0; + virtual int extra_opt(enum ha_extra_function operation, ulong cache_size) + { + return extra(operation); + } virtual int reset()=0; virtual int external_lock(THD *thd, int lock_type)=0; virtual void unlock_row() {} @@ -281,10 +296,11 @@ public: virtual int optimize(THD* thd,HA_CHECK_OPT* check_opt); virtual int analyze(THD* thd, HA_CHECK_OPT* check_opt); virtual int backup(THD* thd, HA_CHECK_OPT* check_opt); + /* + restore assumes .frm file must exist, and that generate_table() has been + called; It will just copy the data file and run repair. + */ virtual int restore(THD* thd, HA_CHECK_OPT* check_opt); - // assumes .frm file must exist, and you must have already called - // generate_table() - it will just copy the data file and run repair - virtual int dump(THD* thd, int fd = -1) { return ER_DUMP_NOT_IMPLEMENTED; } virtual void deactivate_non_unique_index(ha_rows rows) {} virtual bool activate_all_index(THD *thd) {return 0;} @@ -296,11 +312,17 @@ public: virtual void append_create_info(String *packet) {} virtual char* get_foreign_key_create_info() { return(NULL);} /* gets foreign key create string from InnoDB */ + virtual void init_table_handle_for_HANDLER() + { return; } /* prepare InnoDB for HANDLER */ virtual void free_foreign_key_create_info(char* str) {} /* The following can be called without an open handler */ virtual const char *table_type() const =0; virtual const char **bas_ext() const =0; - virtual ulong option_flag() const =0; + virtual ulong table_flags(void) const =0; + virtual ulong index_flags(uint idx) const + { + return (HA_READ_NEXT | HA_READ_PREV | HA_READ_ORDER | HA_KEY_READ_ONLY); + } virtual uint max_record_length() const =0; virtual uint max_keys() const =0; virtual uint max_key_parts() const =0; @@ -320,17 +342,6 @@ public: enum thr_lock_type lock_type)=0; }; -#ifdef HAVE_GEMINI_DB -struct st_gemini -{ - void *context; - unsigned long savepoint; - bool needSavepoint; - uint tx_isolation; - uint lock_count; -}; -#endif - /* Some extern variables used with handlers */ extern const char *ha_row_type[]; @@ -342,6 +353,8 @@ extern TYPELIB ha_table_typelib, tx_isolation_typelib; #define ha_commit(thd) (ha_commit_trans((thd), &((thd)->transaction.all))) #define ha_rollback(thd) (ha_rollback_trans((thd), &((thd)->transaction.all))) +#define ha_supports_generate(T) (T != DB_TYPE_INNODB) + handler *get_new_handler(TABLE *table, enum db_type db_type); my_off_t ha_get_ptr(byte *ptr, uint pack_length); void ha_store_ptr(byte *buff, uint pack_length, my_off_t pos); @@ -354,15 +367,14 @@ int ha_create_table(const char *name, HA_CREATE_INFO *create_info, int ha_delete_table(enum db_type db_type, const char *path); void ha_drop_database(char* path); void ha_key_cache(void); +void ha_resize_key_cache(void); int ha_start_stmt(THD *thd); -int ha_report_binlog_offset_and_commit( - THD *thd, - char *log_file_name, - my_off_t end_offset); +int ha_report_binlog_offset_and_commit(THD *thd, char *log_file_name, + my_off_t end_offset); +int ha_release_temporary_latches(THD *thd); int ha_commit_trans(THD *thd, THD_TRANS *trans); int ha_rollback_trans(THD *thd, THD_TRANS *trans); 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/hash_filo.cc b/sql/hash_filo.cc index 990d2d662d6..b85f8054f10 100644 --- a/sql/hash_filo.cc +++ b/sql/hash_filo.cc @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ diff --git a/sql/hash_filo.h b/sql/hash_filo.h index 157c2739add..34584b45d8c 100644 --- a/sql/hash_filo.h +++ b/sql/hash_filo.h @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -24,7 +24,7 @@ #define HASH_FILO_H #ifdef __GNUC__ -#pragma interface /* gcc class implementation */ +#pragma interface /* gcc class interface */ #endif class hash_filo_element @@ -40,7 +40,7 @@ class hash_filo { const uint size, key_offset, key_length; const hash_get_key get_key; - void (*free_element)(void*); + hash_free_key free_element; bool init; hash_filo_element *first_link,*last_link; @@ -49,7 +49,7 @@ public: HASH cache; hash_filo(uint size_arg, uint key_offset_arg , uint key_length_arg, - hash_get_key get_key_arg,void (*free_element_arg)(void*)) + hash_get_key get_key_arg, hash_free_key free_element_arg) :size(size_arg), key_offset(key_offset_arg), key_length(key_length_arg), get_key(get_key_arg), free_element(free_element_arg),init(0) { diff --git a/sql/hostname.cc b/sql/hostname.cc index 21dbd5a2bbe..ed56e199c3c 100644 --- a/sql/hostname.cc +++ b/sql/hostname.cc @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -27,7 +27,9 @@ extern "C" { // Because of SCO 3.2V4.2 #endif #if !defined( __WIN__) && !defined(OS2) +#if !defined(__NETWARE__) #include <sys/resource.h> +#endif /* __NETWARE__ */ #ifdef HAVE_SYS_UN_H #include <sys/un.h> #endif @@ -57,10 +59,13 @@ void hostname_cache_refresh() bool hostname_cache_init() { + host_entry tmp; + uint offset= (uint) ((char*) (&tmp.ip) - (char*) &tmp); (void) pthread_mutex_init(&LOCK_hostname,MY_MUTEX_INIT_SLOW); - if (!(hostname_cache=new hash_filo(HOST_CACHE_SIZE,offsetof(host_entry,ip), + + if (!(hostname_cache=new hash_filo(HOST_CACHE_SIZE, offset, sizeof(struct in_addr),NULL, - (void (*)(void*)) free))) + (hash_free_key) free))) return 1; hostname_cache->clear(); return 0; diff --git a/sql/init.cc b/sql/init.cc index e6606b82b7c..df06ddd41ef 100644 --- a/sql/init.cc +++ b/sql/init.cc @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ diff --git a/sql/item.cc b/sql/item.cc index 9b4274ab71d..ec9b07c443c 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -37,7 +37,7 @@ void item_init(void) Item::Item() { marker=0; - binary=maybe_null=null_value=with_sum_func=0; + binary=maybe_null=null_value=with_sum_func=unsigned_flag=0; name=0; decimals=0; max_length=0; next=current_thd->free_list; // Put in free list @@ -132,6 +132,7 @@ void Item_field::set_field(Field *field_par) table_name=field_par->table_name; field_name=field_par->field_name; binary=field_par->binary(); + unsigned_flag=test(field_par->flags & UNSIGNED_FLAG); } const char *Item_ident::full_name() const @@ -247,6 +248,22 @@ void Item_int::print(String *str) str->append(name); } +String *Item_uint::val_str(String *str) +{ + str->set((ulonglong) value); + return str; +} + +void Item_uint::print(String *str) +{ + if (!name) + { + str_value.set((ulonglong) value); + name=str_value.c_ptr(); + } + str->append(name); +} + String *Item_real::val_str(String *str) { @@ -299,13 +316,21 @@ bool Item::fix_fields(THD *thd, bool Item_field::fix_fields(THD *thd,TABLE_LIST *tables) { - if (!field) + if (!field) // If field is not checked { Field *tmp; if (!(tmp=find_field_in_tables(thd,this,tables))) return 1; set_field(tmp); } + else if (thd && thd->set_query_id && field->query_id != thd->query_id) + { + /* We only come here in unions */ + TABLE *table=field->table; + field->query_id=thd->query_id; + table->used_fields++; + table->used_keys&=field->part_of_key; + } return 0; } @@ -319,6 +344,8 @@ void Item::init_make_field(Send_field *tmp_field, tmp_field->type=field_type; tmp_field->length=max_length; tmp_field->decimals=decimals; + if (unsigned_flag) + tmp_field->flags |= UNSIGNED_FLAG; } /* ARGSUSED */ @@ -334,6 +361,13 @@ void Item_int::make_field(Send_field *tmp_field) init_make_field(tmp_field,FIELD_TYPE_LONGLONG); } +void Item_uint::make_field(Send_field *tmp_field) +{ + init_make_field(tmp_field,FIELD_TYPE_LONGLONG); + tmp_field->flags|= UNSIGNED_FLAG; + unsigned_flag=1; +} + void Item_real::make_field(Send_field *tmp_field) { init_make_field(tmp_field,FIELD_TYPE_DOUBLE); @@ -349,12 +383,14 @@ void Item_datetime::make_field(Send_field *tmp_field) init_make_field(tmp_field,FIELD_TYPE_DATETIME); } + void Item_null::make_field(Send_field *tmp_field) { init_make_field(tmp_field,FIELD_TYPE_NULL); tmp_field->length=4; } + void Item_func::make_field(Send_field *tmp_field) { init_make_field(tmp_field, ((result_type() == STRING_RESULT) ? @@ -383,7 +419,7 @@ void Item_field::save_org_in_field(Field *to) if (field->is_null()) { null_value=1; - set_field_to_null_with_conversions(to); + set_field_to_null_with_conversions(to, 1); } else { @@ -393,12 +429,12 @@ void Item_field::save_org_in_field(Field *to) } } -bool Item_field::save_in_field(Field *to) +bool Item_field::save_in_field(Field *to, bool no_conversions) { if (result_field->is_null()) { null_value=1; - return set_field_to_null_with_conversions(to); + return set_field_to_null_with_conversions(to, no_conversions); } else { @@ -425,9 +461,9 @@ bool Item_field::save_in_field(Field *to) 1 Field doesn't support NULL values and can't handle 'field = NULL' */ -bool Item_null::save_in_field(Field *field) +bool Item_null::save_in_field(Field *field, bool no_conversions) { - return set_field_to_null_with_conversions(field); + return set_field_to_null_with_conversions(field, no_conversions); } @@ -449,7 +485,7 @@ bool Item_null::save_safe_in_field(Field *field) } -bool Item::save_in_field(Field *field) +bool Item::save_in_field(Field *field, bool no_conversions) { if (result_type() == STRING_RESULT || result_type() == REAL_RESULT && @@ -460,7 +496,7 @@ bool Item::save_in_field(Field *field) str_value.set_quick(buff,sizeof(buff)); result=val_str(&str_value); if (null_value) - return set_field_to_null_with_conversions(field); + return set_field_to_null_with_conversions(field, no_conversions); field->set_notnull(); field->store(result->ptr(),result->length()); str_value.set_quick(0, 0); @@ -477,7 +513,7 @@ bool Item::save_in_field(Field *field) { longlong nr=val_int(); if (null_value) - return set_field_to_null_with_conversions(field); + return set_field_to_null_with_conversions(field, no_conversions); field->set_notnull(); field->store(nr); } @@ -485,7 +521,7 @@ bool Item::save_in_field(Field *field) } -bool Item_string::save_in_field(Field *field) +bool Item_string::save_in_field(Field *field, bool no_conversions) { String *result; result=val_str(&str_value); @@ -496,7 +532,7 @@ bool Item_string::save_in_field(Field *field) return 0; } -bool Item_int::save_in_field(Field *field) +bool Item_int::save_in_field(Field *field, bool no_conversions) { longlong nr=val_int(); if (null_value) @@ -506,7 +542,7 @@ bool Item_int::save_in_field(Field *field) return 0; } -bool Item_real::save_in_field(Field *field) +bool Item_real::save_in_field(Field *field, bool no_conversions) { double nr=val(); if (null_value) @@ -561,7 +597,7 @@ longlong Item_varbinary::val_int() } -bool Item_varbinary::save_in_field(Field *field) +bool Item_varbinary::save_in_field(Field *field, bool no_conversions) { field->set_notnull(); if (field->result_type() == STRING_RESULT) @@ -586,19 +622,19 @@ void Item_varbinary::make_field(Send_field *tmp_field) ** pack data in buffer for sending */ -bool Item::send(String *packet) +bool Item::send(THD *thd, String *packet) { char buff[MAX_FIELD_WIDTH]; + CONVERT *convert; String s(buff,sizeof(buff)),*res; if (!(res=val_str(&s))) return net_store_null(packet); - CONVERT *convert; - if ((convert=current_thd->convert_set)) + if ((convert=thd->variables.convert_set)) return convert->store(packet,res->ptr(),res->length()); return net_store_data(packet,res->ptr(),res->length()); } -bool Item_null::send(String *packet) +bool Item_null::send(THD *thd, String *packet) { return net_store_null(packet); } @@ -612,7 +648,7 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables) { if (!ref) { - if (!(ref=find_item_in_list(this,thd->lex.item_list))) + if (!(ref=find_item_in_list(this,thd->lex.select->item_list))) return 1; max_length= (*ref)->max_length; maybe_null= (*ref)->maybe_null; @@ -622,9 +658,10 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables) return 0; } + /* -** If item is a const function, calculate it and return a const item -** The original item is freed if not returned + If item is a const function, calculate it and return a const item + The original item is freed if not returned */ Item_result item_cmp_type(Item_result a,Item_result b) @@ -728,5 +765,6 @@ bool field_is_equal_to_item(Field *field,Item *item) #ifdef __GNUC__ template class List<Item>; template class List_iterator<Item>; +template class List_iterator_fast<Item>; template class List<List_item>; #endif diff --git a/sql/item.h b/sql/item.h index 408010d211a..a72079a6856 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -23,7 +23,7 @@ struct st_table_list; void item_init(void); /* Init item functions */ class Item { - Item(const Item &); /* Prevent use of theese */ + Item(const Item &); /* Prevent use of these */ void operator=(Item &); public: static void *operator new(size_t size) {return (void*) sql_alloc((uint) size); } @@ -31,7 +31,7 @@ public: enum Type {FIELD_ITEM,FUNC_ITEM,SUM_FUNC_ITEM,STRING_ITEM, INT_ITEM,REAL_ITEM,NULL_ITEM,VARBIN_ITEM, - COPY_STR_ITEM,FIELD_AVG_ITEM, + COPY_STR_ITEM,FIELD_AVG_ITEM, DEFAULT_ITEM, PROC_ITEM,COND_ITEM,REF_ITEM,FIELD_STD_ITEM, CONST_ITEM}; enum cond_result { COND_UNDEF,COND_OK,COND_TRUE,COND_FALSE }; @@ -43,6 +43,7 @@ public: my_bool maybe_null; /* If item may be null */ my_bool null_value; /* if item is null */ my_bool binary; + my_bool unsigned_flag; my_bool with_sum_func; @@ -52,12 +53,12 @@ public: void set_name(char* str,uint length=0); void init_make_field(Send_field *tmp_field,enum enum_field_types type); virtual bool fix_fields(THD *,struct st_table_list *); - virtual bool save_in_field(Field *field); + virtual bool save_in_field(Field *field, bool no_conversions); virtual void save_org_in_field(Field *field) - { (void) save_in_field(field); } + { (void) save_in_field(field, 1); } virtual bool save_safe_in_field(Field *field) - { return save_in_field(field); } - virtual bool send(String *str); + { return save_in_field(field, 1); } + virtual bool send(THD *thd, String *str); virtual bool eq(const Item *, bool binary_cmp) const; virtual Item_result result_type () const { return REAL_RESULT; } virtual enum Type type() const =0; @@ -65,7 +66,7 @@ public: virtual longlong val_int()=0; virtual String *val_str(String*)=0; virtual void make_field(Send_field *field)=0; - virtual Field *tmp_table_field() { return 0; } + virtual Field *tmp_table_field(TABLE *t_arg=(TABLE *)0) { return 0; } virtual const char *full_name() const { return name ? name : "???"; } virtual double val_result() { return val(); } virtual longlong val_int_result() { return val_int(); } @@ -82,6 +83,13 @@ public: virtual void split_sum_func(List<Item> &fields) {} virtual bool get_date(TIME *ltime,bool fuzzydate); virtual bool get_time(TIME *ltime); + virtual bool is_null() { return 0; } + virtual unsigned int size_of()= 0; + virtual void top_level_item() {} + virtual void set_result_field(Field *field) {} + virtual bool is_result_field() { return 0; } + virtual void save_in_result_field(bool no_conversions) {} + virtual void no_rows_in_result() {} }; @@ -96,8 +104,10 @@ public: :db_name(db_name_par),table_name(table_name_par),field_name(field_name_par) { name = (char*) field_name_par; } const char *full_name() const; + unsigned int size_of() { return sizeof(*this);} }; + class Item_field :public Item_ident { void set_field(Field *field); @@ -118,19 +128,24 @@ public: double val_result(); longlong val_int_result(); String *str_result(String* tmp); - bool send(String *str_arg) { return result_field->send(str_arg); } + bool send(THD *thd, String *str_arg) + { + return result_field->send(thd,str_arg); + } void make_field(Send_field *field); bool fix_fields(THD *,struct st_table_list *); - bool save_in_field(Field *field); + bool save_in_field(Field *field,bool no_conversions); void save_org_in_field(Field *field); table_map used_tables() const; enum Item_result result_type () const { return field->result_type(); } - Field *tmp_table_field() { return result_field; } + Field *tmp_table_field(TABLE *t_arg=(TABLE *)0) { return result_field; } bool get_date(TIME *ltime,bool fuzzydate); bool get_time(TIME *ltime); + bool is_null() { return field->is_null(); } + unsigned int size_of() { return sizeof(*this);} }; @@ -145,13 +160,15 @@ public: longlong val_int(); String *val_str(String *str); void make_field(Send_field *field); - bool save_in_field(Field *field); + bool save_in_field(Field *field, bool no_conversions); bool save_safe_in_field(Field *field); enum Item_result result_type () const { return STRING_RESULT; } - bool send(String *str); + bool send(THD *thd, String *str); bool basic_const_item() const { return 1; } Item *new_item() { return new Item_null(name); } + bool is_null() { return 1; } + unsigned int size_of() { return sizeof(*this);} }; @@ -177,10 +194,31 @@ public: double val() { return (double) value; } String *val_str(String*); void make_field(Send_field *field); - bool save_in_field(Field *field); + bool save_in_field(Field *field, bool no_conversions); bool basic_const_item() const { return 1; } Item *new_item() { return new Item_int(name,value,max_length); } void print(String *str); + unsigned int size_of() { return sizeof(*this);} +}; + + +class Item_uint :public Item_int +{ +public: + Item_uint(const char *str_arg, uint length) : + Item_int(str_arg, (longlong) strtoull(str_arg,(char**) 0,10), length) {} + Item_uint(uint32 i) :Item_int((longlong) i, 10) {} + double val() { return ulonglong2double(value); } + String *val_str(String*); + void make_field(Send_field *field); + Item *new_item() { return new Item_uint(name,max_length); } + bool fix_fields(THD *thd,struct st_table_list *table_list) + { + unsigned_flag= 1; + return 0; + } + void print(String *str); + unsigned int size_of() { return sizeof(*this);} }; @@ -192,18 +230,18 @@ public: Item_real(const char *str_arg,uint length) :value(atof(str_arg)) { name=(char*) str_arg; - decimals=nr_of_decimals(str_arg); + decimals=(uint8) nr_of_decimals(str_arg); max_length=length; } Item_real(const char *str,double val_arg,uint decimal_par,uint length) :value(val_arg) { name=(char*) str; - decimals=decimal_par; + decimals=(uint8) decimal_par; max_length=length; } Item_real(double value_par) :value(value_par) {} - bool save_in_field(Field *field); + bool save_in_field(Field *field, bool no_conversions); enum Type type() const { return REAL_ITEM; } double val() { return value; } longlong val_int() { return (longlong) (value+(value > 0 ? 0.5 : -0.5));} @@ -211,6 +249,7 @@ public: void make_field(Send_field *field); bool basic_const_item() const { return 1; } Item *new_item() { return new Item_real(name,value,decimals,max_length); } + unsigned int size_of() { return sizeof(*this);} }; @@ -222,6 +261,7 @@ public: decimals=NOT_FIXED_DEC; max_length=DBL_DIG+8; } + unsigned int size_of() { return sizeof(*this);} }; class Item_string :public Item @@ -246,7 +286,7 @@ public: double val() { return atof(str_value.ptr()); } longlong val_int() { return strtoll(str_value.ptr(),(char**) 0,10); } String *val_str(String*) { return (String*) &str_value; } - bool save_in_field(Field *field); + bool save_in_field(Field *field, bool no_conversions); void make_field(Send_field *field); enum Item_result result_type () const { return STRING_RESULT; } bool basic_const_item() const { return 1; } @@ -255,8 +295,31 @@ public: String *const_string() { return &str_value; } inline void append(char *str,uint length) { str_value.append(str,length); } void print(String *str); + unsigned int size_of() { return sizeof(*this);} }; + +/* For INSERT ... VALUES (DEFAULT) */ + +class Item_default :public Item +{ +public: + Item_default() { name= (char*) "DEFAULT"; } + enum Type type() const { return DEFAULT_ITEM; } + void make_field(Send_field *field) {} + bool save_in_field(Field *field, bool no_conversions) + { + field->set_default(); + return 0; + } + virtual double val() { return 0.0; } + virtual longlong val_int() { return 0; } + virtual String *val_str(String *str) { return 0; } + bool basic_const_item() const { return 1; } + unsigned int size_of() { return sizeof(*this);} +}; + + /* for show tables */ class Item_datetime :public Item_string @@ -265,6 +328,7 @@ public: Item_datetime(const char *item_name): Item_string(item_name,"",0) { max_length=19;} void make_field(Send_field *field); + unsigned int size_of() { return sizeof(*this);} }; class Item_empty_string :public Item_string @@ -272,6 +336,7 @@ class Item_empty_string :public Item_string public: Item_empty_string(const char *header,uint length) :Item_string("",0) { name=(char*) header; max_length=length;} + unsigned int size_of() { return sizeof(*this);} }; class Item_varbinary :public Item @@ -283,9 +348,10 @@ public: double val() { return (double) Item_varbinary::val_int(); } longlong val_int(); String *val_str(String*) { return &str_value; } - bool save_in_field(Field *field); + bool save_in_field(Field *field, bool no_conversions); void make_field(Send_field *field); enum Item_result result_type () const { return INT_RESULT; } + unsigned int size_of() { return sizeof(*this);} }; @@ -295,15 +361,23 @@ public: Field *result_field; /* Save result here */ Item_result_field() :result_field(0) {} ~Item_result_field() {} /* Required with gcc 2.95 */ - Field *tmp_table_field() { return result_field; } + Field *tmp_table_field(TABLE *t_arg=(TABLE *)0) { return result_field; } table_map used_tables() const { return 1; } virtual void fix_length_and_dec()=0; + unsigned int size_of() { return sizeof(*this);} + void set_result_field(Field *field) { result_field= field; } + bool is_result_field() { return 1; } + void save_in_result_field(bool no_conversions) + { + save_in_field(result_field, no_conversions); + } }; class Item_ref :public Item_ident { public: + Field *result_field; /* Save result here */ Item **ref; Item_ref(char *db_par,char *table_name_par,char *field_name_par) :Item_ident(db_par,table_name_par,field_name_par),ref(0) {} @@ -331,17 +405,30 @@ public: null_value=(*ref)->null_value; return tmp; } + bool is_null() + { + (void) (*ref)->val_int_result(); + return (*ref)->null_value; + } bool get_date(TIME *ltime,bool fuzzydate) { return (null_value=(*ref)->get_date(ltime,fuzzydate)); } - bool send(String *tmp) { return (*ref)->send(tmp); } + bool send(THD *thd, String *tmp) { return (*ref)->send(thd, tmp); } void make_field(Send_field *field) { (*ref)->make_field(field); } bool fix_fields(THD *,struct st_table_list *); - bool save_in_field(Field *field) { return (*ref)->save_in_field(field); } + bool save_in_field(Field *field, bool no_conversions) + { return (*ref)->save_in_field(field, no_conversions); } void save_org_in_field(Field *field) { (*ref)->save_org_in_field(field); } enum Item_result result_type () const { return (*ref)->result_type(); } table_map used_tables() const { return (*ref)->used_tables(); } + unsigned int size_of() { return sizeof(*this);} + void set_result_field(Field *field) { result_field= field; } + bool is_result_field() { return 1; } + void save_in_result_field(bool no_conversions) + { + (*ref)->save_in_field(result_field, no_conversions); + } }; @@ -357,10 +444,11 @@ class Item_int_with_ref :public Item_int public: Item_int_with_ref(longlong i, Item *ref_arg) :Item_int(i), ref(ref_arg) {} - bool save_in_field(Field *field) + bool save_in_field(Field *field, bool no_conversions) { - return ref->save_in_field(field); + return ref->save_in_field(field, no_conversions); } + unsigned int size_of() { return sizeof(*this);} }; @@ -394,6 +482,8 @@ public: void copy(); table_map used_tables() const { return (table_map) 1L; } bool const_item() const { return 0; } + bool is_null() { return null_value; } + unsigned int size_of() { return sizeof(*this);} }; @@ -404,6 +494,7 @@ public: Item_buff() :null_value(0) {} virtual bool cmp(void)=0; virtual ~Item_buff(); /*line -e1509 */ + unsigned int size_of() { return sizeof(*this);} }; class Item_str_buff :public Item_buff @@ -414,6 +505,7 @@ public: Item_str_buff(Item *arg) :item(arg),value(arg->max_length) {} bool cmp(void); ~Item_str_buff(); // Deallocate String:s + unsigned int size_of() { return sizeof(*this);} }; @@ -424,6 +516,7 @@ class Item_real_buff :public Item_buff public: Item_real_buff(Item *item_par) :item(item_par),value(0.0) {} bool cmp(void); + unsigned int size_of() { return sizeof(*this);} }; class Item_int_buff :public Item_buff @@ -433,6 +526,7 @@ class Item_int_buff :public Item_buff public: Item_int_buff(Item *item_par) :item(item_par),value(0) {} bool cmp(void); + unsigned int size_of() { return sizeof(*this);} }; @@ -449,10 +543,10 @@ public: buff= (char*) sql_calloc(length=field->pack_length()); } bool cmp(void); + unsigned int size_of() { return sizeof(*this);} }; extern Item_buff *new_Item_buff(Item *item); extern Item_result item_cmp_type(Item_result a,Item_result b); extern Item *resolve_const_item(Item *item,Item *cmp_item); extern bool field_is_equal_to_item(Field *field,Item *item); -Item *get_system_var(LEX_STRING name); diff --git a/sql/item_buff.cc b/sql/item_buff.cc index 61e1f5498a9..b55a4dc66a0 100644 --- a/sql/item_buff.cc +++ b/sql/item_buff.cc @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index e82dc3f90ce..55e8ef7c4b5 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -26,8 +26,8 @@ /* Test functions - These returns 0LL if false and 1LL if true and null if some arg is null - 'AND' and 'OR' never return null + Most of these returns 0LL if false and 1LL if true and + NULL if some arg is NULL. */ longlong Item_func_not::val_int() @@ -37,18 +37,24 @@ longlong Item_func_not::val_int() return !null_value && value == 0 ? 1 : 0; } +/* + Convert a constant expression or string to an integer. + This is done when comparing DATE's of different formats and + also when comparing bigint to strings (in which case the string + is converted once to a bigint). +*/ static bool convert_constant_item(Field *field, Item **item) { - if ((*item)->const_item()) + if ((*item)->const_item() && (*item)->type() != Item::INT_ITEM) { - (*item)->save_in_field(field); - if (!((*item)->null_value)) + if (!(*item)->save_in_field(field, 1) && + !((*item)->null_value)) { Item *tmp=new Item_int_with_ref(field->val_int(), *item); if (tmp) *item=tmp; - return 1; + return 1; // Item was replaced } } return 0; @@ -57,7 +63,7 @@ static bool convert_constant_item(Field *field, Item **item) void Item_bool_func2::fix_length_and_dec() { - max_length=1; + max_length=1; // Function returns 0 or 1 /* As some compare functions are generated after sql_yacc, @@ -256,7 +262,7 @@ longlong Item_func_strcmp::val_int() null_value=1; return 0; } - int value=stringcmp(a,b); + int value= binary ? stringcmp(a,b) : sortcmp(a,b); null_value=0; return !value ? 0 : (value < 0 ? (longlong) -1 : (longlong) 1); } @@ -357,13 +363,19 @@ void Item_func_between::fix_length_and_dec() */ if (!args[0] || !args[1] || !args[2]) return; - cmp_type=args[0]->result_type(); - if (args[0]->binary) + cmp_type=item_cmp_type(args[0]->result_type(), + item_cmp_type(args[1]->result_type(), + args[2]->result_type())); + if (args[0]->binary | args[1]->binary | args[2]->binary) string_compare=stringcmp; else string_compare=sortcmp; - // Make a special case of compare with fields to get nicer DATE comparisons + /* + Make a special case of compare with date/time and longlong fields. + They are compared as integers, so for const item this time-consuming + conversion can be done only once, not for every single comparison + */ if (args[0]->type() == FIELD_ITEM) { Field *field=((Item_field*) args[0])->field; @@ -445,15 +457,29 @@ longlong Item_func_between::val_int() return 0; } +static Item_result item_store_type(Item_result a,Item_result b) +{ + if (a == STRING_RESULT || b == STRING_RESULT) + return STRING_RESULT; + else if (a == REAL_RESULT || b == REAL_RESULT) + return REAL_RESULT; + else + return INT_RESULT; +} + void Item_func_ifnull::fix_length_and_dec() { maybe_null=args[1]->maybe_null; max_length=max(args[0]->max_length,args[1]->max_length); decimals=max(args[0]->decimals,args[1]->decimals); - cached_result_type=args[0]->result_type(); + if ((cached_result_type=item_store_type(args[0]->result_type(), + args[1]->result_type())) != + REAL_RESULT) + decimals= 0; } + double Item_func_ifnull::val() { @@ -499,6 +525,7 @@ Item_func_ifnull::val_str(String *str) return res; } + void Item_func_if::fix_length_and_dec() { @@ -507,17 +534,32 @@ Item_func_if::fix_length_and_dec() 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(); + bool null1=args[1]->null_value; + bool null2=args[2]->null_value; - binary=1; - if (arg1_type == STRING_RESULT || arg2_type == STRING_RESULT) + if (null1) + { + cached_result_type= arg2_type; + binary= args[2]->binary; + } + else if (null2) + { + cached_result_type= arg1_type; + binary= args[1]->binary; + } + else if (arg1_type == STRING_RESULT || arg2_type == STRING_RESULT) { cached_result_type = STRING_RESULT; binary=args[1]->binary | args[2]->binary; } - else if (arg1_type == REAL_RESULT || arg2_type == REAL_RESULT) - cached_result_type = REAL_RESULT; else - cached_result_type=arg1_type; // Should be INT_RESULT + { + binary=1; // Number + if (arg1_type == REAL_RESULT || arg2_type == REAL_RESULT) + cached_result_type = REAL_RESULT; + else + cached_result_type=arg1_type; // Should be INT_RESULT + } } @@ -563,7 +605,7 @@ Item_func_nullif::fix_length_and_dec() } /* - nullif() returns NULL if arguments are different, else it returns the + nullif () returns NULL if arguments are different, else it returns the first argument. Note that we have to evaluate the first argument twice as the compare may have been done with a different type than return value @@ -840,8 +882,9 @@ String *Item_func_coalesce::val_str(String *str) null_value=0; for (uint i=0 ; i < arg_count ; i++) { - if (args[i]->val_str(str) != NULL) - return args[i]->val_str(str); + String *res; + if ((res=args[i]->val_str(str))) + return res; } null_value=1; return 0; @@ -1005,7 +1048,7 @@ void Item_func_in::fix_length_and_dec() for (uint i=0 ; i < arg_count ; i++) { array->set(j,args[i]); - if (!args[i]->null_value) // Skipp NULL values + if (!args[i]->null_value) // Skip NULL values j++; } if ((array->used_count=j)) @@ -1146,6 +1189,8 @@ Item_cond::fix_fields(THD *thd,TABLE_LIST *tables) #endif item= *li.ref(); // new current item } + if (abort_on_null) + item->top_level_item(); if (item->fix_fields(thd,tables)) return 1; /* purecov: inspected */ used_tables_cache|=item->used_tables(); @@ -1193,7 +1238,7 @@ void Item_cond::update_used_tables() { used_tables_cache=0; const_item_cache=1; - List_iterator<Item> li(list); + List_iterator_fast<Item> li(list); Item *item; while ((item=li++)) { @@ -1207,7 +1252,7 @@ void Item_cond::update_used_tables() void Item_cond::print(String *str) { str->append('('); - List_iterator<Item> li(list); + List_iterator_fast<Item> li(list); Item *item; if ((item=li++)) item->print(str); @@ -1221,31 +1266,44 @@ void Item_cond::print(String *str) str->append(')'); } +/* + Evalution of AND(expr, expr, expr ...) + + NOTES: + abort_if_null is set for AND expressions for which we don't care if the + result is NULL or 0. This is set for: + - WHERE clause + - HAVING clause + - IF(expression) + + RETURN VALUES + 1 If all expressions are true + 0 If all expressions are false or if we find a NULL expression and + 'abort_on_null' is set. + NULL if all expression are either 1 or NULL +*/ + longlong Item_cond_and::val_int() { - List_iterator<Item> li(list); + List_iterator_fast<Item> li(list); Item *item; + null_value= 0; while ((item=li++)) { if (item->val_int() == 0) { - /* - TODO: In case of NULL, ANSI would require us to continue evaluation - until we get a FALSE value or run out of values; This would - require a lot of unnecessary evaluation, which we skip for now - */ - null_value=item->null_value; - return 0; + if (abort_on_null || !(null_value= item->null_value)) + return 0; // return FALSE } } - null_value=0; - return 1; + return null_value ? 0 : 1; } + longlong Item_cond_or::val_int() { - List_iterator<Item> li(list); + List_iterator_fast<Item> li(list); Item *item; null_value=0; while ((item=li++)) @@ -1308,14 +1366,12 @@ longlong Item_func_isnull::val_int() */ if (!used_tables_cache) return cached_value; - (void) args[0]->val(); - return (args[0]->null_value) ? 1 : 0; + return args[0]->is_null() ? 1: 0; } longlong Item_func_isnotnull::val_int() { - (void) args[0]->val(); - return !(args[0]->null_value) ? 1 : 0; + return args[0]->is_null() ? 0 : 1; } @@ -1325,23 +1381,23 @@ void Item_func_like::fix_length_and_dec() // cmp_type=STRING_RESULT; // For quick select } - longlong Item_func_like::val_int() { - String *res,*res2; - res=args[0]->val_str(&tmp_value1); + String* res = args[0]->val_str(&tmp_value1); if (args[0]->null_value) { null_value=1; return 0; } - res2=args[1]->val_str(&tmp_value2); + String* res2 = args[1]->val_str(&tmp_value2); if (args[1]->null_value) { null_value=1; return 0; } null_value=0; + if (canDoTurboBM) + return turboBM_matches(res->ptr(), res->length()) ? 1 : 0; if (binary) return wild_compare(*res,*res2,escape) ? 0 : 1; else @@ -1365,6 +1421,55 @@ Item_func::optimize_type Item_func_like::select_optimize() const return OPTIMIZE_NONE; } +bool Item_func_like::fix_fields(THD *thd,struct st_table_list *tlist) +{ + if (Item_bool_func2::fix_fields(thd, tlist)) + return 1; + + /* + TODO--we could do it for non-const, but we'd have to + recompute the tables for each row--probably not worth it. + */ + if (args[1]->const_item() && !(specialflag & SPECIAL_NO_NEW_FUNC)) + { + String* res2 = args[1]->val_str(&tmp_value2); + if (!res2) + return 0; // Null argument + + const size_t len = res2->length(); + const char* first = res2->ptr(); + const char* last = first + len - 1; + /* + len must be > 2 ('%pattern%') + heuristic: only do TurboBM for pattern_len > 2 + */ + + if (len > MIN_TURBOBM_PATTERN_LEN + 2 && + *first == wild_many && + *last == wild_many) + { + const char* tmp = first + 1; + for (; *tmp != wild_many && *tmp != wild_one && *tmp != escape; tmp++) ; + canDoTurboBM = tmp == last; + } + + if (canDoTurboBM) + { + pattern = first + 1; + pattern_len = len - 2; + DBUG_PRINT("info", ("Initializing pattern: '%s'", first)); + int *suff = (int*) thd->alloc(sizeof(int)*((pattern_len + 1)*2+ + alphabet_size)); + bmGs = suff + pattern_len + 1; + bmBc = bmGs + pattern_len + 1; + turboBM_compute_good_suffix_shifts(suff); + turboBM_compute_bad_character_shifts(); + DBUG_PRINT("info",("done")); + } + } + return 0; +} + #ifdef USE_REGEX bool @@ -1404,7 +1509,6 @@ Item_func_regex::fix_fields(THD *thd,TABLE_LIST *tables) return 0; } - longlong Item_func_regex::val_int() { char buff[MAX_FIELD_WIDTH]; @@ -1461,3 +1565,259 @@ Item_func_regex::~Item_func_regex() } #endif /* USE_REGEX */ + + +#ifdef LIKE_CMP_TOUPPER +#define likeconv(A) (uchar) toupper(A) +#else +#define likeconv(A) (uchar) my_sort_order[(uchar) (A)] +#endif + + +/********************************************************************** + turboBM_compute_suffixes() + Precomputation dependent only on pattern_len. +**********************************************************************/ + +void Item_func_like::turboBM_compute_suffixes(int *suff) +{ + const int plm1 = pattern_len - 1; + int f = 0; + int g = plm1; + int *const splm1 = suff + plm1; + + *splm1 = pattern_len; + + if (binary) + { + int i; + for (i = pattern_len - 2; i >= 0; i--) + { + int tmp = *(splm1 + i - f); + if (g < i && tmp < i - g) + suff[i] = tmp; + else + { + if (i < g) + g = i; // g = min(i, g) + f = i; + while (g >= 0 && pattern[g] == pattern[g + plm1 - f]) + g--; + suff[i] = f - g; + } + } + } + else + { + int i; + for (i = pattern_len - 2; 0 <= i; --i) + { + int tmp = *(splm1 + i - f); + if (g < i && tmp < i - g) + suff[i] = tmp; + else + { + if (i < g) + g = i; // g = min(i, g) + f = i; + while (g >= 0 && + likeconv(pattern[g]) == likeconv(pattern[g + plm1 - f])) + g--; + suff[i] = f - g; + } + } + } +} + + +/********************************************************************** + turboBM_compute_good_suffix_shifts() + Precomputation dependent only on pattern_len. +**********************************************************************/ + +void Item_func_like::turboBM_compute_good_suffix_shifts(int *suff) +{ + turboBM_compute_suffixes(suff); + + int *end = bmGs + pattern_len; + int *k; + for (k = bmGs; k < end; k++) + *k = pattern_len; + + int tmp; + int i; + int j = 0; + const int plm1 = pattern_len - 1; + for (i = plm1; i > -1; i--) + { + if (suff[i] == i + 1) + { + for (tmp = plm1 - i; j < tmp; j++) + { + int *tmp2 = bmGs + j; + if (*tmp2 == pattern_len) + *tmp2 = tmp; + } + } + } + + int *tmp2; + for (tmp = plm1 - i; j < tmp; j++) + { + tmp2 = bmGs + j; + if (*tmp2 == pattern_len) + *tmp2 = tmp; + } + + tmp2 = bmGs + plm1; + for (i = 0; i <= pattern_len - 2; i++) + *(tmp2 - suff[i]) = plm1 - i; +} + + +/********************************************************************** + turboBM_compute_bad_character_shifts() + Precomputation dependent on pattern_len. +**********************************************************************/ + +void Item_func_like::turboBM_compute_bad_character_shifts() +{ + int *i; + int *end = bmBc + alphabet_size; + for (i = bmBc; i < end; i++) + *i = pattern_len; + + int j; + const int plm1 = pattern_len - 1; + if (binary) + { + for (j = 0; j < plm1; j++) + bmBc[(uint) (uchar) pattern[j]] = plm1 - j; + } + else + { + for (j = 0; j < plm1; j++) + bmBc[(uint) likeconv(pattern[j])] = plm1 - j; + } +} + + +/********************************************************************** + turboBM_matches() + Search for pattern in text, returns true/false for match/no match +**********************************************************************/ + +bool Item_func_like::turboBM_matches(const char* text, int text_len) const +{ + register int bcShift; + register int turboShift; + int shift = pattern_len; + int j = 0; + int u = 0; + + const int plm1= pattern_len - 1; + const int tlmpl= text_len - pattern_len; + + /* Searching */ + if (binary) + { + while (j <= tlmpl) + { + register int i= plm1; + while (i >= 0 && pattern[i] == text[i + j]) + { + i--; + if (i == plm1 - shift) + i-= u; + } + if (i < 0) + return 1; + + register const int v = plm1 - i; + turboShift = u - v; + bcShift = bmBc[(uint) (uchar) text[i + j]] - plm1 + i; + shift = max(turboShift, bcShift); + shift = max(shift, bmGs[i]); + if (shift == bmGs[i]) + u = min(pattern_len - shift, v); + else + { + if (turboShift < bcShift) + shift = max(shift, u + 1); + u = 0; + } + j+= shift; + } + return 0; + } + else + { + while (j <= tlmpl) + { + register int i = plm1; + while (i >= 0 && likeconv(pattern[i]) == likeconv(text[i + j])) + { + i--; + if (i == plm1 - shift) + i-= u; + } + if (i < 0) + return 1; + + register const int v = plm1 - i; + turboShift = u - v; + bcShift = bmBc[(uint) likeconv(text[i + j])] - plm1 + i; + shift = max(turboShift, bcShift); + shift = max(shift, bmGs[i]); + if (shift == bmGs[i]) + u = min(pattern_len - shift, v); + else + { + if (turboShift < bcShift) + shift = max(shift, u + 1); + u = 0; + } + j+= shift; + } + return 0; + } +} + + +/* + Make a logical XOR of the arguments. + + SYNOPSIS + val_int() + + DESCRIPTION + If either operator is NULL, return NULL. + + NOTE + As we don't do any index optimization on XOR this is not going to be + very fast to use. + + TODO (low priority) + Change this to be optimized as: + A XOR B -> (A) == 1 AND (B) <> 1) OR (A <> 1 AND (B) == 1) + To be able to do this, we would however first have to extend the MySQL + range optimizer to handle OR better. +*/ + +longlong Item_cond_xor::val_int() +{ + List_iterator<Item> li(list); + Item *item; + int result=0; + null_value=0; + while ((item=li++)) + { + result^= (item->val_int() != 0); + if (item->null_value) + { + null_value=1; + return 0; + } + } + return (longlong) result; +} diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index c33042e11ab..f7ade97940c 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -28,6 +28,7 @@ public: Item_bool_func(Item *a) :Item_int_func(a) {} Item_bool_func(Item *a,Item *b) :Item_int_func(a,b) {} void fix_length_and_dec() { decimals=0; max_length=1; } + unsigned int size_of() { return sizeof(*this);} }; class Item_bool_func2 :public Item_int_func @@ -46,6 +47,8 @@ public: virtual enum Functype rev_functype() const { return UNKNOWN_FUNC; } bool have_rev_func() const { return rev_functype() != UNKNOWN_FUNC; } void print(String *str) { Item_func::print_op(str); } + bool is_null() { return test(args[0]->is_null() || args[1]->is_null()); } + unsigned int size_of() { return sizeof(*this);} }; @@ -79,6 +82,7 @@ public: enum Functype rev_functype() const { return EQUAL_FUNC; } cond_result eq_cmp_result() const { return COND_TRUE; } const char *func_name() const { return "<=>"; } + unsigned int size_of() { return sizeof(*this);} }; @@ -185,6 +189,7 @@ public: ~Item_func_interval() { delete item; } const char *func_name() const { return "interval"; } void update_used_tables(); + unsigned int size_of() { return sizeof(*this);} }; @@ -199,6 +204,7 @@ public: enum Item_result result_type () const { return cached_result_type; } void fix_length_and_dec(); const char *func_name() const { return "ifnull"; } + unsigned int size_of() { return sizeof(*this);} }; @@ -211,8 +217,14 @@ public: longlong val_int(); String *val_str(String *str); enum Item_result result_type () const { return cached_result_type; } + bool fix_fields(THD *thd,struct st_table_list *tlist) + { + args[0]->top_level_item(); + return Item_func::fix_fields(thd,tlist); + } void fix_length_and_dec(); const char *func_name() const { return "if"; } + unsigned int size_of() { return sizeof(*this);} }; @@ -227,6 +239,7 @@ public: enum Item_result result_type () const { return cached_result_type; } void fix_length_and_dec(); const char *func_name() const { return "nullif"; } + unsigned int size_of() { return sizeof(*this);} }; @@ -241,6 +254,7 @@ public: void fix_length_and_dec(); enum Item_result result_type () const { return cached_result_type; } const char *func_name() const { return "coalesce"; } + unsigned int size_of() { return sizeof(*this);} }; class Item_func_case :public Item_func @@ -262,6 +276,7 @@ public: bool fix_fields(THD *thd,struct st_table_list *tlist); void split_sum_func(List<Item> &fields); Item *find_item(String *str); + unsigned int size_of() { return sizeof(*this);} }; @@ -284,9 +299,9 @@ public: virtual void set(uint pos,Item *item)=0; virtual byte *get_value(Item *item)=0; void sort() - { - qsort(base,used_count,size,compare); - } + { + qsort(base,used_count,size,compare); + } int find(Item *item); }; @@ -426,6 +441,7 @@ class Item_func_in :public Item_int_func const char *func_name() const { return " IN "; } void update_used_tables(); void split_sum_func(List<Item> &fields); + unsigned int size_of() { return sizeof(*this);} }; @@ -459,12 +475,12 @@ public: if (!(used_tables_cache=args[0]->used_tables())) { /* Remember if the value is always NULL or never NULL */ - args[0]->val(); - cached_value= args[0]->null_value ? (longlong) 1 : (longlong) 0; + cached_value= (longlong) args[0]->is_null(); } } } optimize_type select_optimize() const { return OPTIMIZE_NULL; } + unsigned int size_of() { return sizeof(*this);} }; class Item_func_isnotnull :public Item_bool_func @@ -473,16 +489,38 @@ public: Item_func_isnotnull(Item *a) :Item_bool_func(a) {} longlong val_int(); enum Functype functype() const { return ISNOTNULL_FUNC; } - void fix_length_and_dec() { decimals=0; max_length=1; maybe_null=0; } + void fix_length_and_dec() + { + decimals=0; max_length=1; maybe_null=0; + } const char *func_name() const { return "isnotnull"; } optimize_type select_optimize() const { return OPTIMIZE_NULL; } + unsigned int size_of() { return sizeof(*this);} }; class Item_func_like :public Item_bool_func2 { char escape; + + // Turbo Boyer-Moore data + bool canDoTurboBM; // pattern is '%abcd%' case + const char* pattern; + int pattern_len; + + // TurboBM buffers, *this is owner + int* bmGs; // good suffix shift table, size is pattern_len + 1 + int* bmBc; // bad character shift table, size is alphabet_size + + void turboBM_compute_suffixes(int* suff); + void turboBM_compute_good_suffix_shifts(int* suff); + void turboBM_compute_bad_character_shifts(); + bool turboBM_matches(const char* text, int text_len) const; + enum { alphabet_size = 256 }; + public: - Item_func_like(Item *a,Item *b, char* escape_arg) :Item_bool_func2(a,b),escape(*escape_arg) + Item_func_like(Item *a,Item *b, char* escape_arg) + :Item_bool_func2(a,b), escape(*escape_arg), canDoTurboBM(false), + pattern(0), pattern_len(0), bmGs(0), bmBc(0) {} longlong val_int(); enum Functype functype() const { return LIKE_FUNC; } @@ -490,6 +528,8 @@ public: cond_result eq_cmp_result() const { return COND_TRUE; } const char *func_name() const { return "like"; } void fix_length_and_dec(); + bool fix_fields(THD *thd,struct st_table_list *tlist); + unsigned int size_of() { return sizeof(*this);} }; #ifdef USE_REGEX @@ -509,6 +549,7 @@ public: longlong val_int(); bool fix_fields(THD *thd,struct st_table_list *tlist); const char *func_name() const { return "regex"; } + unsigned int size_of() { return sizeof(*this);} }; #else @@ -530,10 +571,12 @@ class Item_cond :public Item_bool_func { protected: List<Item> list; + bool abort_on_null; public: - Item_cond() : Item_bool_func() { const_item_cache=0; } - Item_cond(Item *i1,Item *i2) :Item_bool_func() - { list.push_back(i1); list.push_back(i2); } + /* Item_cond() is only used to create top level items */ + Item_cond() : Item_bool_func(), abort_on_null(1) { const_item_cache=0; } + Item_cond(Item *i1,Item *i2) :Item_bool_func(), abort_on_null(0) + { list.push_back(i1); list.push_back(i2); } ~Item_cond() { list.delete_elements(); } bool add(Item *item) { return list.push_back(item); } bool fix_fields(THD *,struct st_table_list *); @@ -545,6 +588,8 @@ public: void print(String *str); void split_sum_func(List<Item> &fields); friend int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds); + unsigned int size_of() { return sizeof(*this);} + void top_level_item() { abort_on_null=1; } }; @@ -581,4 +626,15 @@ inline Item *and_conds(Item *a,Item *b) return cond; } +class Item_cond_xor :public Item_cond +{ +public: + Item_cond_xor() :Item_cond() {} + Item_cond_xor(Item *i1,Item *i2) :Item_cond(i1,i2) {} + enum Functype functype() const { return COND_XOR_FUNC; } + longlong val_int(); + const char *func_name() const { return "xor"; } +}; + + Item *and_expressions(Item *a, Item *b, Item **org_item); diff --git a/sql/item_create.cc b/sql/item_create.cc index ef9f5f2d38b..7e082bc174c 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -32,6 +32,16 @@ Item *create_func_acos(Item* a) return new Item_func_acos(a); } +Item *create_func_aes_encrypt(Item* a, Item* b) +{ + return new Item_func_aes_encrypt(a, b); +} + +Item *create_func_aes_decrypt(Item* a, Item* b) +{ + return new Item_func_aes_decrypt(a, b); +} + Item *create_func_ascii(Item* a) { return new Item_func_ascii(a); @@ -65,7 +75,9 @@ Item *create_func_ceiling(Item* a) Item *create_func_connection_id(void) { - return new Item_int("CONNECTION_ID()",(longlong) current_thd->thread_id,10); + THD *thd=current_thd; + thd->safe_to_cache_query=0; + return new Item_int(NullS,(longlong) thd->thread_id,10); } Item *create_func_conv(Item* a, Item *b, Item *c) @@ -129,6 +141,13 @@ Item *create_func_floor(Item* a) return new Item_func_floor(a); } +Item *create_func_found_rows(void) +{ + THD *thd=current_thd; + thd->safe_to_cache_query=0; + return new Item_int(NullS,(longlong) thd->found_rows(),21); +} + Item *create_func_from_days(Item* a) { return new Item_func_from_days(a); @@ -136,13 +155,13 @@ Item *create_func_from_days(Item* a) Item *create_func_get_lock(Item* a, Item *b) { + current_thd->safe_to_cache_query=0; return new Item_func_get_lock(a, b); } Item *create_func_hex(Item *a) { - return new Item_func_conv(a,new Item_int((int32) 10,2), - new Item_int((int32) 16,2)); + return new Item_func_hex(a); } Item *create_func_inet_ntoa(Item* a) @@ -191,14 +210,24 @@ Item *create_func_length(Item* a) return new Item_func_length(a); } +Item *create_func_bit_length(Item* a) +{ + return new Item_func_bit_length(a); +} + Item *create_func_char_length(Item* a) { return new Item_func_char_length(a); } -Item *create_func_log(Item* a) +Item *create_func_ln(Item* a) +{ + return new Item_func_ln(a); +} + +Item *create_func_log2(Item* a) { - return new Item_func_log(a); + return new Item_func_log2(a); } Item *create_func_log10(Item* a) @@ -254,7 +283,7 @@ Item *create_func_period_diff(Item* a, Item *b) Item *create_func_pi(void) { - return new Item_real("PI()",M_PI,6,8); + return new Item_real(NullS,M_PI,6,8); } Item *create_func_pow(Item* a, Item *b) @@ -262,11 +291,27 @@ Item *create_func_pow(Item* a, Item *b) return new Item_func_pow(a,b); } +Item *create_func_current_user() +{ + THD *thd=current_thd; + char buff[HOSTNAME_LENGTH+USERNAME_LENGTH+2]; + uint length; + + length= (uint) (strxmov(buff, thd->priv_user, "@", thd->host_or_ip, NullS) - + buff); + return new Item_string(NullS, thd->memdup(buff, length), length); +} + Item *create_func_quarter(Item* a) { return new Item_func_quarter(a); } +Item *create_func_password(Item* a) +{ + return new Item_func_password(a); +} + Item *create_func_radians(Item *a) { return new Item_func_units((char*) "radians",a,M_PI/180,0.0); @@ -274,6 +319,7 @@ Item *create_func_radians(Item *a) Item *create_func_release_lock(Item* a) { + current_thd->safe_to_cache_query=0; return new Item_func_release_lock(a); } @@ -312,6 +358,11 @@ Item *create_func_sin(Item* a) return new Item_func_sin(a); } +Item *create_func_sha(Item* a) +{ + return new Item_func_sha(a); +} + Item *create_func_space(Item *a) { return new Item_func_repeat(new Item_string(" ",1),a); @@ -359,7 +410,7 @@ Item *create_func_ucase(Item* a) Item *create_func_version(void) { - return new Item_string(NullS,server_version, (uint) strlen(server_version)); + return new Item_string(NullS,server_version, strlen(server_version)); } Item *create_func_weekday(Item* a) @@ -374,10 +425,33 @@ Item *create_func_year(Item* a) Item *create_load_file(Item* a) { + current_thd->safe_to_cache_query=0; return new Item_load_file(a); } -Item *create_wait_for_master_pos(Item* a, Item* b) +Item *create_func_cast(Item *a, Item_cast cast_type) +{ + Item *res; + LINT_INIT(res); + switch (cast_type) { + case ITEM_CAST_BINARY: res= new Item_func_binary(a); break; + case ITEM_CAST_CHAR: res= new Item_char_typecast(a); break; + case ITEM_CAST_SIGNED_INT: res= new Item_func_signed(a); break; + case ITEM_CAST_UNSIGNED_INT: res= new Item_func_unsigned(a); break; + case ITEM_CAST_DATE: res= new Item_date_typecast(a); break; + case ITEM_CAST_TIME: res= new Item_time_typecast(a); break; + case ITEM_CAST_DATETIME: res= new Item_datetime_typecast(a); break; + } + return res; +} + +Item *create_func_is_free_lock(Item* a) +{ + current_thd->safe_to_cache_query=0; + return new Item_func_is_free_lock(a); +} + +Item *create_func_quote(Item* a) { - return new Item_master_pos_wait(a, b); + return new Item_func_quote(a); } diff --git a/sql/item_create.h b/sql/item_create.h index cc7497b0183..5381ad946ae 100644 --- a/sql/item_create.h +++ b/sql/item_create.h @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -18,10 +18,13 @@ Item *create_func_abs(Item* a); Item *create_func_acos(Item* a); +Item *create_func_aes_encrypt(Item* a, Item* b); +Item *create_func_aes_decrypt(Item* a, Item* b); Item *create_func_ascii(Item* a); Item *create_func_asin(Item* a); Item *create_func_bin(Item* a); Item *create_func_bit_count(Item* a); +Item *create_func_bit_length(Item* a); Item *create_func_ceiling(Item* a); Item *create_func_char_length(Item* a); Item *create_func_connection_id(void); @@ -37,6 +40,7 @@ Item *create_func_degrees(Item *); Item *create_func_exp(Item* a); Item *create_func_find_in_set(Item* a, Item *b); Item *create_func_floor(Item* a); +Item *create_func_found_rows(void); Item *create_func_from_days(Item* a); Item *create_func_get_lock(Item* a, Item *b); Item *create_func_hex(Item *a); @@ -48,8 +52,9 @@ Item *create_func_instr(Item* a, Item *b); Item *create_func_isnull(Item* a); Item *create_func_lcase(Item* a); Item *create_func_length(Item* a); +Item *create_func_ln(Item* a); Item *create_func_locate(Item* a, Item *b); -Item *create_func_log(Item* a); +Item *create_func_log2(Item* a); Item *create_func_log10(Item* a); Item *create_func_lpad(Item* a, Item *b, Item *c); Item *create_func_ltrim(Item* a); @@ -63,7 +68,9 @@ Item *create_func_period_add(Item* a, Item *b); Item *create_func_period_diff(Item* a, Item *b); Item *create_func_pi(void); Item *create_func_pow(Item* a, Item *b); +Item *create_func_current_user(void); Item *create_func_quarter(Item* a); +Item *create_func_password(Item* a); Item *create_func_radians(Item *a); Item *create_func_release_lock(Item* a); Item *create_func_repeat(Item* a, Item *b); @@ -73,6 +80,7 @@ Item *create_func_rtrim(Item* a); Item *create_func_sec_to_time(Item* a); Item *create_func_sign(Item* a); Item *create_func_sin(Item* a); +Item *create_func_sha(Item* a); Item *create_func_soundex(Item* a); Item *create_func_space(Item *); Item *create_func_sqrt(Item* a); @@ -85,4 +93,5 @@ Item *create_func_ucase(Item* a); Item *create_func_version(void); Item *create_func_weekday(Item* a); Item *create_load_file(Item* a); -Item *create_wait_for_master_pos(Item* a, Item* b); +Item *create_func_is_free_lock(Item* a); +Item *create_func_quote(Item* a); diff --git a/sql/item_func.cc b/sql/item_func.cc index 723585be0b1..ef629098d2a 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -43,7 +43,7 @@ Item_func::Item_func(List<Item> &list) if ((args=(Item**) sql_alloc(sizeof(Item*)*arg_count))) { uint i=0; - List_iterator<Item> li(list); + List_iterator_fast<Item> li(list); Item *item; while ((item=li++)) @@ -166,6 +166,36 @@ bool Item_func::eq(const Item *item, bool binary_cmp) const } +Field *Item_func::tmp_table_field(TABLE *t_arg) +{ + Field *res; + LINT_INIT(res); + + if (!t_arg) + return result_field; + switch (result_type()) { + case INT_RESULT: + if (max_length > 11) + res= new Field_longlong(max_length, maybe_null, name, t_arg, + unsigned_flag); + else + res= new Field_long(max_length, maybe_null, name, t_arg, + unsigned_flag); + break; + case REAL_RESULT: + res= new Field_double(max_length, maybe_null, name, t_arg, decimals); + break; + case STRING_RESULT: + if (max_length > 255) + res= new Field_blob(max_length, maybe_null, name, t_arg, binary); + else + res= new Field_string(max_length, maybe_null, name, t_arg, binary); + break; + } + return res; +} + + String *Item_real_func::val_str(String *str) { double nr=val(); @@ -184,8 +214,10 @@ String *Item_num_func::val_str(String *str) longlong nr=val_int(); if (null_value) return 0; /* purecov: inspected */ - else + else if (!unsigned_flag) str->set(nr); + else + str->set((ulonglong) nr); } else { @@ -207,24 +239,31 @@ void Item_func::fix_num_length_and_dec() max_length=float_length(decimals); } - String *Item_int_func::val_str(String *str) { longlong nr=val_int(); if (null_value) return 0; - else + else if (!unsigned_flag) str->set(nr); + else + str->set((ulonglong) nr); return str; } -/* Change from REAL_RESULT (default) to INT_RESULT if both arguments are integers */ +/* + Change from REAL_RESULT (default) to INT_RESULT if both arguments are + integers +*/ void Item_num_op::find_num_type(void) { if (args[0]->result_type() == INT_RESULT && args[1]->result_type() == INT_RESULT) + { hybrid_type=INT_RESULT; + unsigned_flag=args[0]->unsigned_flag | args[1]->unsigned_flag; + } } String *Item_num_op::val_str(String *str) @@ -234,8 +273,10 @@ String *Item_num_op::val_str(String *str) longlong nr=val_int(); if (null_value) return 0; /* purecov: inspected */ - else + else if (!unsigned_flag) str->set(nr); + else + str->set((ulonglong) nr); } else { @@ -269,6 +310,21 @@ longlong Item_func_plus::val_int() return (longlong) Item_func_plus::val(); } + +/* + The following function is here to allow the user to force + subtraction of UNSIGNED BIGINT to return negative values. +*/ + +void Item_func_minus::fix_length_and_dec() +{ + Item_num_op::fix_length_and_dec(); + if (unsigned_flag && + (current_thd->sql_mode & MODE_NO_UNSIGNED_SUBTRACTION)) + unsigned_flag=0; +} + + double Item_func_minus::val() { double value=args[0]->val() - args[1]->val(); @@ -411,14 +467,43 @@ void Item_func_abs::fix_length_and_dec() hybrid_type= args[0]->result_type() == INT_RESULT ? INT_RESULT : REAL_RESULT; } +/* Gateway to natural LOG function */ +double Item_func_ln::val() +{ + double value=args[0]->val(); + if ((null_value=(args[0]->null_value || value <= 0.0))) + return 0.0; + return log(value); +} + +/* + Extended but so slower LOG function + We have to check if all values are > zero and first one is not one + as these are the cases then result is not a number. +*/ double Item_func_log::val() { double value=args[0]->val(); if ((null_value=(args[0]->null_value || value <= 0.0))) - return 0.0; /* purecov: inspected */ + return 0.0; + if (arg_count == 2) + { + double value2= args[1]->val(); + if ((null_value=(args[1]->null_value || value2 <= 0.0 || value == 1.0))) + return 0.0; + return log(value2) / log(value); + } return log(value); } +double Item_func_log2::val() +{ + double value=args[0]->val(); + if ((null_value=(args[0]->null_value || value <= 0.0))) + return 0.0; + return log(value) / log(2.0); +} + double Item_func_log10::val() { double value=args[0]->val(); @@ -611,18 +696,39 @@ double Item_func_round::val() } -double Item_func_rand::val() +void Item_func_rand::fix_length_and_dec() { + decimals=NOT_FIXED_DEC; + max_length=float_length(decimals); if (arg_count) { // Only use argument once in query - ulong tmp=((ulong) args[0]->val_int())+55555555L; - randominit(¤t_thd->rand,tmp,tmp/2); -#ifdef DELETE_ITEMS - delete args[0]; -#endif - arg_count=0; + uint32 tmp= (uint32) (args[0]->val_int()); + if ((rand= (struct rand_struct*) sql_alloc(sizeof(*rand)))) + randominit(rand,(uint32) (tmp*0x10001L+55555555L), + (uint32) (tmp*0x10000001L)); + } + else + { + THD *thd= current_thd; + /* + No need to send a Rand log event if seed was given eg: RAND(seed), + as it will be replicated in the query as such. + + Save the seed only the first time RAND() is used in the query + Once events are forwarded rather than recreated, + the following can be skipped if inside the slave thread + */ + thd->rand_used=1; + thd->rand_saved_seed1=thd->rand.seed1; + thd->rand_saved_seed2=thd->rand.seed2; + rand= &thd->rand; } - return rnd(¤t_thd->rand); +} + + +double Item_func_rand::val() +{ + return rnd(rand); } longlong Item_func_sign::val_int() @@ -672,8 +778,10 @@ String *Item_func_min_max::val_str(String *str) longlong nr=val_int(); if (null_value) return 0; - else + else if (!unsigned_flag) str->set(nr); + else + str->set((ulonglong) nr); return str; } case REAL_RESULT: @@ -789,9 +897,7 @@ longlong Item_func_locate::val_int() { String *a=args[0]->val_str(&value1); String *b=args[1]->val_str(&value2); -#ifdef USE_MB bool binary_str = args[0]->binary || args[1]->binary; -#endif if (!a || !b) { null_value=1; @@ -845,7 +951,8 @@ longlong Item_func_locate::val_int() return 0; } #endif /* USE_MB */ - return (longlong) (a->strstr(*b,start)+1) ; + return (longlong) (binary ? a->strstr(*b,start) : + (a->strstr_case(*b,start)))+1; } @@ -1066,7 +1173,8 @@ udf_handler::~udf_handler() } free_udf(u_d); } - delete [] buffers; + if (buffers) // Because of bug in ecc + delete [] buffers; } @@ -1322,8 +1430,10 @@ String *Item_func_udf_int::val_str(String *str) longlong nr=val_int(); if (null_value) return 0; - else + else if (!unsigned_flag) str->set(nr); + else + str->set((ulonglong) nr); return str; } @@ -1410,6 +1520,7 @@ void item_user_lock_init(void) void item_user_lock_free(void) { hash_free(&hash_user_locks); + pthread_mutex_destroy(&LOCK_user_locks); } void item_user_lock_release(ULL *ull) @@ -1417,20 +1528,15 @@ void item_user_lock_release(ULL *ull) ull->locked=0; if (mysql_bin_log.is_open()) { - THD *thd = current_thd; - int save_errno; char buf[256]; String tmp(buf,sizeof(buf)); tmp.length(0); tmp.append("DO RELEASE_LOCK(\""); tmp.append(ull->key,ull->key_length); tmp.append("\")"); - save_errno=thd->net.last_errno; - thd->net.last_errno=0; - thd->query_length=tmp.length(); - Query_log_event qev(thd,tmp.ptr()); + Query_log_event qev(current_thd, tmp.ptr(), tmp.length(),1); + qev.error_code=0; // this query is always safe to run on slave mysql_bin_log.write(&qev); - thd->net.last_errno=save_errno; } if (--ull->count) pthread_cond_signal(&ull->cond); @@ -1448,22 +1554,95 @@ longlong Item_master_pos_wait::val_int() THD* thd = current_thd; String *log_name = args[0]->val_str(&value); int event_count; - + null_value=0; if (thd->slave_thread || !log_name || !log_name->length()) { null_value = 1; return 0; } - ulong pos = (ulong)args[1]->val_int(); - if ((event_count = glob_mi.wait_for_pos(thd, log_name, pos)) == -1) + longlong pos = args[1]->val_int(); + longlong timeout = (arg_count==3) ? args[2]->val_int() : 0 ; + LOCK_ACTIVE_MI; + if ((event_count = active_mi->rli.wait_for_pos(thd, log_name, pos, timeout)) == -2) { null_value = 1; event_count=0; } + UNLOCK_ACTIVE_MI; return event_count; } +#ifdef EXTRA_DEBUG +void debug_sync_point(const char* lock_name, uint lock_timeout) +{ + THD* thd=current_thd; + ULL* ull; + struct timespec abstime; + int lock_name_len,error=0; + lock_name_len=strlen(lock_name); + pthread_mutex_lock(&LOCK_user_locks); + + if (thd->ull) + { + item_user_lock_release(thd->ull); + thd->ull=0; + } + + /* + If the lock has not been aquired by some client, we do not want to + create an entry for it, since we immediately release the lock. In + this case, we will not be waiting, but rather, just waste CPU and + memory on the whole deal + */ + if (!(ull= ((ULL*) hash_search(&hash_user_locks,lock_name, + lock_name_len)))) + { + pthread_mutex_unlock(&LOCK_user_locks); + return; + } + ull->count++; + + /* + Structure is now initialized. Try to get the lock. + Set up control struct to allow others to abort locks + */ + thd->proc_info="User lock"; + thd->mysys_var->current_mutex= &LOCK_user_locks; + thd->mysys_var->current_cond= &ull->cond; + + set_timespec(abstime,lock_timeout); + while (!thd->killed && + (error=pthread_cond_timedwait(&ull->cond,&LOCK_user_locks,&abstime)) + != ETIME && error != ETIMEDOUT && ull->locked) ; + if (ull->locked) + { + if (!--ull->count) + delete ull; // Should never happen + } + else + { + ull->locked=1; + ull->thread=thd->real_id; + thd->ull=ull; + } + pthread_mutex_unlock(&LOCK_user_locks); + pthread_mutex_lock(&thd->mysys_var->mutex); + thd->proc_info=0; + thd->mysys_var->current_mutex= 0; + thd->mysys_var->current_cond= 0; + pthread_mutex_unlock(&thd->mysys_var->mutex); + pthread_mutex_lock(&LOCK_user_locks); + if (thd->ull) + { + item_user_lock_release(thd->ull); + thd->ull=0; + } + pthread_mutex_unlock(&LOCK_user_locks); +} + +#endif + /* Get a user level lock. If the thread has an old lock this is first released. Returns 1: Got lock @@ -1514,20 +1693,15 @@ longlong Item_func_get_lock::val_int() } ull->count++; - /* structure is now initialized. Try to get the lock */ - /* Set up control struct to allow others to abort locks */ + /* + Structure is now initialized. Try to get the lock. + Set up control struct to allow others to abort locks. + */ thd->proc_info="User lock"; thd->mysys_var->current_mutex= &LOCK_user_locks; thd->mysys_var->current_cond= &ull->cond; -#ifdef HAVE_TIMESPEC_TS_SEC - abstime.ts_sec=time((time_t*) 0)+(time_t) timeout; - abstime.ts_nsec=0; -#else - abstime.tv_sec=time((time_t*) 0)+(time_t) timeout; - abstime.tv_nsec=0; -#endif - + set_timespec(abstime,timeout); while (!thd->killed && (error=pthread_cond_timedwait(&ull->cond,&LOCK_user_locks,&abstime)) != ETIME && error != ETIMEDOUT && error != EINVAL && ull->locked) ; @@ -1563,10 +1737,11 @@ longlong Item_func_get_lock::val_int() /* -** Release a user level lock. -** Returns 1 if lock released -** 0 if lock wasn't held -** NULL if no such lock + Release a user level lock. + Return: + 1 if lock released + 0 if lock wasn't held + (SQL) NULL if no such lock */ longlong Item_func_release_lock::val_int() @@ -1635,6 +1810,7 @@ longlong Item_func_benchmark::val_int() return 0; } + #define extra_size sizeof(double) static user_var_entry *get_variable(HASH *hash, LEX_STRING &name, @@ -1672,7 +1848,7 @@ static user_var_entry *get_variable(HASH *hash, LEX_STRING &name, bool Item_func_set_user_var::fix_fields(THD *thd,TABLE_LIST *tables) { if (!thd) - thd=current_thd; + thd=current_thd; // Should never happen if (Item_func::fix_fields(thd,tables) || !(entry= get_variable(&thd->user_vars, name, 1))) return 1; @@ -1800,13 +1976,13 @@ void Item_func_set_user_var::print(String *str) user_var_entry *Item_func_get_user_var::get_entry() { - if (!entry || ! entry->value) + if (!var_entry || ! var_entry->value) { null_value=1; return 0; } null_value=0; - return entry; + return var_entry; } @@ -1875,11 +2051,14 @@ void Item_func_get_user_var::fix_length_and_dec() maybe_null=1; decimals=NOT_FIXED_DEC; max_length=MAX_BLOB_WIDTH; - if ((entry= get_variable(&thd->user_vars, name, 0))) - const_var_flag= thd->query_id != entry->update_query_id; + var_entry= get_variable(&thd->user_vars, name, 0); } +bool Item_func_get_user_var::const_item() const +{ return var_entry && current_thd->query_id != var_entry->update_query_id; } + + enum Item_result Item_func_get_user_var::result_type() const { user_var_entry *entry; @@ -1898,6 +2077,7 @@ void Item_func_get_user_var::print(String *str) str->append(')'); } + bool Item_func_get_user_var::eq(const Item *item, bool binary_cmp) const { /* Assume we don't have rtti */ @@ -1957,48 +2137,15 @@ err: return 0; } -double Item_func_match::val() -{ - if (ft_handler==NULL) - return -1.0; - - if (join_key) - { - if (table->file->ft_handler) - return ft_get_relevance(ft_handler); - - join_key=0; // Magic here ! See ha_myisam::ft_read() - } - - /* we'll have to find ft_relevance manually in ft_handler array */ - - int a,b,c; - FT_DOC *docs=ft_handler->doc; - my_off_t docid=table->file->row_position(); - - if ((null_value=(docid==HA_OFFSET_ERROR))) - return 0.0; - - // Assuming docs[] is sorted by dpos... - - for (a=0, b=ft_handler->ndocs, c=(a+b)/2; b-a>1; c=(a+b)/2) - { - if (docs[c].dpos > docid) - b=c; - else - a=c; - } - if (docs[a].dpos == docid) - return docs[a].weight; - else - return 0.0; - -} void Item_func_match::init_search(bool no_order) { + DBUG_ENTER("Item_func_match::init_search"); if (ft_handler) - return; + DBUG_VOID_RETURN; + + if (key == NO_SUCH_KEY) + concat= new Item_func_concat_ws(new Item_string(" ",1), fields); if (master) { @@ -2006,31 +2153,32 @@ void Item_func_match::init_search(bool no_order) master->init_search(no_order); ft_handler=master->ft_handler; join_key=master->join_key; - return; + DBUG_VOID_RETURN; } String *ft_tmp=0; char tmp1[FT_QUERY_MAXLEN]; String tmp2(tmp1,sizeof(tmp1)); - // MATCH ... AGAINST (NULL) is meaningless, but possible + // MATCH ... AGAINST (NULL) is meaningless, but possible if (!(ft_tmp=key_item()->val_str(&tmp2))) { - ft_tmp=&tmp2; + ft_tmp= &tmp2; tmp2.set("",0); } - ft_handler=(FT_DOCLIST *) - table->file->ft_init_ext(key, (byte*) ft_tmp->ptr(), ft_tmp->length(), - join_key && !no_order); + ft_handler=table->file->ft_init_ext(mode, key, + (byte*) ft_tmp->ptr(), + ft_tmp->length(), + join_key && !no_order); if (join_key) - { table->file->ft_handler=ft_handler; - return; - } + + DBUG_VOID_RETURN; } + bool Item_func_match::fix_fields(THD *thd,struct st_table_list *tlist) { List_iterator<Item> li(fields); @@ -2039,12 +2187,12 @@ bool Item_func_match::fix_fields(THD *thd,struct st_table_list *tlist) maybe_null=1; join_key=0; - /* Serg: - I'd rather say now that const_item is assumed in quite a bit of - places, so it would be difficult to remove; If it would ever to be - removed, this should include modifications to find_best and auto_close - as complement to auto_init code above. - */ + /* + const_item is assumed in quite a bit of places, so it would be difficult + to remove; If it would ever to be removed, this should include + modifications to find_best and auto_close as complement to auto_init code + above. + */ if (Item_func::fix_fields(thd,tlist) || !const_item()) { my_error(ER_WRONG_ARGUMENTS,MYF(0),"AGAINST"); @@ -2058,106 +2206,111 @@ bool Item_func_match::fix_fields(THD *thd,struct st_table_list *tlist) if (item->type() == Item::REF_ITEM) li.replace(item= *((Item_ref *)item)->ref); if (item->type() != Item::FIELD_ITEM || !item->used_tables()) - { - my_error(ER_WRONG_ARGUMENTS,MYF(0),"MATCH"); - return 1; - } + key=NO_SUCH_KEY; used_tables_cache|=item->used_tables(); } /* check that all columns come from the same table */ if (count_bits(used_tables_cache) != 1) + key=NO_SUCH_KEY; + const_item_cache=0; + table=((Item_field *)fields.head())->field->table; + table->fulltext_searched=1; + record=table->record[0]; + if (key == NO_SUCH_KEY && mode != FT_BOOL) { my_error(ER_WRONG_ARGUMENTS,MYF(0),"MATCH"); return 1; } - const_item_cache=0; - table=((Item_field *)fields.head())->field->table; - table->fulltext_searched=1; + return 0; } bool Item_func_match::fix_index() { - List_iterator<Item> li(fields); + List_iterator_fast<Item> li(fields); Item_field *item; - uint ft_to_key[MAX_KEY], ft_cnt[MAX_KEY], fts=0, key; + uint ft_to_key[MAX_KEY], ft_cnt[MAX_KEY], fts=0, keynr; + uint max_cnt=0, mkeys=0; + + if (key == NO_SUCH_KEY) + return 0; - for (key=0 ; key<table->keys ; key++) + for (keynr=0 ; keynr < table->keys ; keynr++) { - if ((table->key_info[key].flags & HA_FULLTEXT) && - (table->keys_in_use_for_query & (((key_map)1) << key))) + if ((table->key_info[keynr].flags & HA_FULLTEXT) && + (table->keys_in_use_for_query & (((key_map)1) << keynr))) { - ft_to_key[fts]=key; + ft_to_key[fts]=keynr; ft_cnt[fts]=0; fts++; } } if (!fts) - { - my_printf_error(ER_FT_MATCHING_KEY_NOT_FOUND, - ER(ER_FT_MATCHING_KEY_NOT_FOUND),MYF(0)); - return 1; - } + goto err; while ((item=(Item_field*)(li++))) { - for (key=0 ; key<fts ; key++) + for (keynr=0 ; keynr < fts ; keynr++) { - KEY *ft_key=&table->key_info[ft_to_key[key]]; + KEY *ft_key=&table->key_info[ft_to_key[keynr]]; uint key_parts=ft_key->key_parts; for (uint part=0 ; part < key_parts ; part++) { if (item->field->eq(ft_key->key_part[part].field)) - ft_cnt[key]++; + ft_cnt[keynr]++; } } } - uint max_cnt=0, mkeys=0; - for (key=0 ; key<fts ; key++) + for (keynr=0 ; keynr < fts ; keynr++) { - if (ft_cnt[key] > max_cnt) + if (ft_cnt[keynr] > max_cnt) { mkeys=0; - max_cnt=ft_cnt[mkeys]=ft_cnt[key]; - ft_to_key[mkeys]=ft_to_key[key]; + max_cnt=ft_cnt[mkeys]=ft_cnt[keynr]; + ft_to_key[mkeys]=ft_to_key[keynr]; continue; } - if (max_cnt && ft_cnt[key] == max_cnt) + if (max_cnt && ft_cnt[keynr] == max_cnt) { mkeys++; - ft_cnt[mkeys]=ft_cnt[key]; - ft_to_key[mkeys]=ft_to_key[key]; + ft_cnt[mkeys]=ft_cnt[keynr]; + ft_to_key[mkeys]=ft_to_key[keynr]; continue; } } - for (key=0 ; key<=mkeys ; key++) + for (keynr=0 ; keynr <= mkeys ; keynr++) { // for now, partial keys won't work. SerG if (max_cnt < fields.elements || - max_cnt < table->key_info[ft_to_key[key]].key_parts) + max_cnt < table->key_info[ft_to_key[keynr]].key_parts) continue; - this->key=ft_to_key[key]; + key=ft_to_key[keynr]; return 0; } +err: + if (mode == FT_BOOL) + { + key=NO_SUCH_KEY; + return 0; + } my_printf_error(ER_FT_MATCHING_KEY_NOT_FOUND, - ER(ER_FT_MATCHING_KEY_NOT_FOUND),MYF(0)); + ER(ER_FT_MATCHING_KEY_NOT_FOUND),MYF(0)); return 1; } + bool Item_func_match::eq(const Item *item, bool binary_cmp) const { - if (item->type() != FUNC_ITEM) - return 0; - - if (func_name() != ((Item_func*)item)->func_name()) + if (item->type() != FUNC_ITEM || + func_name() != ((Item_func*)item)->func_name()) return 0; Item_func_match *ifm=(Item_func_match*) item; @@ -2170,19 +2323,109 @@ bool Item_func_match::eq(const Item *item, bool binary_cmp) const } +double Item_func_match::val() +{ + DBUG_ENTER("Item_func_match::val"); + if (ft_handler == NULL) + DBUG_RETURN(-1.0); + + if (join_key) + { + if (table->file->ft_handler) + DBUG_RETURN(ft_handler->please->get_relevance(ft_handler)); + join_key=0; + } + + if (key == NO_SUCH_KEY) + { + String *a= concat->val_str(&value); + if ((null_value= (a == 0))) + DBUG_RETURN(0); + DBUG_RETURN(ft_handler->please->find_relevance(ft_handler, + (byte *)a->ptr(), a->length())); + } + else + DBUG_RETURN(ft_handler->please->find_relevance(ft_handler, record, 0)); +} + +longlong Item_func_bit_xor::val_int() +{ + ulonglong arg1= (ulonglong) args[0]->val_int(); + ulonglong arg2= (ulonglong) args[1]->val_int(); + if ((null_value= (args[0]->null_value || args[1]->null_value))) + return 0; + return (longlong) (arg1 ^ arg2); +} + + /*************************************************************************** System variables - This has to be recoded after we get more than 3 system variables ****************************************************************************/ -Item *get_system_var(LEX_STRING name) +Item *get_system_var(enum_var_type var_type, LEX_STRING name) { - if (!my_strcasecmp(name.str,"IDENTITY")) - return new Item_int((char*) "@@IDENTITY", - current_thd->insert_id(),21); if (!my_strcasecmp(name.str,"VERSION")) return new Item_string("@@VERSION",server_version, (uint) strlen(server_version)); - net_printf(¤t_thd->net, ER_UNKNOWN_SYSTEM_VARIABLE, name.str); + + THD *thd=current_thd; + Item *item; + sys_var *var; + char buff[MAX_SYS_VAR_LENGTH+3+8], *pos; + + if (!(var= find_sys_var(name.str))) + { + net_printf(&thd->net, ER_UNKNOWN_SYSTEM_VARIABLE, name.str); + return 0; + } + if (!(item=var->item(thd, var_type))) + return 0; // Impossible + thd->safe_to_cache_query=0; + buff[0]='@'; + buff[1]='@'; + pos=buff+2; + if (var_type == OPT_SESSION) + pos=strmov(pos,"session."); + else if (var_type == OPT_GLOBAL) + pos=strmov(pos,"global."); + memcpy(pos, var->name, var->name_length+1); + // set_name() will allocate the name + item->set_name(buff,(uint) (pos-buff)+var->name_length); + return item; +} + + +/* + Check a user level lock. + + SYNOPSIS: + val_int() + + RETURN VALUES + 1 Available + 0 Already taken + NULL Error +*/ + +longlong Item_func_is_free_lock::val_int() +{ + String *res=args[0]->val_str(&value); + THD *thd=current_thd; + ULL *ull; + int error=0; + + null_value=0; + if (!res || !res->length()) + { + null_value=1; + return 0; + } + + pthread_mutex_lock(&LOCK_user_locks); + ull= (ULL*) hash_search(&hash_user_locks,(byte*) res->ptr(), + res->length()); + pthread_mutex_unlock(&LOCK_user_locks); + if (!ull || !ull->locked) + return 1; return 0; } diff --git a/sql/item_func.h b/sql/item_func.h index 853995a7c92..68e5335dc7e 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -39,7 +39,7 @@ public: enum Functype { UNKNOWN_FUNC,EQ_FUNC,EQUAL_FUNC,NE_FUNC,LT_FUNC,LE_FUNC, GE_FUNC,GT_FUNC,FT_FUNC, LIKE_FUNC,NOTLIKE_FUNC,ISNULL_FUNC,ISNOTNULL_FUNC, - COND_AND_FUNC,COND_OR_FUNC,BETWEEN,IN_FUNC,INTERVAL_FUNC}; + COND_AND_FUNC,COND_OR_FUNC,COND_XOR_FUNC,BETWEEN,IN_FUNC,INTERVAL_FUNC}; enum optimize_type { OPTIMIZE_NONE,OPTIMIZE_KEY,OPTIMIZE_OP, OPTIMIZE_NULL }; enum Type type() const { return FUNC_ITEM; } virtual enum Functype functype() const { return UNKNOWN_FUNC; } @@ -119,7 +119,10 @@ public: { return (null_value=args[0]->get_time(ltime)); } + bool is_null() { (void) val_int(); return null_value; } friend class udf_handler; + unsigned int size_of() { return sizeof(*this);} + Field *tmp_table_field(TABLE *t_arg); }; @@ -134,8 +137,10 @@ public: longlong val_int() { return (longlong) val(); } enum Item_result result_type () const { return REAL_RESULT; } void fix_length_and_dec() { decimals=NOT_FIXED_DEC; max_length=float_length(decimals); } + unsigned int size_of() { return sizeof(*this);} }; + class Item_num_func :public Item_func { protected: @@ -147,6 +152,8 @@ public: longlong val_int() { return (longlong) val(); } enum Item_result result_type () const { return hybrid_type; } void fix_length_and_dec() { fix_num_length_and_dec(); } + bool is_null() { (void) val(); return null_value; } + unsigned int size_of() { return sizeof(*this);} }; @@ -161,23 +168,48 @@ class Item_num_op :public Item_func enum Item_result result_type () const { return hybrid_type; } void fix_length_and_dec() { fix_num_length_and_dec(); find_num_type(); } void find_num_type(void); + bool is_null() { (void) val(); return null_value; } + unsigned int size_of() { return sizeof(*this);} }; class Item_int_func :public Item_func { public: - Item_int_func() :Item_func() {} - Item_int_func(Item *a) :Item_func(a) {} - Item_int_func(Item *a,Item *b) :Item_func(a,b) {} - Item_int_func(Item *a,Item *b,Item *c) :Item_func(a,b,c) {} - Item_int_func(List<Item> &list) :Item_func(list) {} + Item_int_func() :Item_func() { max_length=21; } + Item_int_func(Item *a) :Item_func(a) { max_length=21; } + Item_int_func(Item *a,Item *b) :Item_func(a,b) { max_length=21; } + Item_int_func(Item *a,Item *b,Item *c) :Item_func(a,b,c) { max_length=21; } + Item_int_func(List<Item> &list) :Item_func(list) { max_length=21; } double val() { return (double) val_int(); } String *val_str(String*str); enum Item_result result_type () const { return INT_RESULT; } - void fix_length_and_dec() { decimals=0; max_length=21; } + void fix_length_and_dec() {} +}; + + +class Item_func_signed :public Item_int_func +{ +public: + Item_func_signed(Item *a) :Item_int_func(a) {} + double val() { return args[0]->val(); } + longlong val_int() { return args[0]->val_int(); } + void fix_length_and_dec() + { max_length=args[0]->max_length; unsigned_flag=0; } +}; + + +class Item_func_unsigned :public Item_int_func +{ +public: + Item_func_unsigned(Item *a) :Item_int_func(a) {} + double val() { return args[0]->val(); } + longlong val_int() { return args[0]->val_int(); } + void fix_length_and_dec() + { max_length=args[0]->max_length; unsigned_flag=1; } }; + class Item_func_plus :public Item_num_op { public: @@ -194,8 +226,10 @@ public: const char *func_name() const { return "-"; } double val(); longlong val_int(); + void fix_length_and_dec(); }; + class Item_func_mul :public Item_num_op { public: @@ -284,15 +318,35 @@ public: const char *func_name() const { return "exp"; } }; + +class Item_func_ln :public Item_dec_func +{ +public: + Item_func_ln(Item *a) :Item_dec_func(a) {} + double val(); + const char *func_name() const { return "ln"; } +}; + + class Item_func_log :public Item_dec_func { public: Item_func_log(Item *a) :Item_dec_func(a) {} + Item_func_log(Item *a,Item *b) :Item_dec_func(a,b) {} double val(); const char *func_name() const { return "log"; } }; +class Item_func_log2 :public Item_dec_func +{ +public: + Item_func_log2(Item *a) :Item_dec_func(a) {} + double val(); + const char *func_name() const { return "log2"; } +}; + + class Item_func_log10 :public Item_dec_func { public: @@ -410,14 +464,15 @@ public: class Item_func_rand :public Item_real_func { + struct rand_struct *rand; public: Item_func_rand(Item *a) :Item_real_func(a) {} Item_func_rand() :Item_real_func() {} double val(); const char *func_name() const { return "rand"; } - void fix_length_and_dec() { decimals=NOT_FIXED_DEC; max_length=float_length(decimals); } bool const_item() const { return 0; } table_map used_tables() const { return RAND_TABLE_BIT; } + void fix_length_and_dec(); }; @@ -440,6 +495,7 @@ class Item_func_units :public Item_real_func double val(); const char *func_name() const { return name; } void fix_length_and_dec() { decimals=NOT_FIXED_DEC; max_length=float_length(decimals); } + unsigned int size_of() { return sizeof(*this);} }; @@ -456,6 +512,7 @@ public: String *val_str(String *); void fix_length_and_dec(); enum Item_result result_type () const { return cmp_type; } + unsigned int size_of() { return sizeof(*this);} }; class Item_func_min :public Item_func_min_max @@ -481,6 +538,15 @@ public: longlong val_int(); const char *func_name() const { return "length"; } void fix_length_and_dec() { max_length=10; } + unsigned int size_of() { return sizeof(*this);} +}; + +class Item_func_bit_length :public Item_func_length +{ +public: + Item_func_bit_length(Item *a) :Item_func_length(a) {} + longlong val_int() { return Item_func_length::val_int()*8; } + const char *func_name() const { return "bit_length"; } }; class Item_func_char_length :public Item_int_func @@ -491,6 +557,7 @@ public: longlong val_int(); const char *func_name() const { return "char_length"; } void fix_length_and_dec() { max_length=10; } + unsigned int size_of() { return sizeof(*this);} }; class Item_func_locate :public Item_int_func @@ -502,6 +569,7 @@ public: const char *func_name() const { return "locate"; } longlong val_int(); void fix_length_and_dec() { maybe_null=0; max_length=11; } + unsigned int size_of() { return sizeof(*this);} }; @@ -532,6 +600,7 @@ public: const_item_cache&= item->const_item(); with_sum_func= with_sum_func || item->with_sum_func; } + unsigned int size_of() { return sizeof(*this);} }; @@ -543,6 +612,7 @@ public: longlong val_int(); const char *func_name() const { return "ascii"; } void fix_length_and_dec() { max_length=3; } + unsigned int size_of() { return sizeof(*this);} }; class Item_func_ord :public Item_int_func @@ -552,7 +622,7 @@ public: Item_func_ord(Item *a) :Item_int_func(a) {} longlong val_int(); const char *func_name() const { return "ord"; } - void fix_length_and_dec() { max_length=21; } + unsigned int size_of() { return sizeof(*this);} }; class Item_func_find_in_set :public Item_int_func @@ -565,6 +635,7 @@ public: longlong val_int(); const char *func_name() const { return "find_in_set"; } void fix_length_and_dec(); + unsigned int size_of() { return sizeof(*this);} }; @@ -574,7 +645,7 @@ public: Item_func_bit_or(Item *a,Item *b) :Item_int_func(a,b) {} longlong val_int(); const char *func_name() const { return "|"; } - void fix_length_and_dec() { decimals=0; max_length=21; } + void fix_length_and_dec() { unsigned_flag=1; } }; class Item_func_bit_and :public Item_int_func @@ -583,7 +654,7 @@ public: Item_func_bit_and(Item *a,Item *b) :Item_int_func(a,b) {} longlong val_int(); const char *func_name() const { return "&"; } - void fix_length_and_dec() { decimals=0; max_length=21; } + void fix_length_and_dec() { unsigned_flag=1; } }; class Item_func_bit_count :public Item_int_func @@ -592,7 +663,7 @@ public: Item_func_bit_count(Item *a) :Item_int_func(a) {} longlong val_int(); const char *func_name() const { return "bit_count"; } - void fix_length_and_dec() { decimals=0; max_length=2; } + void fix_length_and_dec() { max_length=2; } }; class Item_func_shift_left :public Item_int_func @@ -601,7 +672,7 @@ public: Item_func_shift_left(Item *a,Item *b) :Item_int_func(a,b) {} longlong val_int(); const char *func_name() const { return "<<"; } - void fix_length_and_dec() { decimals=0; max_length=21; } + void fix_length_and_dec() { unsigned_flag=1; } }; class Item_func_shift_right :public Item_int_func @@ -610,7 +681,6 @@ public: Item_func_shift_right(Item *a,Item *b) :Item_int_func(a,b) {} longlong val_int(); const char *func_name() const { return ">>"; } - void fix_length_and_dec() { decimals=0; max_length=21; } }; class Item_func_bit_neg :public Item_int_func @@ -619,7 +689,7 @@ public: Item_func_bit_neg(Item *a) :Item_int_func(a) {} longlong val_int(); const char *func_name() const { return "~"; } - void fix_length_and_dec() { decimals=0; max_length=21; } + void fix_length_and_dec() { unsigned_flag=1; } }; class Item_func_set_last_insert_id :public Item_int_func @@ -628,7 +698,7 @@ public: Item_func_set_last_insert_id(Item *a) :Item_int_func(a) {} longlong val_int(); const char *func_name() const { return "last_insert_id"; } - void fix_length_and_dec() { decimals=0; max_length=args[0]->max_length; } + void fix_length_and_dec() { max_length=args[0]->max_length; } }; class Item_func_benchmark :public Item_int_func @@ -640,7 +710,8 @@ class Item_func_benchmark :public Item_int_func {} longlong val_int(); const char *func_name() const { return "benchmark"; } - void fix_length_and_dec() { decimals=0; max_length=1; maybe_null=0; } + void fix_length_and_dec() { max_length=1; maybe_null=0; } + unsigned int size_of() { return sizeof(*this);} }; @@ -665,6 +736,7 @@ public: return res; } Item_result result_type () const { return udf.result_type(); } + unsigned int size_of() { return sizeof(*this);} }; @@ -772,7 +844,8 @@ class Item_func_get_lock :public Item_int_func Item_func_get_lock(Item *a,Item *b) :Item_int_func(a,b) {} longlong val_int(); const char *func_name() const { return "get_lock"; } - void fix_length_and_dec() { decimals=0; max_length=1; maybe_null=1;} + void fix_length_and_dec() { max_length=1; maybe_null=1;} + unsigned int size_of() { return sizeof(*this);} }; class Item_func_release_lock :public Item_int_func @@ -782,7 +855,8 @@ class Item_func_release_lock :public Item_int_func Item_func_release_lock(Item *a) :Item_int_func(a) {} longlong val_int(); const char *func_name() const { return "release_lock"; } - void fix_length_and_dec() { decimals=0; max_length=1; maybe_null=1;} + void fix_length_and_dec() { max_length=1; maybe_null=1;} + unsigned int size_of() { return sizeof(*this);} }; /* replication functions */ @@ -792,9 +866,11 @@ class Item_master_pos_wait :public Item_int_func String value; public: Item_master_pos_wait(Item *a,Item *b) :Item_int_func(a,b) {} + Item_master_pos_wait(Item *a,Item *b,Item *c) :Item_int_func(a,b,c) {} longlong val_int(); const char *func_name() const { return "master_pos_wait"; } - void fix_length_and_dec() { decimals=0; max_length=1; maybe_null=1;} + void fix_length_and_dec() { max_length=21; maybe_null=1;} + unsigned int size_of() { return sizeof(*this);} }; @@ -820,18 +896,18 @@ public: void fix_length_and_dec(); void print(String *str); const char *func_name() const { return "set_user_var"; } + unsigned int size_of() { return sizeof(*this);} }; class Item_func_get_user_var :public Item_func { LEX_STRING name; - user_var_entry *entry; - bool const_var_flag; + user_var_entry *var_entry; public: Item_func_get_user_var(LEX_STRING a): - Item_func(), name(a), const_var_flag(1) {} + Item_func(), name(a) {} user_var_entry *get_entry(); double val(); longlong val_int(); @@ -840,10 +916,11 @@ public: void print(String *str); enum Item_result result_type() const; const char *func_name() const { return "get_user_var"; } - bool const_item() const { return const_var_flag; } + bool const_item() const; table_map used_tables() const - { return const_var_flag ? 0 : RAND_TABLE_BIT; } + { return const_item() ? 0 : RAND_TABLE_BIT; } bool eq(const Item *item, bool binary_cmp) const; + unsigned int size_of() { return sizeof(*this);} }; @@ -864,35 +941,91 @@ class Item_func_match :public Item_real_func { public: List<Item> fields; + String value; TABLE *table; - uint key; - bool join_key; Item_func_match *master; - FT_DOCLIST *ft_handler; + FT_INFO * ft_handler; + Item *concat; + byte *record; + uint key, mode; + bool join_key; Item_func_match(List<Item> &a, Item *b): Item_real_func(b), - fields(a), table(0), join_key(0), master(0), ft_handler(0) {} + fields(a), table(0), master(0), ft_handler(0), + concat(0), key(0), join_key(0) + {} ~Item_func_match() { - if (!master) + if (!master && ft_handler) { - if (ft_handler) - { - ft_close_search(ft_handler); - if(join_key) - table->file->ft_handler=0; - table->fulltext_searched=0; - } + ft_handler->please->close_search(ft_handler); + ft_handler=0; + if(join_key) + table->file->ft_handler=0; + table->fulltext_searched=0; } + if (concat) + delete concat; } - const char *func_name() const { return "match"; } enum Functype functype() const { return FT_FUNC; } void update_used_tables() {} bool fix_fields(THD *thd,struct st_table_list *tlist); bool eq(const Item *, bool binary_cmp) const; - double val(); longlong val_int() { return val()!=0.0; } + double val(); bool fix_index(); void init_search(bool no_order); + unsigned int size_of() { return sizeof(*this);} +}; + + +class Item_func_match_nl :public Item_func_match +{ +public: + Item_func_match_nl(List<Item> &a, Item *b) + :Item_func_match(a,b) + { mode=FT_NL; } + const char *func_name() const { return "match_nl"; } +}; + + +class Item_func_match_bool :public Item_func_match +{ +public: + Item_func_match_bool(List<Item> &a, Item *b) + :Item_func_match(a,b) + { mode=FT_BOOL; } + const char *func_name() const { return "match_bool"; } +}; + +/* For type casts */ + +enum Item_cast +{ + ITEM_CAST_BINARY, ITEM_CAST_SIGNED_INT, ITEM_CAST_UNSIGNED_INT, + ITEM_CAST_DATE, ITEM_CAST_TIME, ITEM_CAST_DATETIME, ITEM_CAST_CHAR +}; + +Item *create_func_cast(Item *a, Item_cast cast_type); + + +class Item_func_bit_xor : public Item_int_func +{ +public: + Item_func_bit_xor(Item *a,Item *b) :Item_int_func(a,b) {} + longlong val_int(); + const char *func_name() const { return "^"; } + void fix_length_xor_dec() { unsigned_flag=1; } +}; + +class Item_func_is_free_lock :public Item_int_func +{ + String value; +public: + Item_func_is_free_lock(Item *a) :Item_int_func(a) {} + longlong val_int(); + const char *func_name() const { return "check_lock"; } + void fix_length_and_dec() { decimals=0; max_length=1; maybe_null=1;} + unsigned int size_of() { return sizeof(*this);} }; diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 323810398ec..f1e37889d5f 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -17,7 +17,7 @@ /* This file defines all string functions ** Warning: Some string functions doesn't always put and end-null on a String -** (This shouldn't be neaded) +** (This shouldn't be needed) */ #ifdef __GNUC__ @@ -30,8 +30,12 @@ #ifdef HAVE_CRYPT_H #include <crypt.h> #endif - +#ifdef HAVE_OPENSSL +#include <openssl/des.h> +#endif /* HAVE_OPENSSL */ #include "md5.h" +#include "sha1.h" +#include "my_aes.h" String empty_string(""); @@ -40,7 +44,7 @@ uint nr_of_decimals(const char *str) if ((str=strchr(str,'.'))) { const char *start= ++str; - for ( ; isdigit(*str) ; str++) ; + for (; isdigit(*str) ; str++) ; return (uint) (str-start); } return 0; @@ -66,14 +70,18 @@ String *Item_func_md5::val_str(String *str) String * sptr= args[0]->val_str(str); if (sptr) { - MD5_CTX context; + my_MD5_CTX context; unsigned char digest[16]; null_value=0; - MD5Init (&context); - MD5Update (&context,(unsigned char *) sptr->ptr(), sptr->length()); - MD5Final (digest, &context); - str->alloc(32); // Ensure that memory is free + my_MD5Init (&context); + my_MD5Update (&context,(unsigned char *) sptr->ptr(), sptr->length()); + my_MD5Final (digest, &context); + if (str->alloc(32)) // Ensure that memory is free + { + null_value=1; + return 0; + } sprintf((char *) str->ptr(), "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", digest[0], digest[1], digest[2], digest[3], @@ -93,10 +101,123 @@ void Item_func_md5::fix_length_and_dec() max_length=32; } + +String *Item_func_sha::val_str(String *str) +{ + String * sptr= args[0]->val_str(str); + if (sptr) /* If we got value different from NULL */ + { + SHA1_CONTEXT context; /* Context used to generate SHA1 hash */ + /* Temporary buffer to store 160bit digest */ + uint8 digest[SHA1_HASH_SIZE]; + sha1_reset(&context); /* We do not have to check for error here */ + /* No need to check error as the only case would be too long message */ + sha1_input(&context,(const unsigned char *) sptr->ptr(), sptr->length()); + /* Ensure that memory is free and we got result */ + if (!( str->alloc(SHA1_HASH_SIZE*2) || (sha1_result(&context,digest)))) + { + sprintf((char *) str->ptr(), + "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\ +%02x%02x%02x%02x%02x%02x%02x%02x", + digest[0], digest[1], digest[2], digest[3], + digest[4], digest[5], digest[6], digest[7], + digest[8], digest[9], digest[10], digest[11], + digest[12], digest[13], digest[14], digest[15], + digest[16], digest[17], digest[18], digest[19]); + + str->length((uint) SHA1_HASH_SIZE*2); + null_value=0; + return str; + } + } + null_value=1; + return 0; +} + +void Item_func_sha::fix_length_and_dec() +{ + max_length=SHA1_HASH_SIZE*2; // size of hex representation of hash +} + + +/* Implementation of AES encryption routines */ + +String *Item_func_aes_encrypt::val_str(String *str) +{ + char key_buff[80]; + String tmp_key_value(key_buff, sizeof(key_buff)); + String *sptr= args[0]->val_str(str); // String to encrypt + String *key= args[1]->val_str(&tmp_key_value); // key + int aes_length; + if (sptr && key) // we need both arguments to be not NULL + { + null_value=0; + aes_length=my_aes_get_size(sptr->length()); // Calculate result length + + if (!str_value.alloc(aes_length)) // Ensure that memory is free + { + // finally encrypt directly to allocated buffer. + if (my_aes_encrypt(sptr->ptr(),sptr->length(), (char*) str_value.ptr(), + key->ptr(), key->length()) == aes_length) + { + // We got the expected result length + str_value.length((uint) aes_length); + return &str_value; + } + } + } + null_value=1; + return 0; +} + + +void Item_func_aes_encrypt::fix_length_and_dec() +{ + max_length=my_aes_get_size(args[0]->max_length); +} + + +String *Item_func_aes_decrypt::val_str(String *str) +{ + char key_buff[80]; + String tmp_key_value(key_buff, sizeof(key_buff)), *sptr, *key; + DBUG_ENTER("Item_func_aes_decrypt::val_str"); + + sptr= args[0]->val_str(str); // String to decrypt + key= args[1]->val_str(&tmp_key_value); // Key + if (sptr && key) // Need to have both arguments not NULL + { + null_value=0; + if (!str_value.alloc(sptr->length())) // Ensure that memory is free + { + // finally decrypt directly to allocated buffer. + int length; + length=my_aes_decrypt(sptr->ptr(), sptr->length(), + (char*) str_value.ptr(), + key->ptr(), key->length()); + if (length >= 0) // if we got correct data data + { + str_value.length((uint) length); + DBUG_RETURN(&str_value); + } + } + } + // Bad parameters. No memory or bad data will all go here + null_value=1; + DBUG_RETURN(0); +} + + +void Item_func_aes_decrypt::fix_length_and_dec() +{ + max_length=args[0]->max_length; +} + + /* -** Concatinate args with the following premissess -** If only one arg which is ok, return value of arg -** Don't reallocate val_str() if not absolute necessary. + Concatenate args with the following premises: + If only one arg (which is ok), return value of arg + Don't reallocate val_str() if not absolute necessary. */ String *Item_func_concat::val_str(String *str) @@ -121,7 +242,8 @@ String *Item_func_concat::val_str(String *str) goto null; if (res2->length() == 0) continue; - if (res->length()+res2->length() > max_allowed_packet) + if (res->length()+res2->length() > + current_thd->variables.max_allowed_packet) goto null; // Error check if (res->alloced_length() >= res->length()+res2->length()) { // Use old buffer @@ -199,11 +321,166 @@ void Item_func_concat::fix_length_and_dec() } } +/* + Function des_encrypt() by tonu@spam.ee & monty + Works only if compiled with OpenSSL library support. + This returns a binary string where first character is CHAR(128 | key-number). + If one uses a string key key_number is 127. + Encryption result is longer than original by formula: + new_length= org_length + (8-(org_length % 8))+1 +*/ + +String *Item_func_des_encrypt::val_str(String *str) +{ +#ifdef HAVE_OPENSSL + des_cblock ivec; + struct st_des_keyblock keyblock; + struct st_des_keyschedule keyschedule; + const char *append_str="********"; + uint key_number, res_length, tail; + String *res= args[0]->val_str(str); + + if ((null_value=args[0]->null_value)) + return 0; + if ((res_length=res->length()) == 0) + return &empty_string; + + if (arg_count == 1) + { + /* Protect against someone doing FLUSH DES_KEY_FILE */ + VOID(pthread_mutex_lock(&LOCK_des_key_file)); + keyschedule= des_keyschedule[key_number=des_default_key]; + VOID(pthread_mutex_unlock(&LOCK_des_key_file)); + } + else if (args[1]->result_type() == INT_RESULT) + { + key_number= (uint) args[1]->val_int(); + if (key_number > 9) + goto error; + VOID(pthread_mutex_lock(&LOCK_des_key_file)); + keyschedule= des_keyschedule[key_number]; + VOID(pthread_mutex_unlock(&LOCK_des_key_file)); + } + else + { + String *keystr=args[1]->val_str(&tmp_value); + if (!keystr) + goto error; + key_number=127; // User key string + + /* We make good 24-byte (168 bit) key from given plaintext key with MD5 */ + bzero((char*) &ivec,sizeof(ivec)); + EVP_BytesToKey(EVP_des_ede3_cbc(),EVP_md5(),NULL, + (uchar*) keystr->ptr(), (int) keystr->length(), + 1, (uchar*) &keyblock,ivec); + des_set_key_unchecked(&keyblock.key1,keyschedule.ks1); + des_set_key_unchecked(&keyblock.key2,keyschedule.ks2); + des_set_key_unchecked(&keyblock.key3,keyschedule.ks3); + } + + /* + The problem: DES algorithm requires original data to be in 8-bytes + chunks. Missing bytes get filled with '*'s and result of encryption + can be up to 8 bytes longer than original string. When decrypted, + we do not know the size of original string :( + We add one byte with value 0x1..0x8 as the last byte of the padded + string marking change of string length. + */ + + tail= (8-(res_length) % 8); // 1..8 marking extra length + res_length+=tail; + if (tail && res->append(append_str, tail) || tmp_value.alloc(res_length+1)) + goto error; + (*res)[res_length-1]=tail; // save extra length + tmp_value.length(res_length+1); + tmp_value[0]=(char) (128 | key_number); + // Real encryption + bzero((char*) &ivec,sizeof(ivec)); + des_ede3_cbc_encrypt((const uchar*) (res->ptr()), + (uchar*) (tmp_value.ptr()+1), + res_length, + keyschedule.ks1, + keyschedule.ks2, + keyschedule.ks3, + &ivec, TRUE); + return &tmp_value; + +error: +#endif /* HAVE_OPENSSL */ + null_value=1; + return 0; +} + + +String *Item_func_des_decrypt::val_str(String *str) +{ +#ifdef HAVE_OPENSSL + des_key_schedule ks1, ks2, ks3; + des_cblock ivec; + struct st_des_keyblock keyblock; + struct st_des_keyschedule keyschedule; + String *res= args[0]->val_str(str); + uint length=res->length(),tail; + + if ((null_value=args[0]->null_value)) + return 0; + length=res->length(); + if (length < 9 || (length % 8) != 1 || !((*res)[0] & 128)) + return res; // Skip decryption if not encrypted + + if (arg_count == 1) // If automatic uncompression + { + uint key_number=(uint) (*res)[0] & 127; + // Check if automatic key and that we have privilege to uncompress using it + if (!(current_thd->master_access & SUPER_ACL) || key_number > 9) + goto error; + VOID(pthread_mutex_lock(&LOCK_des_key_file)); + keyschedule= des_keyschedule[key_number]; + VOID(pthread_mutex_unlock(&LOCK_des_key_file)); + } + else + { + // We make good 24-byte (168 bit) key from given plaintext key with MD5 + String *keystr=args[1]->val_str(&tmp_value); + if (!keystr) + goto error; + + bzero((char*) &ivec,sizeof(ivec)); + EVP_BytesToKey(EVP_des_ede3_cbc(),EVP_md5(),NULL, + (uchar*) keystr->ptr(),(int) keystr->length(), + 1,(uchar*) &keyblock,ivec); + // Here we set all 64-bit keys (56 effective) one by one + des_set_key_unchecked(&keyblock.key1,keyschedule.ks1); + des_set_key_unchecked(&keyblock.key2,keyschedule.ks2); + des_set_key_unchecked(&keyblock.key3,keyschedule.ks3); + } + if (tmp_value.alloc(length-1)) + goto error; + + bzero((char*) &ivec,sizeof(ivec)); + des_ede3_cbc_encrypt((const uchar*) res->ptr()+1, + (uchar*) (tmp_value.ptr()), + length-1, + keyschedule.ks1, + keyschedule.ks2, + keyschedule.ks3, + &ivec, FALSE); + /* Restore old length of key */ + if ((tail=(uint) (uchar) tmp_value[length-2]) > 8) + goto error; // Wrong key + tmp_value.length(length-1-tail); + return &tmp_value; + +error: +#endif /* HAVE_OPENSSL */ + null_value=1; + return 0; +} /* -** concat with separator. First arg is the separator -** concat_ws takes at least two arguments. + concat with separator. First arg is the separator + concat_ws takes at least two arguments. */ String *Item_func_concat_ws::val_str(String *str) @@ -232,10 +509,10 @@ String *Item_func_concat_ws::val_str(String *str) for (i++; i < arg_count ; i++) { if (!(res2= args[i]->val_str(use_as_buff)) || !res2->length()) - continue; // Skipp NULL and empty string + continue; // Skip NULL and empty string if (res->length() + sep_str->length() + res2->length() > - max_allowed_packet) + current_thd->variables.max_allowed_packet) goto null; // Error check if (res->alloced_length() >= res->length() + sep_str->length() + res2->length()) @@ -397,7 +674,7 @@ void Item_func_reverse::fix_length_and_dec() /* ** Replace all occurences of string2 in string1 with string3. -** Don't reallocate val_str() if not neaded +** Don't reallocate val_str() if not needed */ /* TODO: Fix that this works with binary strings when using USE_MB */ @@ -456,7 +733,8 @@ redo: while (j != search_end) if (*i++ != *j++) goto skipp; offset= (int) (ptr-res->ptr()); - if (res->length()-from_length + to_length > max_allowed_packet) + if (res->length()-from_length + to_length > + current_thd->variables.max_allowed_packet) goto null; if (!alloced) { @@ -476,7 +754,8 @@ skipp: #endif /* USE_MB */ do { - if (res->length()-from_length + to_length > max_allowed_packet) + if (res->length()-from_length + to_length > + current_thd->variables.max_allowed_packet) goto null; if (!alloced) { @@ -533,10 +812,11 @@ String *Item_func_insert::val_str(String *str) } #endif if (start > res->length()+1) - return res; // Wrong param; skipp insert + return res; // Wrong param; skip insert if (length > res->length()-start) length=res->length()-start; - if (res->length() - length + res2->length() > max_allowed_packet) + if (res->length() - length + res2->length() > + current_thd->variables.max_allowed_packet) goto null; // OOM check res=copy_if_not_alloced(str,res,res->length()); res->replace(start,length,*res2); @@ -1110,7 +1390,7 @@ void Item_func_soundex::fix_length_and_dec() /* If alpha, map input letter to soundex code. - If not alpha and remove_garbage is set then skipp to next char + If not alpha and remove_garbage is set then skip to next char else return 0 */ @@ -1137,12 +1417,12 @@ String *Item_func_soundex::val_str(String *str) if ((null_value=args[0]->null_value)) return 0; /* purecov: inspected */ - if (str_value.alloc(max(res->length(),4))) + if (tmp_value.alloc(max(res->length(),4))) return str; /* purecov: inspected */ - char *to= (char *) str_value.ptr(); + char *to= (char *) tmp_value.ptr(); char *from= (char *) res->ptr(), *end=from+res->length(); - while (from != end && isspace(*from)) // Skipp pre-space + while (from != end && isspace(*from)) // Skip pre-space from++; /* purecov: inspected */ if (from == end) return &empty_string; // No alpha characters. @@ -1163,11 +1443,11 @@ String *Item_func_soundex::val_str(String *str) last_ch = ch; // save code of last input letter } // for next double-letter check } - for (end=(char*) str_value.ptr()+4 ; to < end ; to++) + for (end=(char*) tmp_value.ptr()+4 ; to < end ; to++) *to = '0'; *to=0; // end string - str_value.length((uint) (to-str_value.ptr())); - return &str_value; + tmp_value.length((uint) (to-tmp_value.ptr())); + return &tmp_value; } @@ -1344,7 +1624,7 @@ String *Item_func_make_set::val_str(String *str) if (bits & 1) { String *res= (*ptr)->val_str(str); - if (res) // Skipp nulls + if (res) // Skip nulls { if (!first_found) { // First argument @@ -1468,7 +1748,8 @@ String *Item_func_repeat::val_str(String *str) if (count == 1) // To avoid reallocs return res; length=res->length(); - if (length > max_allowed_packet/count)// Safe length check + // Safe length check + if (length > current_thd->variables.max_allowed_packet/count) goto err; // Probably an error tot_length= length*(uint) count; if (!(res= alloc_buffer(res,str,&tmp_value,tot_length))) @@ -1526,7 +1807,8 @@ String *Item_func_rpad::val_str(String *str) return (res); } length_pad= rpad->length(); - if ((ulong) count > max_allowed_packet || args[2]->null_value || !length_pad) + if ((ulong) count > current_thd->variables.max_allowed_packet || + args[2]->null_value || !length_pad) goto err; if (!(res= alloc_buffer(res,str,&tmp_value,count))) goto err; @@ -1585,7 +1867,8 @@ String *Item_func_lpad::val_str(String *str) return (res); } length_pad= lpad->length(); - if (count > max_allowed_packet || args[2]->null_value || !length_pad) + if (count > current_thd->variables.max_allowed_packet || + args[2]->null_value || !length_pad) goto err; if (res->alloced_length() < count) @@ -1649,6 +1932,45 @@ String *Item_func_conv::val_str(String *str) return str; } + +String *Item_func_hex::val_str(String *str) +{ + if (args[0]->result_type() != STRING_RESULT) + { + /* Return hex of unsigned longlong value */ + longlong dec= args[0]->val_int(); + char ans[65],*ptr; + if ((null_value= args[0]->null_value)) + return 0; + ptr= longlong2str(dec,ans,16); + if (str->copy(ans,(uint32) (ptr-ans))) + return &empty_string; // End of memory + return str; + } + + /* Convert given string to a hex string, character by character */ + String *res= args[0]->val_str(str); + const char *from, *end; + char *to; + if (!res || tmp_value.alloc(res->length()*2)) + { + null_value=1; + return 0; + } + null_value=0; + tmp_value.length(res->length()*2); + for (from=res->ptr(), end=from+res->length(), to= (char*) tmp_value.ptr(); + from != end ; + from++, to+=2) + { + uint tmp=(uint) (uchar) *from; + to[0]=_dig_vec[tmp >> 4]; + to[1]=_dig_vec[tmp & 15]; + } + return &tmp_value; +} + + #include <my_dir.h> // For my_stat String *Item_load_file::val_str(String *str) @@ -1667,7 +1989,7 @@ String *Item_load_file::val_str(String *str) /* my_error(ER_TEXTFILE_NOT_READABLE, MYF(0), file_name->c_ptr()); */ goto err; } - if (stat_info.st_size > (long) max_allowed_packet) + if (stat_info.st_size > (long) current_thd->variables.max_allowed_packet) { /* my_error(ER_TOO_LONG_STRING, MYF(0), file_name->c_ptr()); */ goto err; @@ -1741,7 +2063,7 @@ String* Item_func_export_set::val_str(String* str) str->append(*yes); else str->append(*no); - if(i != num_set_values - 1) + if (i != num_set_values - 1) str->append(*sep); } return str; @@ -1759,24 +2081,23 @@ String* Item_func_inet_ntoa::val_str(String* str) uchar buf[8], *p; ulonglong n = (ulonglong) args[0]->val_int(); char num[4]; + /* - we do not know if args[0] is NULL until we have called + We do not know if args[0] is NULL until we have called some val function on it if args[0] is not a constant! + + Also return null if n > 255.255.255.255 */ - if ((null_value=args[0]->null_value)) + if ((null_value= (args[0]->null_value || n > (ulonglong) LL(4294967295)))) return 0; // Null value str->length(0); - int8store(buf,n); + int4store(buf,n); - /* - Now we can assume little endian. - We handle the possibility of an 8-byte IP address however, we do - not want to confuse those who are just using 4 byte ones - */ - for (p= buf + 8; p > buf+4 && p[-1] == 0 ; p-- ) ; + /* Now we can assume little endian. */ + num[3]='.'; - while (p-- > buf) + for (p=buf+4 ; p-- > buf ; ) { uint c = *p; uint n1,n2; // Try to avoid divisions @@ -1794,3 +2115,94 @@ String* Item_func_inet_ntoa::val_str(String* str) str->length(str->length()-1); // Remove last '.'; return str; } + +/* + QUOTE() function returns argument string in single quotes suitable for + using in a SQL statement. + + DESCRIPTION + Adds a \ before all characters that needs to be escaped in a SQL string. + We also escape '^Z' (END-OF-FILE in windows) to avoid probelms when + running commands from a file in windows. + + This function is very useful when you want to generate SQL statements + + RETURN VALUES + str Quoted string + NULL Argument to QUOTE() was NULL or out of memory. +*/ + +#define get_esc_bit(mask, num) (1 & (*((mask) + ((num) >> 3))) >> ((num) & 7)) + +String *Item_func_quote::val_str(String *str) +{ + /* + Bit mask that has 1 for set for the position of the following characters: + 0, \, ' and ^Z + */ + + static uchar escmask[32]= + { + 0x01, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + char *from, *to, *end, *start; + String *arg= args[0]->val_str(str); + uint arg_length, new_length; + if (!arg) // Null argument + goto null; + arg_length= arg->length(); + new_length= arg_length+2; /* for beginning and ending ' signs */ + + for (from= (char*) arg->ptr(), end= from + arg_length; from < end; from++) + new_length+= get_esc_bit(escmask, *from); + + /* + We have to use realloc() instead of alloc() as we want to keep the + old result in str + */ + if (str->realloc(new_length)) + goto null; + + /* + As 'arg' and 'str' may be the same string, we must replace characters + from the end to the beginning + */ + to= (char*) str->ptr() + new_length - 1; + *to--= '\''; + for (start= (char*) arg->ptr(),end= start + arg_length; end-- != start; to--) + { + /* + We can't use the bitmask here as we want to replace \O and ^Z with 0 + and Z + */ + switch (*end) { + case 0: + *to--= '0'; + *to= '\\'; + break; + case '\032': + *to--= 'Z'; + *to= '\\'; + break; + case '\'': + case '\\': + *to--= *end; + *to= '\\'; + break; + default: + *to= *end; + break; + } + } + *to= '\''; + str->length(new_length); + return str; + +null: + null_value= 1; + return 0; +} diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index a955c0ed4f3..5c9706ed633 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -35,8 +35,10 @@ public: double val(); enum Item_result result_type () const { return STRING_RESULT; } void left_right_max_length(); + unsigned int size_of() { return sizeof(*this);} }; + class Item_func_md5 :public Item_str_func { String tmp_value; @@ -45,8 +47,38 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "md5"; } + unsigned int size_of() { return sizeof(*this);} }; + +class Item_func_sha :public Item_str_func +{ +public: + Item_func_sha(Item *a) :Item_str_func(a) {} + String *val_str(String *); + void fix_length_and_dec(); + const char *func_name() const { return "sha"; } +}; + +class Item_func_aes_encrypt :public Item_str_func +{ +public: + Item_func_aes_encrypt(Item *a, Item *b) :Item_str_func(a,b) {} + String *val_str(String *); + void fix_length_and_dec(); + const char *func_name() const { return "aes_encrypt"; } +}; + +class Item_func_aes_decrypt :public Item_str_func +{ +public: + Item_func_aes_decrypt(Item *a, Item *b) :Item_str_func(a,b) {} + String *val_str(String *); + void fix_length_and_dec(); + const char *func_name() const { return "aes_decrypt"; } +}; + + class Item_func_concat :public Item_str_func { String tmp_value; @@ -56,6 +88,7 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "concat"; } + unsigned int size_of() { return sizeof(*this);} }; class Item_func_concat_ws :public Item_str_func @@ -97,6 +130,7 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "replace"; } + unsigned int size_of() { return sizeof(*this);} }; @@ -109,6 +143,7 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "insert"; } + unsigned int size_of() { return sizeof(*this);} }; @@ -155,6 +190,7 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "right"; } + unsigned int size_of() { return sizeof(*this);} }; @@ -167,6 +203,7 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "substr"; } + unsigned int size_of() { return sizeof(*this);} }; @@ -178,6 +215,7 @@ public: String *val_str(String *); void fix_length_and_dec() { max_length= args[0]->max_length; } const char *func_name() const { return "substr_index"; } + unsigned int size_of() { return sizeof(*this);} }; @@ -189,6 +227,7 @@ public: String *val_str(String *); void fix_length_and_dec() { max_length= args[0]->max_length; } const char *func_name() const { return "ltrim"; } + unsigned int size_of() { return sizeof(*this);} }; @@ -200,6 +239,7 @@ public: String *val_str(String *); void fix_length_and_dec() { max_length= args[0]->max_length; } const char *func_name() const { return "rtrim"; } + unsigned int size_of() { return sizeof(*this);} }; class Item_func_trim :public Item_str_func @@ -210,6 +250,7 @@ public: String *val_str(String *); void fix_length_and_dec() { max_length= args[0]->max_length; } const char *func_name() const { return "trim"; } + unsigned int size_of() { return sizeof(*this);} }; @@ -221,6 +262,32 @@ public: String *val_str(String *); void fix_length_and_dec() { max_length = 16; } const char *func_name() const { return "password"; } + unsigned int size_of() { return sizeof(*this);} +}; + +class Item_func_des_encrypt :public Item_str_func +{ + String tmp_value; +public: + Item_func_des_encrypt(Item *a) :Item_str_func(a) {} + Item_func_des_encrypt(Item *a, Item *b): Item_str_func(a,b) {} + String *val_str(String *); + void fix_length_and_dec() + { maybe_null=1; max_length = args[0]->max_length+8; } + const char *func_name() const { return "des_encrypt"; } + unsigned int size_of() { return sizeof(*this);} +}; + +class Item_func_des_decrypt :public Item_str_func +{ + String tmp_value; +public: + Item_func_des_decrypt(Item *a) :Item_str_func(a) {} + Item_func_des_decrypt(Item *a, Item *b): Item_str_func(a,b) {} + String *val_str(String *); + void fix_length_and_dec() { maybe_null=1; max_length = args[0]->max_length; } + const char *func_name() const { return "des_decrypt"; } + unsigned int size_of() { return sizeof(*this);} }; class Item_func_encrypt :public Item_str_func @@ -231,6 +298,7 @@ public: Item_func_encrypt(Item *a, Item *b): Item_str_func(a,b) {} String *val_str(String *); void fix_length_and_dec() { maybe_null=1; max_length = 13; } + unsigned int size_of() { return sizeof(*this);} }; #include "sql_crypt.h" @@ -244,6 +312,7 @@ public: Item_str_func(a),sql_crypt(seed) {} String *val_str(String *); void fix_length_and_dec(); + unsigned int size_of() { return sizeof(*this);} }; class Item_func_decode :public Item_func_encode @@ -275,11 +344,13 @@ public: class Item_func_soundex :public Item_str_func { + String tmp_value; public: Item_func_soundex(Item *a) :Item_str_func(a) {} String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "soundex"; } + unsigned int size_of() { return sizeof(*this);} }; @@ -301,6 +372,7 @@ public: void fix_length_and_dec(); void update_used_tables(); const char *func_name() const { return "elt"; } + unsigned int size_of() { return sizeof(*this);} }; @@ -321,6 +393,7 @@ public: void fix_length_and_dec(); void update_used_tables(); const char *func_name() const { return "make_set"; } + unsigned int size_of() { return sizeof(*this);} }; @@ -335,6 +408,7 @@ public: max_length=args[0]->max_length+(args[0]->max_length-args[0]->decimals)/3; } const char *func_name() const { return "format"; } + unsigned int size_of() { return sizeof(*this);} }; @@ -356,6 +430,7 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "repeat"; } + unsigned int size_of() { return sizeof(*this);} }; @@ -368,6 +443,7 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "rpad"; } + unsigned int size_of() { return sizeof(*this);} }; @@ -380,6 +456,7 @@ public: String *val_str(String *); void fix_length_and_dec(); const char *func_name() const { return "lpad"; } + unsigned int size_of() { return sizeof(*this);} }; @@ -392,6 +469,19 @@ public: void fix_length_and_dec() { decimals=0; max_length=64; } }; + +class Item_func_hex :public Item_str_func +{ + String tmp_value; +public: + Item_func_hex(Item *a) :Item_str_func(a) {} + const char *func_name() const { return "hex"; } + String *val_str(String *); + void fix_length_and_dec() { decimals=0; max_length=args[0]->max_length*2; } + unsigned int size_of() { return sizeof(*this);} +}; + + class Item_func_binary :public Item_str_func { public: @@ -417,6 +507,7 @@ public: const char *func_name() const { return "load_file"; } void fix_length_and_dec() { binary=1; maybe_null=1; max_length=MAX_BLOB_WIDTH;} + unsigned int size_of() { return sizeof(*this);} }; @@ -441,3 +532,12 @@ public: const char *func_name() const { return "inet_ntoa"; } void fix_length_and_dec() { decimals = 0; max_length=3*8+7; } }; + +class Item_func_quote :public Item_str_func +{ +public: + Item_func_quote(Item *a) :Item_str_func(a) {} + const char *func_name() const { return "quote"; } + String *val_str(String *); + void fix_length_and_dec() { max_length= args[0]->max_length * 2 + 2; } +}; diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 5b24a1eda90..b6bbc12efd6 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -1,15 +1,15 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - +/* Copyright (C) 2000-2003 MySQL AB + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -30,7 +30,7 @@ Item_sum::Item_sum(List<Item> &list) if ((args=(Item**) sql_alloc(sizeof(Item*)*arg_count))) { uint i=0; - List_iterator<Item> li(list); + List_iterator_fast<Item> li(list); Item *item; while ((item=li++)) @@ -52,6 +52,8 @@ void Item_sum::make_field(Send_field *tmp_field) tmp_field->flags=0; if (!maybe_null) tmp_field->flags|= NOT_NULL_FLAG; + if (unsigned_flag) + tmp_field->flags |= UNSIGNED_FLAG; tmp_field->length=max_length; tmp_field->decimals=decimals; tmp_field->type=(result_type() == INT_RESULT ? FIELD_TYPE_LONG : @@ -150,7 +152,7 @@ Item_sum_hybrid::fix_fields(THD *thd,TABLE_LIST *tables) return 1; hybrid_type=item->result_type(); if (hybrid_type == INT_RESULT) - max_length=21; + max_length=20; else if (hybrid_type == REAL_RESULT) max_length=float_length(decimals); else @@ -158,6 +160,7 @@ Item_sum_hybrid::fix_fields(THD *thd,TABLE_LIST *tables) decimals=item->decimals; maybe_null=item->maybe_null; binary=item->binary; + unsigned_flag=item->unsigned_flag; result_field=0; null_value=1; fix_length_and_dec(); @@ -323,12 +326,27 @@ double Item_sum_hybrid::val() { if (null_value) return 0.0; - if (hybrid_type == STRING_RESULT) - { + switch (hybrid_type) { + case STRING_RESULT: String *res; res=val_str(&str_value); return res ? atof(res->c_ptr()) : 0.0; + case INT_RESULT: + if (unsigned_flag) + return ulonglong2double(sum_int); + return (double) sum_int; + case REAL_RESULT: + return sum; } - return sum; + return 0; // Keep compiler happy +} + +longlong Item_sum_hybrid::val_int() +{ + if (null_value) + return 0; + if (hybrid_type == INT_RESULT) + return sum_int; + return (longlong) Item_sum_hybrid::val(); } @@ -337,25 +355,26 @@ Item_sum_hybrid::val_str(String *str) { if (null_value) return 0; - if (hybrid_type == STRING_RESULT) + switch (hybrid_type) { + case STRING_RESULT: return &value; - str->set(sum,decimals); - return str; + case REAL_RESULT: + str->set(sum,decimals); + break; + case INT_RESULT: + if (unsigned_flag) + str->set((ulonglong) sum_int); + else + str->set((longlong) sum_int); + break; + } + return str; // Keep compiler happy } - bool Item_sum_min::add() { - if (hybrid_type != STRING_RESULT) - { - double nr=args[0]->val(); - if (!args[0]->null_value && (null_value || nr < sum)) - { - sum=nr; - null_value=0; - } - } - else + switch (hybrid_type) { + case STRING_RESULT: { String *result=args[0]->val_str(&tmp_value); if (!args[0]->null_value && @@ -366,22 +385,39 @@ bool Item_sum_min::add() null_value=0; } } - return 0; -} - - -bool Item_sum_max::add() -{ - if (hybrid_type != STRING_RESULT) + break; + case INT_RESULT: + { + longlong nr=args[0]->val_int(); + if (!args[0]->null_value && (null_value || + (unsigned_flag && + (ulonglong) nr < (ulonglong) sum_int) || + (!unsigned_flag && nr < sum_int))) + { + sum_int=nr; + null_value=0; + } + } + break; + case REAL_RESULT: { double nr=args[0]->val(); - if (!args[0]->null_value && (null_value || nr > sum)) + if (!args[0]->null_value && (null_value || nr < sum)) { sum=nr; null_value=0; } } - else + break; + } + return 0; +} + + +bool Item_sum_max::add() +{ + switch (hybrid_type) { + case STRING_RESULT: { String *result=args[0]->val_str(&tmp_value); if (!args[0]->null_value && @@ -392,6 +428,31 @@ bool Item_sum_max::add() null_value=0; } } + break; + case INT_RESULT: + { + longlong nr=args[0]->val_int(); + if (!args[0]->null_value && (null_value || + (unsigned_flag && + (ulonglong) nr > (ulonglong) sum_int) || + (!unsigned_flag && nr > sum_int))) + { + sum_int=nr; + null_value=0; + } + } + break; + case REAL_RESULT: + { + double nr=args[0]->val(); + if (!args[0]->null_value && (null_value || nr > sum)) + { + sum=nr; + null_value=0; + } + } + break; + } return 0; } @@ -676,9 +737,17 @@ Item_sum_hybrid::min_max_update_int_field(int offset) nr=args[0]->val_int(); if (!args[0]->null_value) { - if (result_field->is_null(offset) || - (cmp_sign > 0 ? old_nr > nr : old_nr < nr)) + if (result_field->is_null(offset)) old_nr=nr; + else + { + bool res=(unsigned_flag ? + (ulonglong) old_nr > (ulonglong) nr : + old_nr > nr); + /* (cmp_sign > 0 && res) || (!(cmp_sign > 0) && !res) */ + if ((cmp_sign > 0) ^ (!res)) + old_nr=nr; + } result_field->set_notnull(); } else if (result_field->is_null(offset)) @@ -788,14 +857,76 @@ String *Item_std_field::val_str(String *str) #include "sql_select.h" +static int simple_raw_key_cmp(void* arg, byte* key1, byte* key2) +{ + return memcmp(key1, key2, *(uint*) arg); +} + +static int simple_str_key_cmp(void* arg, byte* key1, byte* key2) +{ + return my_sortcmp((char*) key1, (char*) key2, *(uint*) arg); +} + +/* + Did not make this one static - at least gcc gets confused when + I try to declare a static function as a friend. If you can figure + out the syntax to make a static function a friend, make this one + static +*/ + +int composite_key_cmp(void* arg, byte* key1, byte* key2) +{ + Item_sum_count_distinct* item = (Item_sum_count_distinct*)arg; + Field **field = item->table->field; + Field **field_end= field + item->table->fields; + uint32 *lengths=item->field_lengths; + for (; field < field_end; ++field) + { + Field* f = *field; + int len = *lengths++; + int res = f->key_cmp(key1, key2); + if (res) + return res; + key1 += len; + key2 += len; + } + return 0; +} + +/* + helper function for walking the tree when we dump it to MyISAM - + tree_walk will call it for each leaf +*/ + +int dump_leaf(byte* key, uint32 count __attribute__((unused)), + Item_sum_count_distinct* item) +{ + byte* buf = item->table->record[0]; + int error; + /* + The first item->rec_offset bytes are taken care of with + restore_record(table,2) in setup() + */ + memcpy(buf + item->rec_offset, key, item->tree.size_of_element); + if ((error = item->table->file->write_row(buf))) + { + if (error != HA_ERR_FOUND_DUPP_KEY && + error != HA_ERR_FOUND_DUPP_UNIQUE) + return 1; + } + return 0; +} + + Item_sum_count_distinct::~Item_sum_count_distinct() { if (table) free_tmp_table(current_thd, table); delete tmp_table_param; + if (use_tree) + delete_tree(&tree); } - bool Item_sum_count_distinct::fix_fields(THD *thd,TABLE_LIST *tables) { if (Item_sum_num::fix_fields(thd,tables) || @@ -829,22 +960,126 @@ bool Item_sum_count_distinct::setup(THD *thd) tmp_table_param->cleanup(); } if (!(table=create_tmp_table(thd, tmp_table_param, list, (ORDER*) 0, 1, - 0, 0, current_lex->options | thd->options))) + 0, 0, + current_lex->select->options | thd->options))) return 1; table->file->extra(HA_EXTRA_NO_ROWS); // Don't update rows + table->no_rows=1; + + + // no blobs, otherwise it would be MyISAM + if (table->db_type == DB_TYPE_HEAP) + { + qsort_cmp2 compare_key; + void* cmp_arg; + + // to make things easier for dump_leaf if we ever have to dump to MyISAM + restore_record(table,2); + + if (table->fields == 1) + { + /* + If we have only one field, which is the most common use of + count(distinct), it is much faster to use a simpler key + compare method that can take advantage of not having to worry + about other fields + */ + Field* field = table->field[0]; + switch(field->type()) + { + /* + If we have a string, we must take care of charsets and case + sensitivity + */ + case FIELD_TYPE_STRING: + case FIELD_TYPE_VAR_STRING: + compare_key = (qsort_cmp2)(field->binary() ? simple_raw_key_cmp: + simple_str_key_cmp); + break; + default: + /* + Since at this point we cannot have blobs anything else can + be compared with memcmp + */ + compare_key = (qsort_cmp2)simple_raw_key_cmp; + break; + } + key_length = field->pack_length(); + cmp_arg = (void*) &key_length; + rec_offset = 1; + } + else // too bad, cannot cheat - there is more than one field + { + bool all_binary = 1; + Field** field, **field_end; + field_end = (field = table->field) + table->fields; + uint32 *lengths; + if (!(field_lengths= + (uint32*) thd->alloc(sizeof(uint32) * table->fields))) + return 1; + + for (key_length = 0, lengths=field_lengths; field < field_end; ++field) + { + uint32 length= (*field)->pack_length(); + key_length += length; + *lengths++ = length; + if (!(*field)->binary()) + all_binary = 0; // Can't break loop here + } + rec_offset = table->reclength - key_length; + if (all_binary) + { + compare_key = (qsort_cmp2)simple_raw_key_cmp; + cmp_arg = (void*) &key_length; + } + else + { + compare_key = (qsort_cmp2) composite_key_cmp ; + cmp_arg = (void*) this; + } + } + + init_tree(&tree, min(thd->variables.max_heap_table_size, + thd->variables.sortbuff_size/16), 0, + key_length, compare_key, 0, NULL, cmp_arg); + use_tree = 1; + + /* + The only time key_length could be 0 is if someone does + count(distinct) on a char(0) field - stupid thing to do, + but this has to be handled - otherwise someone can crash + the server with a DoS attack + */ + max_elements_in_tree = ((key_length) ? + thd->variables.max_heap_table_size/key_length : 1); + } return 0; } +int Item_sum_count_distinct::tree_to_myisam() +{ + if (create_myisam_from_heap(table, tmp_table_param, + HA_ERR_RECORD_FILE_FULL, 1) || + tree_walk(&tree, (tree_walk_action)&dump_leaf, (void*)this, + left_root_right)) + return 1; + delete_tree(&tree); + use_tree = 0; + return 0; +} + void Item_sum_count_distinct::reset() { - if (table) + if (use_tree) + reset_tree(&tree); + else if (table) { table->file->extra(HA_EXTRA_NO_CACHE); table->file->delete_all_rows(); table->file->extra(HA_EXTRA_WRITE_CACHE); - (void) add(); } + (void) add(); } bool Item_sum_count_distinct::add() @@ -853,13 +1088,27 @@ bool Item_sum_count_distinct::add() if (always_null) return 0; copy_fields(tmp_table_param); - copy_funcs(tmp_table_param->funcs); + copy_funcs(tmp_table_param->items_to_copy); for (Field **field=table->field ; *field ; field++) if ((*field)->is_real_null(0)) return 0; // Don't count NULL - if ((error=table->file->write_row(table->record[0]))) + if (use_tree) + { + /* + If the tree got too big, convert to MyISAM, otherwise insert into the + tree. + */ + if (tree.elements_in_tree > max_elements_in_tree) + { + if (tree_to_myisam()) + return 1; + } + else if (!tree_insert(&tree, table->record[0] + rec_offset, 0)) + return 1; + } + else if ((error=table->file->write_row(table->record[0]))) { if (error != HA_ERR_FOUND_DUPP_KEY && error != HA_ERR_FOUND_DUPP_UNIQUE) @@ -875,6 +1124,8 @@ longlong Item_sum_count_distinct::val_int() { if (!table) // Empty query return LL(0); + if (use_tree) + return tree.elements_in_tree; table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); return table->file->records; } @@ -897,7 +1148,7 @@ void Item_udf_sum::reset() bool Item_udf_sum::add() { - DBUG_ENTER("Item_udf_sum::reset"); + DBUG_ENTER("Item_udf_sum::add"); udf.add(&null_value); DBUG_RETURN(0); } diff --git a/sql/item_sum.h b/sql/item_sum.h index f68dfee1b61..29ac1f1d1b1 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -21,6 +21,8 @@ #pragma interface /* gcc class implementation */ #endif +#include <my_tree.h> + class Item_sum :public Item_result_field { public: @@ -62,11 +64,14 @@ public: { return new Item_field(field);} table_map used_tables() const { return ~(table_map) 0; } /* Not used */ bool const_item() const { return 0; } + bool is_null() { return null_value; } void update_used_tables() { } void make_field(Send_field *field); void print(String *str); void fix_num_length_and_dec(); + void no_rows_in_result() { reset(); } virtual bool setup(THD *thd) {return 0;} + unsigned int size_of() { return sizeof(*this);} }; @@ -81,6 +86,7 @@ public: longlong val_int() { return (longlong) val(); } /* Real as default */ String *val_str(String*str); void reset_field(); + unsigned int size_of() { return sizeof(*this);} }; @@ -95,6 +101,7 @@ public: double val() { return (double) val_int(); } String *val_str(String*str); enum Item_result result_type () const { return INT_RESULT; } + unsigned int size_of() { return sizeof(*this);} }; @@ -112,6 +119,7 @@ class Item_sum_sum :public Item_sum_num void reset_field(); void update_field(int offset); const char *func_name() const { return "sum"; } + unsigned int size_of() { return sizeof(*this);} }; @@ -128,12 +136,14 @@ class Item_sum_count :public Item_sum_int bool const_item() const { return !used_table_cache; } enum Sumfunctype sum_func () const { return COUNT_FUNC; } void reset(); + void no_rows_in_result() { count=0; } bool add(); void make_const(longlong count_arg) { count=count_arg; used_table_cache=0; } longlong val_int(); void reset_field(); void update_field(int offset); const char *func_name() const { return "count"; } + unsigned int size_of() { return sizeof(*this);} }; @@ -144,15 +154,39 @@ class Item_sum_count_distinct :public Item_sum_int TABLE *table; table_map used_table_cache; bool fix_fields(THD *thd,TABLE_LIST *tables); + uint32 *field_lengths; TMP_TABLE_PARAM *tmp_table_param; - bool always_null; + TREE tree; + uint key_length; + + // calculated based on max_heap_table_size. If reached, + // walk the tree and dump it into MyISAM table + uint max_elements_in_tree; + + // the first few bytes of record ( at least one) + // are just markers for deleted and NULLs. We want to skip them since + // they will just bloat the tree without providing any valuable info + int rec_offset; + + // If there are no blobs, we can use a tree, which + // is faster than heap table. In that case, we still use the table + // to help get things set up, but we insert nothing in it + bool use_tree; + bool always_null; // Set to 1 if the result is always NULL + + int tree_to_myisam(); + + friend int composite_key_cmp(void* arg, byte* key1, byte* key2); + friend int dump_leaf(byte* key, uint32 count __attribute__((unused)), + Item_sum_count_distinct* item); public: Item_sum_count_distinct(List<Item> &list) :Item_sum_int(list),table(0),used_table_cache(~(table_map) 0), - tmp_table_param(0),always_null(0) + tmp_table_param(0),use_tree(0),always_null(0) { quick_group=0; } ~Item_sum_count_distinct(); + table_map used_tables() const { return used_table_cache; } enum Sumfunctype sum_func () const { return COUNT_DISTINCT_FUNC; } void reset(); @@ -162,6 +196,8 @@ class Item_sum_count_distinct :public Item_sum_int void update_field(int offset) { return ; } // Never called const char *func_name() const { return "count_distinct"; } bool setup(THD *thd); + void no_rows_in_result() {} + unsigned int size_of() { return sizeof(*this);} }; @@ -177,9 +213,11 @@ public: enum Type type() const { return FIELD_AVG_ITEM; } double val(); longlong val_int() { return (longlong) val(); } + bool is_null() { (void) val_int(); return null_value; } String *val_str(String*); void make_field(Send_field *field); void fix_length_and_dec() {} + unsigned int size_of() { return sizeof(*this);} }; @@ -201,6 +239,7 @@ class Item_sum_avg :public Item_sum_num Item *result_item(Field *field) { return new Item_avg_field(this); } const char *func_name() const { return "avg"; } + unsigned int size_of() { return sizeof(*this);} }; class Item_sum_std; @@ -214,8 +253,10 @@ public: double val(); longlong val_int() { return (longlong) val(); } String *val_str(String*); + bool is_null() { (void) val_int(); return null_value; } void make_field(Send_field *field); void fix_length_and_dec() {} + unsigned int size_of() { return sizeof(*this);} }; class Item_sum_std :public Item_sum_num @@ -236,6 +277,7 @@ class Item_sum_std :public Item_sum_num Item *result_item(Field *field) { return new Item_std_field(this); } const char *func_name() const { return "std"; } + unsigned int size_of() { return sizeof(*this);} }; @@ -246,6 +288,7 @@ class Item_sum_hybrid :public Item_sum protected: String value,tmp_value; double sum; + longlong sum_int; Item_result hybrid_type; int cmp_sign; table_map used_table_cache; @@ -261,12 +304,13 @@ class Item_sum_hybrid :public Item_sum void reset() { sum=0.0; + sum_int=0; value.length(0); null_value=1; add(); } double val(); - longlong val_int() { return (longlong) val(); } /* Real as default */ + longlong val_int(); void reset_field(); String *val_str(String *); void make_const() { used_table_cache=0; } @@ -276,6 +320,7 @@ class Item_sum_hybrid :public Item_sum void min_max_update_str_field(int offset); void min_max_update_real_field(int offset); void min_max_update_int_field(int offset); + unsigned int size_of() { return sizeof(*this);} }; @@ -287,6 +332,7 @@ public: bool add(); const char *func_name() const { return "min"; } + unsigned int size_of() { return sizeof(*this);} }; @@ -298,6 +344,7 @@ public: bool add(); const char *func_name() const { return "max"; } + unsigned int size_of() { return sizeof(*this);} }; @@ -313,6 +360,7 @@ class Item_sum_bit :public Item_sum_int void reset(); longlong val_int(); void reset_field(); + unsigned int size_of() { return sizeof(*this);} }; @@ -323,6 +371,7 @@ class Item_sum_or :public Item_sum_bit bool add(); void update_field(int offset); const char *func_name() const { return "bit_or"; } + unsigned int size_of() { return sizeof(*this);} }; @@ -333,6 +382,7 @@ class Item_sum_and :public Item_sum_bit bool add(); void update_field(int offset); const char *func_name() const { return "bit_and"; } + unsigned int size_of() { return sizeof(*this);} }; /* @@ -363,6 +413,7 @@ public: bool add(); void reset_field() {}; void update_field(int offset_arg) {}; + unsigned int size_of() { return sizeof(*this);} }; diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index eb9b1423c78..6a95c15a226 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -175,15 +175,28 @@ longlong Item_func_second::val_int() } -// Returns the week of year in the range of 0 - 53 +/* + Returns the week of year. + + The bits in week_format has the following meaning: + 0 If not set: USA format: Sunday is first day of week + If set: ISO format: Monday is first day of week + 1 If not set: Week is in range 0-53 + If set Week is in range 1-53. +*/ longlong Item_func_week::val_int() { uint year; + uint week_format; TIME ltime; if (get_arg0_date(<ime,0)) return 0; - return (longlong) calc_week(<ime, 0, args[1]->val_int() == 0, &year); + week_format= (uint) args[1]->val_int(); + return (longlong) calc_week(<ime, + (week_format & 2) != 0, + (week_format & 1) == 0, + &year); } @@ -193,7 +206,7 @@ longlong Item_func_yearweek::val_int() TIME ltime; if (get_arg0_date(<ime,0)) return 0; - week=calc_week(<ime, 1, args[1]->val_int() == 0, &year); + week=calc_week(<ime, 1, (args[1]->val_int() & 1) == 0, &year); return week+year*100; } @@ -390,7 +403,7 @@ String *Item_date::val_str(String *str) } -bool Item_date::save_in_field(Field *field) +bool Item_date::save_in_field(Field *field, bool no_conversions) { TIME ltime; timestamp_type t_type=TIMESTAMP_FULL; @@ -505,7 +518,7 @@ bool Item_func_now::get_date(TIME *res, } -bool Item_func_now::save_in_field(Field *to) +bool Item_func_now::save_in_field(Field *to, bool no_conversions) { to->set_notnull(); to->store_time(<ime,TIMESTAMP_FULL); @@ -672,7 +685,7 @@ String *Item_func_date_format::val_str(String *str) else size=format_length(format); if (format == str) - str=&value; // Save result here + str= &value; // Save result here if (str->alloc(size)) { null_value=1; @@ -683,7 +696,7 @@ String *Item_func_date_format::val_str(String *str) /* Create the result string */ const char *ptr=format->ptr(); const char *end=ptr+format->length(); - for ( ; ptr != end ; ptr++) + for (; ptr != end ; ptr++) { if (*ptr != '%' || ptr+1 == end) str->append(*ptr); @@ -691,7 +704,7 @@ String *Item_func_date_format::val_str(String *str) { switch (*++ptr) { case 'M': - if(!l_time.month) + if (!l_time.month) { null_value=1; return 0; @@ -699,7 +712,7 @@ String *Item_func_date_format::val_str(String *str) str->append(month_names[l_time.month-1]); break; case 'b': - if(!l_time.month) + if (!l_time.month) { null_value=1; return 0; @@ -707,7 +720,7 @@ String *Item_func_date_format::val_str(String *str) str->append(month_names[l_time.month-1].ptr(),3); break; case 'W': - if(date_or_time) + if (date_or_time) { null_value=1; return 0; @@ -716,7 +729,7 @@ String *Item_func_date_format::val_str(String *str) str->append(day_names[weekday]); break; case 'a': - if(date_or_time) + if (date_or_time) { null_value=1; return 0; @@ -725,7 +738,7 @@ String *Item_func_date_format::val_str(String *str) str->append(day_names[weekday].ptr(),3); break; case 'D': - if(date_or_time) + if (date_or_time) { null_value=1; return 0; @@ -791,7 +804,7 @@ String *Item_func_date_format::val_str(String *str) str->append(intbuff); break; case 'j': - if(date_or_time) + if (date_or_time) { null_value=1; return 0; @@ -1123,3 +1136,13 @@ longlong Item_extract::val_int() } return 0; // Impossible } + + +void Item_typecast::print(String *str) +{ + str->append("CAST("); + args[0]->print(str); + str->append(" AS "); + str->append(func_name()); + str->append(')'); +} diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index 720f8ba2882..0ca2a36609d 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -73,6 +73,7 @@ public: void fix_length_and_dec() { decimals=0; max_length=2; maybe_null=1; } }; + class Item_func_monthname :public Item_func_month { public: @@ -175,6 +176,7 @@ public: const char *func_name() const { return "weekday"; } enum Item_result result_type () const { return INT_RESULT; } void fix_length_and_dec() { decimals=0; max_length=1; maybe_null=1; } + unsigned int size_of() { return sizeof(*this);} }; class Item_func_dayname :public Item_func_weekday @@ -200,6 +202,7 @@ public: { decimals=0; max_length=10; } + unsigned int size_of() { return sizeof(*this);} }; @@ -228,7 +231,35 @@ public: double val() { return (double) val_int(); } const char *func_name() const { return "date"; } void fix_length_and_dec() { decimals=0; max_length=10; } - bool save_in_field(Field *to); + bool save_in_field(Field *to, bool no_conversions); + void make_field(Send_field *tmp_field) + { + init_make_field(tmp_field,FIELD_TYPE_DATE); + } + Field *tmp_table_field(TABLE *t_arg) + { + return (!t_arg) ? result_field : new Field_date(maybe_null, name, t_arg); + } + unsigned int size_of() { return sizeof(*this);} +}; + + +class Item_date_func :public Item_str_func +{ +public: + Item_date_func() :Item_str_func() {} + Item_date_func(Item *a) :Item_str_func(a) {} + Item_date_func(Item *a,Item *b) :Item_str_func(a,b) {} + void make_field(Send_field *tmp_field) + { + init_make_field(tmp_field,FIELD_TYPE_DATETIME); + } + Field *tmp_table_field(TABLE *t_arg) + { + return (!t_arg) ? result_field : new Field_datetime(maybe_null, name, + t_arg); + } + unsigned int size_of() { return sizeof(*this);} }; @@ -247,6 +278,15 @@ public: { str_value.set(buff,buff_length); return &str_value; } const char *func_name() const { return "curtime"; } void fix_length_and_dec(); + void make_field(Send_field *tmp_field) + { + init_make_field(tmp_field,FIELD_TYPE_TIME); + } + Field *tmp_table_field(TABLE *t_arg) + { + return (!t_arg) ? result_field : new Field_time(maybe_null, name, t_arg); + } + unsigned int size_of() { return sizeof(*this);} }; @@ -260,27 +300,29 @@ public: const char *func_name() const { return "curdate"; } void fix_length_and_dec(); /* Retrieves curtime */ bool get_date(TIME *res,bool fuzzy_date); + unsigned int size_of() { return sizeof(*this);} }; -class Item_func_now :public Item_func +class Item_func_now :public Item_date_func { longlong value; char buff[20]; uint buff_length; TIME ltime; public: - Item_func_now() :Item_func() {} - Item_func_now(Item *a) :Item_func(a) {} + Item_func_now() :Item_date_func() {} + Item_func_now(Item *a) :Item_date_func(a) {} enum Item_result result_type () const { return STRING_RESULT; } double val() { return (double) value; } longlong val_int() { return value; } - bool save_in_field(Field *to); + bool save_in_field(Field *to, bool no_conversions); String *val_str(String *str) { str_value.set(buff,buff_length); return &str_value; } const char *func_name() const { return "now"; } void fix_length_and_dec(); bool get_date(TIME *res,bool fuzzy_date); + unsigned int size_of() { return sizeof(*this);} }; @@ -305,19 +347,20 @@ public: const char *func_name() const { return "date_format"; } void fix_length_and_dec(); uint format_length(const String *format); + unsigned int size_of() { return sizeof(*this);} }; -class Item_func_from_unixtime :public Item_func +class Item_func_from_unixtime :public Item_date_func { public: - Item_func_from_unixtime(Item *a) :Item_func(a) {} + Item_func_from_unixtime(Item *a) :Item_date_func(a) {} double val() { return (double) Item_func_from_unixtime::val_int(); } longlong val_int(); String *val_str(String *str); const char *func_name() const { return "from_unixtime"; } void fix_length_and_dec() { decimals=0; max_length=19; } - enum Item_result result_type () const { return STRING_RESULT; } +// enum Item_result result_type () const { return STRING_RESULT; } bool get_date(TIME *res,bool fuzzy_date); }; @@ -331,8 +374,17 @@ public: String *val_str(String *); void fix_length_and_dec() { maybe_null=1; max_length=13; } const char *func_name() const { return "sec_to_time"; } + void make_field(Send_field *tmp_field) + { + init_make_field(tmp_field,FIELD_TYPE_TIME); + } + Field *tmp_table_field(TABLE *t_arg) + { + return (!t_arg) ? result_field : new Field_time(maybe_null, name, t_arg); + } }; + enum interval_type { INTERVAL_YEAR, INTERVAL_MONTH, INTERVAL_DAY, INTERVAL_HOUR, INTERVAL_MINUTE, INTERVAL_SECOND, INTERVAL_YEAR_MONTH, INTERVAL_DAY_HOUR, @@ -340,7 +392,7 @@ enum interval_type { INTERVAL_YEAR, INTERVAL_MONTH, INTERVAL_DAY, INTERVAL_HOUR_MINUTE, INTERVAL_HOUR_SECOND, INTERVAL_MINUTE_SECOND}; -class Item_date_add_interval :public Item_str_func +class Item_date_add_interval :public Item_date_func { const interval_type int_type; String value; @@ -348,15 +400,17 @@ class Item_date_add_interval :public Item_str_func public: Item_date_add_interval(Item *a,Item *b,interval_type type_arg,bool neg_arg) - :Item_str_func(a,b),int_type(type_arg), date_sub_interval(neg_arg) {} + :Item_date_func(a,b),int_type(type_arg), date_sub_interval(neg_arg) {} String *val_str(String *); const char *func_name() const { return "date_add_interval"; } void fix_length_and_dec() { maybe_null=1; max_length=19; value.alloc(32);} double val() { return (double) val_int(); } longlong val_int(); bool get_date(TIME *res,bool fuzzy_date); + unsigned int size_of() { return sizeof(*this);} }; + class Item_extract :public Item_int_func { const interval_type int_type; @@ -368,4 +422,74 @@ class Item_extract :public Item_int_func longlong val_int(); const char *func_name() const { return "extract"; } void fix_length_and_dec(); + unsigned int size_of() { return sizeof(*this);} +}; + + +class Item_typecast :public Item_str_func +{ +public: + Item_typecast(Item *a) :Item_str_func(a) {} + const char *func_name() const { return "char"; } + String *val_str(String *a) + { a=args[0]->val_str(a); null_value=args[0]->null_value; return a; } + void fix_length_and_dec() { max_length=args[0]->max_length; } + void print(String *str); +}; + + +class Item_char_typecast :public Item_typecast +{ +public: + Item_char_typecast(Item *a) :Item_typecast(a) {} + void fix_length_and_dec() { binary=0; max_length=args[0]->max_length; } +}; + + +class Item_date_typecast :public Item_typecast +{ +public: + Item_date_typecast(Item *a) :Item_typecast(a) {} + const char *func_name() const { return "date"; } + void make_field(Send_field *tmp_field) + { + init_make_field(tmp_field,FIELD_TYPE_DATE); + } + Field *tmp_table_field(TABLE *t_arg) + { + return (!t_arg) ? result_field : new Field_date(maybe_null, name, t_arg); + } +}; + + +class Item_time_typecast :public Item_typecast +{ +public: + Item_time_typecast(Item *a) :Item_typecast(a) {} + const char *func_name() const { return "time"; } + void make_field(Send_field *tmp_field) + { + init_make_field(tmp_field,FIELD_TYPE_TIME); + } + Field *tmp_table_field(TABLE *t_arg) + { + return (!t_arg) ? result_field : new Field_time(maybe_null, name, t_arg); + } +}; + + +class Item_datetime_typecast :public Item_typecast +{ +public: + Item_datetime_typecast(Item *a) :Item_typecast(a) {} + const char *func_name() const { return "datetime"; } + void make_field(Send_field *tmp_field) + { + init_make_field(tmp_field,FIELD_TYPE_DATETIME); + } + Field *tmp_table_field(TABLE *t_arg) + { + return (!t_arg) ? result_field : new Field_datetime(maybe_null, name, + t_arg); + } }; diff --git a/sql/item_uniq.cc b/sql/item_uniq.cc index 80ed6433fd8..88e0cbbc0e6 100644 --- a/sql/item_uniq.cc +++ b/sql/item_uniq.cc @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ diff --git a/sql/item_uniq.h b/sql/item_uniq.h index ff11222e2ee..cc087832f49 100644 --- a/sql/item_uniq.h +++ b/sql/item_uniq.h @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -29,6 +29,7 @@ public: :Item_real_func(list) {} double val() { return 0.0; } void fix_length_and_dec() { decimals=0; max_length=6; } + unsigned int size_of() { return sizeof(*this);} }; class Item_sum_unique_users :public Item_sum_num @@ -43,4 +44,5 @@ public: void reset_field() {} void update_field(int offset) {} bool fix_fields(THD *thd,struct st_table_list *tlist) { return 0;} + unsigned int size_of() { return sizeof(*this);} }; diff --git a/sql/key.cc b/sql/key.cc index f2488ab74cb..d103c07eb72 100644 --- a/sql/key.cc +++ b/sql/key.cc @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -96,7 +96,7 @@ void key_copy(byte *key,TABLE *table,uint idx,uint key_length) length=min(key_length,key_part->length); set_if_smaller(blob_length,length); int2store(key,(uint) blob_length); - key+=2; // Skipp length info + key+=2; // Skip length info memcpy(key,pos,blob_length); } else @@ -250,7 +250,7 @@ void key_unpack(String *to,TABLE *table,uint idx) bool check_if_key_used(TABLE *table, uint idx, List<Item> &fields) { - List_iterator<Item> f(fields); + List_iterator_fast<Item> f(fields); KEY_PART_INFO *key_part,*key_part_end; for (key_part=table->key_info[idx].key_part,key_part_end=key_part+ table->key_info[idx].key_parts ; @@ -258,7 +258,7 @@ bool check_if_key_used(TABLE *table, uint idx, List<Item> &fields) key_part++) { Item_field *field; - + if (key_part->field == table->timestamp_field) return 1; // Can't be used for update diff --git a/sql/lex.h b/sql/lex.h index 776e03b811b..a9e44e034d0 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -30,9 +30,9 @@ #endif /* -** Symbols are broken into separated arrays to allow fieldnames with -** same name as functions -** These are kept sorted for human lookup (the symbols are hashed) + Symbols are broken into separated arrays to allow field names with + same name as functions. + These are kept sorted for human lookup (the symbols are hashed). */ static SYMBOL symbols[] = { @@ -61,27 +61,33 @@ static SYMBOL symbols[] = { { "AVG", SYM(AVG_SYM),0,0}, { "AVG_ROW_LENGTH", SYM(AVG_ROW_LENGTH),0,0}, { "AUTO_INCREMENT", SYM(AUTO_INC),0,0}, - { "AUTOCOMMIT", SYM(AUTOCOMMIT),0,0}, - { "BACKUP", SYM(BACKUP_SYM),0,0}, - { "BEGIN", SYM(BEGIN_SYM),0,0}, + { "BACKUP", SYM(BACKUP_SYM),0,0}, + { "BEGIN", SYM(BEGIN_SYM),0,0}, { "BERKELEYDB", SYM(BERKELEY_DB_SYM),0,0}, { "BDB", SYM(BERKELEY_DB_SYM),0,0}, { "BETWEEN", SYM(BETWEEN_SYM),0,0}, { "BIGINT", SYM(BIGINT),0,0}, { "BIT", SYM(BIT_SYM),0,0}, { "BINARY", SYM(BINARY),0,0}, + { "BINLOG", SYM(BINLOG_SYM),0,0}, { "BLOB", SYM(BLOB_SYM),0,0}, { "BOOL", SYM(BOOL_SYM),0,0}, + { "BOOLEAN", SYM(BOOLEAN_SYM),0,0}, { "BOTH", SYM(BOTH),0,0}, { "BY", SYM(BY),0,0}, + { "CACHE", SYM(CACHE_SYM),0,0}, { "CASCADE", SYM(CASCADE),0,0}, { "CASE", SYM(CASE_SYM),0,0}, { "CHAR", SYM(CHAR_SYM),0,0}, { "CHARACTER", SYM(CHAR_SYM),0,0}, + { "CHARSET", SYM(CHARSET),0,0}, { "CHANGE", SYM(CHANGE),0,0}, { "CHANGED", SYM(CHANGED),0,0}, { "CHECK", SYM(CHECK_SYM),0,0}, { "CHECKSUM", SYM(CHECKSUM_SYM),0,0}, + { "CIPHER", SYM(CIPHER_SYM),0,0}, + { "CLIENT", SYM(CLIENT_SYM),0,0}, + { "CLOSE", SYM(CLOSE_SYM),0,0}, { "COLUMN", SYM(COLUMN_SYM),0,0}, { "COLUMNS", SYM(COLUMNS),0,0}, { "COMMENT", SYM(COMMENT_SYM),0,0}, @@ -92,6 +98,7 @@ static SYMBOL symbols[] = { { "CONSTRAINT", SYM(CONSTRAINT),0,0}, { "CREATE", SYM(CREATE),0,0}, { "CROSS", SYM(CROSS),0,0}, + { "CUBE", SYM(CUBE_SYM),0,0}, { "CURRENT_DATE", SYM(CURDATE),0,0}, { "CURRENT_TIME", SYM(CURTIME),0,0}, { "CURRENT_TIMESTAMP", SYM(NOW_SYM),0,0}, @@ -106,12 +113,15 @@ static SYMBOL symbols[] = { { "DAY_SECOND", SYM(DAY_SECOND_SYM),0,0}, { "DEC", SYM(DECIMAL_SYM),0,0}, { "DECIMAL", SYM(DECIMAL_SYM),0,0}, + { "DES_KEY_FILE", SYM(DES_KEY_FILE),0,0}, { "DEFAULT", SYM(DEFAULT),0,0}, { "DELAYED", SYM(DELAYED_SYM),0,0}, { "DELAY_KEY_WRITE", SYM(DELAY_KEY_WRITE_SYM),0,0}, { "DELETE", SYM(DELETE_SYM),0,0}, { "DESC", SYM(DESC),0,0}, { "DESCRIBE", SYM(DESCRIBE),0,0}, + { "DIRECTORY", SYM(DIRECTORY_SYM),0,0}, + { "DISABLE", SYM(DISABLE_SYM),0,0}, { "DISTINCT", SYM(DISTINCT),0,0}, { "DISTINCTROW", SYM(DISTINCT),0,0}, /* Access likes this */ { "DO", SYM(DO_SYM),0,0}, @@ -123,8 +133,11 @@ static SYMBOL symbols[] = { { "ELSE", SYM(ELSE),0,0}, { "ESCAPE", SYM(ESCAPE_SYM),0,0}, { "ESCAPED", SYM(ESCAPED),0,0}, + { "ENABLE", SYM(ENABLE_SYM),0,0}, { "ENCLOSED", SYM(ENCLOSED),0,0}, { "ENUM", SYM(ENUM),0,0}, + { "EVENTS", SYM(EVENTS_SYM),0,0}, + { "EXECUTE", SYM(EXECUTE_SYM),0,0}, { "EXPLAIN", SYM(DESCRIBE),0,0}, { "EXISTS", SYM(EXISTS),0,0}, { "EXTENDED", SYM(EXTENDED_SYM),0,0}, @@ -138,6 +151,7 @@ static SYMBOL symbols[] = { { "FLOAT8", SYM(DOUBLE_SYM),0,0}, { "FLUSH", SYM(FLUSH_SYM),0,0}, { "FOREIGN", SYM(FOREIGN),0,0}, + { "FORCE", SYM(FORCE_SYM),0,0}, { "RAID_TYPE", SYM(RAID_TYPE),0,0}, { "RAID_CHUNKS", SYM(RAID_CHUNKS),0,0}, { "RAID_CHUNKSIZE", SYM(RAID_CHUNKSIZE),0,0}, @@ -147,13 +161,12 @@ static SYMBOL symbols[] = { { "FULL", SYM(FULL),0,0}, { "FULLTEXT", SYM(FULLTEXT_SYM),0,0}, { "FUNCTION", SYM(UDF_SYM),0,0}, - { "GEMINI", SYM(GEMINI_SYM),0,0}, - { "GEMINI_SPIN_RETRIES", SYM(GEMINI_SPIN_RETRIES),0,0}, { "GLOBAL", SYM(GLOBAL_SYM),0,0}, { "GRANT", SYM(GRANT),0,0}, { "GRANTS", SYM(GRANTS),0,0}, { "GROUP", SYM(GROUP),0,0}, { "HAVING", SYM(HAVING),0,0}, + { "HANDLER", SYM(HANDLER_SYM),0,0}, { "HEAP", SYM(HEAP_SYM),0,0}, { "HIGH_PRIORITY", SYM(HIGH_PRIORITY),0,0}, { "HOUR", SYM(HOUR_SYM),0,0}, @@ -164,12 +177,13 @@ static SYMBOL symbols[] = { { "IGNORE", SYM(IGNORE_SYM),0,0}, { "IN", SYM(IN_SYM),0,0}, { "INDEX", SYM(INDEX),0,0}, + { "INDEXES", SYM(INDEXES),0,0}, { "INFILE", SYM(INFILE),0,0}, { "INNER", SYM(INNER_SYM),0,0}, { "INNOBASE", SYM(INNOBASE_SYM),0,0}, { "INNODB", SYM(INNOBASE_SYM),0,0}, { "INSERT", SYM(INSERT),0,0}, - { "INSERT_ID", SYM(INSERT_ID),0,0}, + { "INSERT_METHOD", SYM(INSERT_METHOD),0,0}, { "INT", SYM(INT_SYM),0,0}, { "INTEGER", SYM(INT_SYM),0,0}, { "INTERVAL", SYM(INTERVAL_SYM),0,0}, @@ -179,15 +193,17 @@ static SYMBOL symbols[] = { { "INT4", SYM(INT_SYM),0,0}, { "INT8", SYM(BIGINT),0,0}, { "INTO", SYM(INTO),0,0}, + { "IO_THREAD", SYM(IO_THREAD),0,0}, { "IF", SYM(IF),0,0}, { "IS", SYM(IS),0,0}, { "ISOLATION", SYM(ISOLATION),0,0}, { "ISAM", SYM(ISAM_SYM),0,0}, + { "ISSUER", SYM(ISSUER_SYM),0,0}, { "JOIN", SYM(JOIN_SYM),0,0}, { "KEY", SYM(KEY_SYM),0,0}, { "KEYS", SYM(KEYS),0,0}, { "KILL", SYM(KILL_SYM),0,0}, - { "LAST_INSERT_ID", SYM(LAST_INSERT_ID),0,0}, + { "LAST", SYM(LAST_SYM),0,0}, { "LEADING", SYM(LEADING),0,0}, { "LEFT", SYM(LEFT),0,0}, { "LEVEL", SYM(LEVEL_SYM),0,0}, @@ -196,6 +212,8 @@ static SYMBOL symbols[] = { { "LIMIT", SYM(LIMIT),0,0}, { "LOAD", SYM(LOAD),0,0}, { "LOCAL", SYM(LOCAL_SYM),0,0}, + { "LOCALTIME", SYM(NOW_SYM),0,0}, + { "LOCALTIMESTAMP", SYM(NOW_SYM),0,0}, { "LOCK", SYM(LOCK_SYM),0,0}, { "LOCKS", SYM(LOCKS_SYM),0,0}, { "LOGS", SYM(LOGS_SYM),0,0}, @@ -210,8 +228,12 @@ static SYMBOL symbols[] = { { "MASTER_LOG_POS", SYM(MASTER_LOG_POS_SYM),0,0}, { "MASTER_PASSWORD", SYM(MASTER_PASSWORD_SYM),0,0}, { "MASTER_PORT", SYM(MASTER_PORT_SYM),0,0}, + { "MASTER_SERVER_ID", SYM(MASTER_SERVER_ID_SYM),0,0}, { "MASTER_USER", SYM(MASTER_USER_SYM),0,0}, { "MAX_ROWS", SYM(MAX_ROWS),0,0}, + { "MAX_QUERIES_PER_HOUR", SYM(MAX_QUERIES_PER_HOUR), 0,0}, + { "MAX_UPDATES_PER_HOUR", SYM(MAX_UPDATES_PER_HOUR), 0,0}, + { "MAX_CONNECTIONS_PER_HOUR", SYM(MAX_CONNECTIONS_PER_HOUR), 0,0}, { "MATCH", SYM(MATCH),0,0}, { "MEDIUMBLOB", SYM(MEDIUMBLOB),0,0}, { "MEDIUMTEXT", SYM(MEDIUMTEXT),0,0}, @@ -229,12 +251,15 @@ static SYMBOL symbols[] = { { "MYISAM", SYM(MYISAM_SYM),0,0}, { "NATURAL", SYM(NATURAL),0,0}, { "NATIONAL", SYM(NATIONAL_SYM),0,0}, + { "NEXT", SYM(NEXT_SYM),0,0}, + { "NEW", SYM(NEW_SYM),0,0}, { "NCHAR", SYM(NCHAR_SYM),0,0}, - { "NUMERIC", SYM(NUMERIC_SYM),0,0}, { "NO", SYM(NO_SYM),0,0}, - { "FOREIGN_KEY_CHECKS", SYM(FOREIGN_KEY_CHECKS), 0, 0}, + { "NONE", SYM(NONE_SYM),0,0}, { "NOT", SYM(NOT),0,0}, { "NULL", SYM(NULL_SYM),0,0}, + { "NUMERIC", SYM(NUMERIC_SYM),0,0}, + { "OFFSET", SYM(OFFSET_SYM),0,0}, { "ON", SYM(ON),0,0}, { "OPEN", SYM(OPEN_SYM),0,0}, { "OPTIMIZE", SYM(OPTIMIZE),0,0}, @@ -249,24 +274,30 @@ static SYMBOL symbols[] = { { "PASSWORD", SYM(PASSWORD),0,0}, { "PURGE", SYM(PURGE),0,0}, { "PRECISION", SYM(PRECISION),0,0}, + { "PREV", SYM(PREV_SYM),0,0}, { "PRIMARY", SYM(PRIMARY_SYM),0,0}, { "PROCEDURE", SYM(PROCEDURE),0,0}, { "PROCESS" , SYM(PROCESS),0,0}, { "PROCESSLIST", SYM(PROCESSLIST_SYM),0,0}, { "PRIVILEGES", SYM(PRIVILEGES),0,0}, + { "QUERY", SYM(QUERY_SYM),0,0}, { "QUICK", SYM(QUICK),0,0}, { "RAID0", SYM(RAID_0_SYM),0,0}, { "READ", SYM(READ_SYM),0,0}, { "REAL", SYM(REAL),0,0}, { "REFERENCES", SYM(REFERENCES),0,0}, + { "RELAY_LOG_FILE", SYM(RELAY_LOG_FILE_SYM),0,0}, + { "RELAY_LOG_POS", SYM(RELAY_LOG_POS_SYM),0,0}, { "RELOAD", SYM(RELOAD),0,0}, { "REGEXP", SYM(REGEXP),0,0}, - { "UNIQUE_CHECKS", SYM(UNIQUE_CHECKS), 0, 0}, { "RENAME", SYM(RENAME),0,0}, { "REPAIR", SYM(REPAIR),0,0}, { "REPLACE", SYM(REPLACE),0,0}, + { "REPLICATION", SYM(REPLICATION),0,0}, { "REPEATABLE", SYM(REPEATABLE_SYM),0,0}, + { "REQUIRE", SYM(REQUIRE_SYM),0,0}, { "RESET", SYM(RESET_SYM),0,0}, + { "USER_RESOURCES", SYM(RESOURCES),0,0}, { "RESTORE", SYM(RESTORE_SYM),0,0}, { "RESTRICT", SYM(RESTRICT),0,0}, { "RETURNS", SYM(UDF_RETURNS_SYM),0,0}, @@ -274,6 +305,7 @@ static SYMBOL symbols[] = { { "RIGHT", SYM(RIGHT),0,0}, { "RLIKE", SYM(REGEXP),0,0}, /* Like in mSQL2 */ { "ROLLBACK", SYM(ROLLBACK_SYM),0,0}, + { "ROLLUP", SYM(ROLLUP_SYM),0,0}, { "ROW", SYM(ROW_SYM),0,0}, { "ROWS", SYM(ROWS_SYM),0,0}, { "SECOND", SYM(SECOND_SYM),0,0}, @@ -281,28 +313,21 @@ static SYMBOL symbols[] = { { "SERIALIZABLE", SYM(SERIALIZABLE_SYM),0,0}, { "SESSION", SYM(SESSION_SYM),0,0}, { "SET", SYM(SET),0,0}, + { "SIGNED", SYM(SIGNED_SYM),0,0}, { "SHARE", SYM(SHARE_SYM),0,0}, { "SHOW", SYM(SHOW),0,0}, { "SHUTDOWN", SYM(SHUTDOWN),0,0}, { "SLAVE", SYM(SLAVE),0,0}, { "SMALLINT", SYM(SMALLINT),0,0}, { "SONAME", SYM(UDF_SONAME_SYM),0,0}, - { "SQL_AUTO_IS_NULL", SYM(SQL_AUTO_IS_NULL),0,0}, { "SQL_BIG_RESULT", SYM(SQL_BIG_RESULT),0,0}, - { "SQL_BIG_SELECTS", SYM(SQL_BIG_SELECTS),0,0}, - { "SQL_BIG_TABLES", SYM(SQL_BIG_TABLES),0,0}, { "SQL_BUFFER_RESULT", SYM(SQL_BUFFER_RESULT),0,0}, - { "SQL_LOG_BIN", SYM(SQL_LOG_BIN),0,0}, - { "SQL_LOG_OFF", SYM(SQL_LOG_OFF),0,0}, - { "SQL_LOG_UPDATE", SYM(SQL_LOG_UPDATE),0,0}, - { "SQL_LOW_PRIORITY_UPDATES", SYM(SQL_LOW_PRIORITY_UPDATES),0,0}, - { "SQL_MAX_JOIN_SIZE",SYM(SQL_MAX_JOIN_SIZE), 0, 0}, - { "SQL_QUOTE_SHOW_CREATE",SYM(SQL_QUOTE_SHOW_CREATE), 0, 0}, - { "SQL_SAFE_UPDATES", SYM(SQL_SAFE_UPDATES),0,0}, - { "SQL_SELECT_LIMIT", SYM(SQL_SELECT_LIMIT),0,0}, - { "SQL_SLAVE_SKIP_COUNTER", SYM(SQL_SLAVE_SKIP_COUNTER),0,0}, + { "SQL_CACHE", SYM(SQL_CACHE_SYM), 0, 0}, + { "SQL_CALC_FOUND_ROWS", SYM(SQL_CALC_FOUND_ROWS),0,0}, + { "SQL_NO_CACHE", SYM(SQL_NO_CACHE_SYM), 0, 0}, { "SQL_SMALL_RESULT", SYM(SQL_SMALL_RESULT),0,0}, - { "SQL_WARNINGS", SYM(SQL_WARNINGS),0,0}, + { "SQL_THREAD", SYM(SQL_THREAD),0,0}, + { "SSL", SYM(SSL_SYM),0,0}, { "STRAIGHT_JOIN", SYM(STRAIGHT_JOIN),0,0}, { "START", SYM(START_SYM),0,0}, { "STARTING", SYM(STARTING),0,0}, @@ -310,6 +335,8 @@ static SYMBOL symbols[] = { { "STRING", SYM(STRING_SYM),0,0}, { "STOP", SYM(STOP_SYM),0,0}, { "STRIPED", SYM(RAID_STRIPED_SYM),0,0}, + { "SUBJECT", SYM(SUBJECT_SYM),0,0}, + { "SUPER", SYM(SUPER_SYM),0,0}, { "TABLE", SYM(TABLE_SYM),0,0}, { "TABLES", SYM(TABLES),0,0}, { "TEMPORARY", SYM(TEMPORARY),0,0}, @@ -332,6 +359,7 @@ static SYMBOL symbols[] = { { "UNLOCK", SYM(UNLOCK_SYM),0,0}, { "UNSIGNED", SYM(UNSIGNED),0,0}, { "USE", SYM(USE_SYM),0,0}, + { "USE_FRM", SYM(USE_FRM),0,0}, { "USING", SYM(USING),0,0}, { "UPDATE", SYM(UPDATE_SYM),0,0}, { "USAGE", SYM(USAGE),0,0}, @@ -345,6 +373,8 @@ static SYMBOL symbols[] = { { "WRITE", SYM(WRITE_SYM),0,0}, { "WHEN", SYM(WHEN_SYM),0,0}, { "WHERE", SYM(WHERE),0,0}, + { "XOR", SYM(XOR),0,0}, + { "X509", SYM(X509_SYM),0,0}, { "YEAR", SYM(YEAR_SYM),0,0}, { "YEAR_MONTH", SYM(YEAR_MONTH_SYM),0,0}, { "ZEROFILL", SYM(ZEROFILL),0,0}, @@ -356,6 +386,8 @@ static SYMBOL sql_functions[] = { { "ABS", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_abs)}, { "ACOS", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_acos)}, { "ADDDATE", SYM(DATE_ADD_INTERVAL),0,0}, + { "AES_ENCRYPT", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_aes_encrypt)}, + { "AES_DECRYPT", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_aes_decrypt)}, { "ASCII", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ascii)}, { "ASIN", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_asin)}, { "ATAN", SYM(ATAN),0,0}, @@ -365,7 +397,11 @@ static SYMBOL sql_functions[] = { { "BIT_COUNT", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_bit_count)}, { "BIT_OR", SYM(BIT_OR),0,0}, { "BIT_AND", SYM(BIT_AND),0,0}, + { "CAST", SYM(CAST_SYM),0,0}, + { "CEIL", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ceiling)}, { "CEILING", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ceiling)}, + { "CURRENT_USER", SYM(FUNC_ARG0),0,CREATE_FUNC(create_func_current_user)}, + { "BIT_LENGTH", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_bit_length)}, { "CHAR_LENGTH", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_char_length)}, { "CHARACTER_LENGTH", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_char_length)}, { "COALESCE", SYM(COALESCE),0,0}, @@ -373,6 +409,7 @@ static SYMBOL sql_functions[] = { { "CONCAT_WS", SYM(CONCAT_WS),0,0}, { "CONNECTION_ID", SYM(FUNC_ARG0),0,CREATE_FUNC(create_func_connection_id)}, { "CONV", SYM(FUNC_ARG3),0,CREATE_FUNC(create_func_conv)}, + { "CONVERT", SYM(CONVERT_SYM),0,0}, { "COUNT", SYM(COUNT_SYM),0,0}, { "COS", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_cos)}, { "COT", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_cot)}, @@ -387,6 +424,8 @@ static SYMBOL sql_functions[] = { { "DAYOFYEAR", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_dayofyear)}, { "DECODE", SYM(DECODE_SYM),0,0}, { "DEGREES", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_degrees)}, + { "DES_ENCRYPT", SYM(DES_ENCRYPT_SYM),0,0}, + { "DES_DECRYPT", SYM(DES_DECRYPT_SYM),0,0}, { "ELT", SYM(ELT_FUNC),0,0}, { "ENCODE", SYM(ENCODE_SYM),0,0}, { "ENCRYPT", SYM(ENCRYPT),0,0}, @@ -397,6 +436,7 @@ static SYMBOL sql_functions[] = { { "FIND_IN_SET", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_find_in_set)}, { "FLOOR", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_floor)}, { "FORMAT", SYM(FORMAT_SYM),0,0}, + { "FOUND_ROWS", SYM(FUNC_ARG0),0,CREATE_FUNC(create_func_found_rows)}, { "FROM_DAYS", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_from_days)}, { "FROM_UNIXTIME", SYM(FROM_UNIXTIME),0,0}, { "GET_LOCK", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_get_lock)}, @@ -408,19 +448,22 @@ static SYMBOL sql_functions[] = { { "INET_NTOA", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_inet_ntoa)}, { "INSTR", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_instr)}, { "ISNULL", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_isnull)}, + { "IS_FREE_LOCK", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_is_free_lock)}, + { "LAST_INSERT_ID", SYM(LAST_INSERT_ID),0,0}, { "LCASE", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_lcase)}, { "LEAST", SYM(LEAST_SYM),0,0}, { "LENGTH", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_length)}, + { "LN", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ln)}, { "LOAD_FILE", SYM(FUNC_ARG1),0,CREATE_FUNC(create_load_file)}, { "LOCATE", SYM(LOCATE),0,0}, - { "LOG", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_log)}, + { "LOG", SYM(LOG_SYM),0,0}, + { "LOG2", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_log2)}, { "LOG10", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_log10)}, { "LOWER", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_lcase)}, { "LPAD", SYM(FUNC_ARG3),0,CREATE_FUNC(create_func_lpad)}, { "LTRIM", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ltrim)}, - { "MASTER_POS_WAIT", SYM(FUNC_ARG2),0, - CREATE_FUNC(create_wait_for_master_pos)}, { "MAKE_SET", SYM(MAKE_SET_SYM),0,0}, + { "MASTER_POS_WAIT", SYM(MASTER_POS_WAIT),0,0}, { "MAX", SYM(MAX_SYM),0,0}, { "MD5", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_md5)}, { "MID", SYM(SUBSTRING),0,0}, /* unireg function */ @@ -431,6 +474,7 @@ static SYMBOL sql_functions[] = { { "NULLIF", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_nullif)}, { "OCTET_LENGTH", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_length)}, { "OCT", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_oct)}, + { "OLD_PASSWORD", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_password)}, { "ORD", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ord)}, { "PERIOD_ADD", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_period_add)}, { "PERIOD_DIFF", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_period_diff)}, @@ -439,6 +483,7 @@ static SYMBOL sql_functions[] = { { "POW", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_pow)}, { "POWER", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_pow)}, { "QUARTER", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_quarter)}, + { "QUOTE", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_quote)}, { "RADIANS", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_radians)}, { "RAND", SYM(RAND),0,0}, { "RELEASE_LOCK", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_release_lock)}, @@ -452,6 +497,8 @@ static SYMBOL sql_functions[] = { { "SUBDATE", SYM(DATE_SUB_INTERVAL),0,0}, { "SIGN", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_sign)}, { "SIN", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_sin)}, + { "SHA", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_sha)}, + { "SHA1", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_sha)}, { "SOUNDEX", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_soundex)}, { "SPACE", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_space)}, { "SQRT", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_sqrt)}, diff --git a/sql/lex_symbol.h b/sql/lex_symbol.h index a011e27b59e..9fff1751b1b 100644 --- a/sql/lex_symbol.h +++ b/sql/lex_symbol.h @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ diff --git a/sql/lock.cc b/sql/lock.cc index e46e2aac7bc..41a76007289 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -21,6 +21,46 @@ before getting internal locks. If we do it in the other order, the status information is not up to date when called from the lock handler. + GENERAL DESCRIPTION OF LOCKING + + When not using LOCK TABLES: + + - For each SQL statement mysql_lock_tables() is called for all involved + tables. + - mysql_lock_tables() will call + table_handler->external_lock(thd,locktype) for each table. + This is followed by a call to thr_multi_lock() for all tables. + + - When statement is done, we call mysql_unlock_tables(). + This will call thr_multi_unlock() followed by + table_handler->external_lock(thd, F_UNLCK) for each table. + + - Note that mysql_unlock_tables() may be called several times as + MySQL in some cases can free some tables earlier than others. + + - The above is true both for normal and temporary tables. + + - Temporary non transactional tables are never passed to thr_multi_lock() + and we never call external_lock(thd, F_UNLOCK) on these. + + When using LOCK TABLES: + + - LOCK TABLE will call mysql_lock_tables() for all tables. + mysql_lock_tables() will call + table_handler->external_lock(thd,locktype) for each table. + This is followed by a call to thr_multi_lock() for all tables. + + - For each statement, we will call table_handler->start_stmt(THD) + to inform the table handler that we are using the table. + + The tables used can only be tables used in LOCK TABLES or a + temporary table. + + - When statement is done, we will call ha_commit_stmt(thd); + + - When calling UNLOCK TABLES we call mysql_unlock_tables() for all + tables used in LOCK TABLES + TODO: Change to use my_malloc() ONLY when using LOCK TABLES command or when we are forced to use mysql_lock_merge. @@ -28,12 +68,13 @@ TODO: #include "mysql_priv.h" #include <hash.h> +#include <assert.h> extern HASH open_cache; 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 lock_external(THD *thd, TABLE **table,uint count); static int unlock_external(THD *thd, TABLE **table,uint count); static void print_lock_error(int error); @@ -55,33 +96,13 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd,TABLE **tables,uint count) Someone has issued LOCK ALL TABLES FOR READ and we want a write lock Wait until the lock is gone */ - if (thd->global_read_lock) // This thread had the read locks + if (wait_if_global_read_lock(thd, 1)) { - my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE,MYF(0), - write_lock_used->table_name); my_free((gptr) sql_lock,MYF(0)); sql_lock=0; break; } - - pthread_mutex_lock(&LOCK_open); - thd->mysys_var->current_mutex= &LOCK_open; - thd->mysys_var->current_cond= &COND_refresh; - thd->proc_info="Waiting for table"; - - while (global_read_lock && ! thd->killed && - thd->version == refresh_version) - { - (void) pthread_cond_wait(&COND_refresh,&LOCK_open); - } - pthread_mutex_unlock(&LOCK_open); - pthread_mutex_lock(&thd->mysys_var->mutex); - thd->mysys_var->current_mutex= 0; - thd->mysys_var->current_cond= 0; - thd->proc_info= 0; - pthread_mutex_unlock(&thd->mysys_var->mutex); - - if (thd->version != refresh_version || thd->killed) + if (thd->version != refresh_version) { my_free((gptr) sql_lock,MYF(0)); goto retry; @@ -89,14 +110,14 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd,TABLE **tables,uint count) } thd->proc_info="System lock"; - if (lock_external(tables,count)) + if (lock_external(thd, tables, count)) { my_free((gptr) sql_lock,MYF(0)); sql_lock=0; thd->proc_info=0; break; } - thd->proc_info=0; + thd->proc_info="Table lock"; thd->locked=1; if (thr_multi_lock(sql_lock->locks,sql_lock->lock_count)) { @@ -115,6 +136,7 @@ MYSQL_LOCK *mysql_lock_tables(THD *thd,TABLE **tables,uint count) thd->locked=0; break; } + thd->proc_info=0; /* some table was altered or deleted. reopen tables marked deleted */ mysql_unlock_tables(thd,sql_lock); @@ -124,6 +146,7 @@ retry: if (wait_for_tables(thd)) break; // Couldn't open tables } + thd->proc_info=0; if (thd->killed) { my_error(ER_SERVER_SHUTDOWN,MYF(0)); @@ -138,15 +161,15 @@ retry: } -static int lock_external(TABLE **tables,uint count) +static int lock_external(THD *thd, TABLE **tables, uint count) { reg1 uint i; int lock_type,error; - THD *thd=current_thd; DBUG_ENTER("lock_external"); for (i=1 ; i <= count ; i++, tables++) { + DBUG_ASSERT((*tables)->reginfo.lock_type >= TL_READ); lock_type=F_WRLCK; /* Lock exclusive */ if ((*tables)->db_stat & HA_READ_ONLY || ((*tables)->reginfo.lock_type >= TL_READ && @@ -155,7 +178,7 @@ static int lock_external(TABLE **tables,uint count) if ((error=(*tables)->file->external_lock(thd,lock_type))) { - for ( ; i-- ; tables--) + for (; i-- ; tables--) { (*tables)->file->external_lock(thd, F_UNLCK); (*tables)->current_lock=F_UNLCK; @@ -225,7 +248,7 @@ void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock) sql_lock->lock_count= found; } - /* Then to the same for the external locks */ + /* Then do the same for the external locks */ /* Move all write locked tables first */ TABLE **table=sql_lock->table; for (i=found=0 ; i < sql_lock->table_count ; i++) @@ -290,6 +313,25 @@ void mysql_lock_abort(THD *thd, TABLE *table) } +/* Abort one thread / table combination */ + +void mysql_lock_abort_for_thread(THD *thd, TABLE *table) +{ + MYSQL_LOCK *locked; + TABLE *write_lock_used; + DBUG_ENTER("mysql_lock_abort_for_thread"); + + if ((locked = get_lock_data(thd,&table,1,1,&write_lock_used))) + { + for (uint i=0; i < locked->lock_count; i++) + thr_abort_locks_for_thread(locked->locks[i]->lock, + table->in_use->real_id); + my_free((gptr) locked,MYF(0)); + } + DBUG_VOID_RETURN; +} + + MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b) { MYSQL_LOCK *sql_lock; @@ -401,6 +443,36 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, *****************************************************************************/ /* + Lock and wait for the named lock. + Returns 0 on ok +*/ + +int lock_and_wait_for_table_name(THD *thd, TABLE_LIST *table_list) +{ + int lock_retcode; + int error= -1; + DBUG_ENTER("lock_and_wait_for_table_name"); + + if (wait_if_global_read_lock(thd,0)) + DBUG_RETURN(1); + VOID(pthread_mutex_lock(&LOCK_open)); + if ((lock_retcode = lock_table_name(thd, table_list)) < 0) + goto end; + if (lock_retcode && wait_for_locked_table_names(thd, table_list)) + { + unlock_table_name(thd, table_list); + goto end; + } + error=0; + +end: + pthread_mutex_unlock(&LOCK_open); + start_waiting_global_read_lock(thd); + DBUG_RETURN(error); +} + + +/* Put a not open table with an old refresh version in the table cache. This will force any other threads that uses the table to release it as soon as possible. @@ -411,7 +483,6 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, > 0 table locked, but someone is using it */ - int lock_table_name(THD *thd, TABLE_LIST *table_list) { TABLE *table; @@ -419,6 +490,7 @@ int lock_table_name(THD *thd, TABLE_LIST *table_list) char *db= table_list->db ? table_list->db : (thd->db ? thd->db : (char*) ""); uint key_length; DBUG_ENTER("lock_table_name"); + safe_mutex_assert_owner(&LOCK_open); key_length=(uint) (strmov(strmov(key,db)+1,table_list->real_name) -key)+ 1; @@ -430,10 +502,11 @@ int lock_table_name(THD *thd, TABLE_LIST *table_list) if (table->in_use == thd) DBUG_RETURN(0); - /* Create a table entry with the right key and with an old refresh version */ - /* Note that we must use my_malloc() here as this is freed by the table - cache */ - + /* + Create a table entry with the right key and with an old refresh version + Note that we must use my_malloc() here as this is freed by the table + cache + */ if (!(table= (TABLE*) my_malloc(sizeof(*table)+key_length, MYF(MY_WME | MY_ZEROFILL)))) DBUG_RETURN(-1); @@ -464,7 +537,7 @@ void unlock_table_name(THD *thd, TABLE_LIST *table_list) static bool locked_named_table(THD *thd, TABLE_LIST *table_list) { - for ( ; table_list ; table_list=table_list->next) + for (; table_list ; table_list=table_list->next) { if (table_list->table && table_is_used(table_list->table,0)) return 1; @@ -477,6 +550,7 @@ bool wait_for_locked_table_names(THD *thd, TABLE_LIST *table_list) { bool result=0; DBUG_ENTER("wait_for_locked_table_names"); + safe_mutex_assert_owner(&LOCK_open); while (locked_named_table(thd,table_list)) { @@ -582,3 +656,102 @@ static void print_lock_error(int error) DBUG_VOID_RETURN; } + +/**************************************************************************** + Handling of global read locks + + The global locks are handled through the global variables: + global_read_lock + waiting_for_read_lock + protect_against_global_read_lock +****************************************************************************/ + +volatile uint global_read_lock=0; +static volatile uint protect_against_global_read_lock=0; +static volatile uint waiting_for_read_lock=0; + +bool lock_global_read_lock(THD *thd) +{ + DBUG_ENTER("lock_global_read_lock"); + + if (!thd->global_read_lock) + { + (void) pthread_mutex_lock(&LOCK_open); + const char *old_message=thd->enter_cond(&COND_refresh, &LOCK_open, + "Waiting to get readlock"); + DBUG_PRINT("info", + ("waiting_for: %d protect_against: %d", + waiting_for_read_lock, protect_against_global_read_lock)); + + waiting_for_read_lock++; + while (protect_against_global_read_lock && !thd->killed) + pthread_cond_wait(&COND_refresh, &LOCK_open); + waiting_for_read_lock--; + thd->exit_cond(old_message); + if (thd->killed) + { + (void) pthread_mutex_unlock(&LOCK_open); + DBUG_RETURN(1); + } + thd->global_read_lock=1; + global_read_lock++; + (void) pthread_mutex_unlock(&LOCK_open); + } + DBUG_RETURN(0); +} + +void unlock_global_read_lock(THD *thd) +{ + uint tmp; + thd->global_read_lock=0; + pthread_mutex_lock(&LOCK_open); + tmp= --global_read_lock; + pthread_mutex_unlock(&LOCK_open); + /* Send the signal outside the mutex to avoid a context switch */ + if (!tmp) + pthread_cond_broadcast(&COND_refresh); +} + + +bool wait_if_global_read_lock(THD *thd, bool abort_on_refresh) +{ + const char *old_message; + bool result=0; + DBUG_ENTER("wait_if_global_read_lock"); + + (void) pthread_mutex_lock(&LOCK_open); + if (global_read_lock) + { + if (thd->global_read_lock) // This thread had the read locks + { + my_error(ER_CANT_UPDATE_WITH_READLOCK,MYF(0)); + (void) pthread_mutex_unlock(&LOCK_open); + DBUG_RETURN(1); + } + old_message=thd->enter_cond(&COND_refresh, &LOCK_open, + "Waiting for release of readlock"); + while (global_read_lock && ! thd->killed && + (!abort_on_refresh || thd->version == refresh_version)) + (void) pthread_cond_wait(&COND_refresh,&LOCK_open); + if (thd->killed) + result=1; + thd->exit_cond(old_message); + } + if (!abort_on_refresh && !result) + protect_against_global_read_lock++; + pthread_mutex_unlock(&LOCK_open); + DBUG_RETURN(result); +} + + +void start_waiting_global_read_lock(THD *thd) +{ + bool tmp; + DBUG_ENTER("start_waiting_global_read_lock"); + (void) pthread_mutex_lock(&LOCK_open); + tmp= (!--protect_against_global_read_lock && waiting_for_read_lock); + (void) pthread_mutex_unlock(&LOCK_open); + if (tmp) + pthread_cond_broadcast(&COND_refresh); + DBUG_VOID_RETURN; +} diff --git a/sql/log.cc b/sql/log.cc index 5e5d5b9368e..073b7f691e8 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -29,10 +29,10 @@ #include <my_dir.h> #include <stdarg.h> #include <m_ctype.h> // For test_if_number +#include <assert.h> MYSQL_LOG mysql_log,mysql_update_log,mysql_slow_log,mysql_bin_log; extern I_List<i_string> binlog_do_db, binlog_ignore_db; -extern ulong max_binlog_size; static bool test_if_number(const char *str, long *res, bool allow_wildcards); @@ -79,9 +79,11 @@ static int find_uniq_filename(char *name) DBUG_RETURN(0); } -MYSQL_LOG::MYSQL_LOG(): last_time(0), query_start(0),index_file(-1), - name(0), log_type(LOG_CLOSED),write_error(0), - inited(0), no_rotate(0) + +MYSQL_LOG::MYSQL_LOG() + :bytes_written(0), last_time(0), query_start(0), name(0), + file_id(1), open_count(1), log_type(LOG_CLOSED), write_error(0), inited(0), + no_rotate(0), need_start_event(1) { /* We don't want to intialize LOCK_Log here as the thread system may @@ -89,34 +91,33 @@ MYSQL_LOG::MYSQL_LOG(): last_time(0), query_start(0),index_file(-1), */ index_file_name[0] = 0; bzero((char*) &log_file,sizeof(log_file)); + bzero((char*) &index_file, sizeof(index_file)); } + MYSQL_LOG::~MYSQL_LOG() { + cleanup(); +} + +void MYSQL_LOG::cleanup() +{ if (inited) { + close(1); + inited= 0; (void) pthread_mutex_destroy(&LOCK_log); (void) pthread_mutex_destroy(&LOCK_index); + (void) pthread_cond_destroy(&update_cond); } } -void MYSQL_LOG::set_index_file_name(const char* index_file_name) -{ - if (index_file_name) - fn_format(this->index_file_name,index_file_name,mysql_data_home,".index", - 4); - else - this->index_file_name[0] = 0; -} - int MYSQL_LOG::generate_new_name(char *new_name, const char *log_name) { - if (log_type == LOG_NORMAL) - fn_format(new_name,log_name,mysql_data_home,"",4); - else + fn_format(new_name,log_name,mysql_data_home,"",4); + if (log_type != LOG_NORMAL) { - fn_format(new_name,log_name,mysql_data_home,"",4); if (!fn_ext(log_name)[0]) { if (find_uniq_filename(new_name)) @@ -129,44 +130,55 @@ int MYSQL_LOG::generate_new_name(char *new_name, const char *log_name) return 0; } -bool MYSQL_LOG::open_index( int options) -{ - return (index_file < 0 && - (index_file = my_open(index_file_name, options | O_BINARY , - MYF(MY_WME))) < 0); -} -void MYSQL_LOG::init(enum_log_type log_type_arg) +void MYSQL_LOG::init(enum_log_type log_type_arg, + enum cache_type io_cache_type_arg, + bool no_auto_events_arg) { log_type = log_type_arg; + io_cache_type = io_cache_type_arg; + no_auto_events = no_auto_events_arg; if (!inited) { - inited=1; + inited= 1; (void) pthread_mutex_init(&LOCK_log,MY_MUTEX_INIT_SLOW); (void) pthread_mutex_init(&LOCK_index, MY_MUTEX_INIT_SLOW); + (void) pthread_cond_init(&update_cond, 0); } } -void MYSQL_LOG::close_index() -{ - if (index_file >= 0) - { - my_close(index_file, MYF(0)); - index_file = -1; - } -} -void MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg, - const char *new_name) +/* + Open a (new) log file. + + DESCRIPTION + - If binary logs, also open the index file and register the new + file name in it + - When calling this when the file is in use, you must have a locks + on LOCK_log and LOCK_index. + + RETURN VALUES + 0 ok + 1 error +*/ + +bool MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg, + const char *new_name, const char *index_file_name_arg, + enum cache_type io_cache_type_arg, + bool no_auto_events_arg) { - MY_STAT tmp_stat; char buff[512]; - File file= -1; - bool do_magic; - + File file= -1, index_file_nr= -1; + int open_flags = O_CREAT | O_APPEND | O_BINARY; + DBUG_ENTER("MYSQL_LOG::open"); + DBUG_PRINT("enter",("log_type: %d",(int) log_type)); + + last_time=query_start=0; + write_error=0; + if (!inited && log_type_arg == LOG_BIN && *fn_ext(log_name)) - no_rotate = 1; - init(log_type_arg); + no_rotate = 1; + init(log_type_arg,io_cache_type_arg,no_auto_events_arg); if (!(name=my_strdup(log_name,MYF(MY_WME)))) goto err; @@ -174,21 +186,22 @@ void MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg, strmov(log_file_name,new_name); else if (generate_new_name(log_file_name, name)) goto err; - - if (log_type == LOG_BIN && !index_file_name[0]) - fn_format(index_file_name, name, mysql_data_home, ".index", 6); - db[0]=0; - do_magic = ((log_type == LOG_BIN) && !my_stat(log_file_name, - &tmp_stat, MYF(0))); + if (io_cache_type == SEQ_READ_APPEND) + open_flags |= O_RDWR; + else + open_flags |= O_WRONLY; - if ((file=my_open(log_file_name,O_CREAT | O_APPEND | O_WRONLY | O_BINARY, + db[0]=0; + open_count++; + if ((file=my_open(log_file_name,open_flags, MYF(MY_WME | ME_WAITTANG))) < 0 || - init_io_cache(&log_file, file, IO_SIZE, WRITE_CACHE, + init_io_cache(&log_file, file, IO_SIZE, io_cache_type, my_tell(file,MYF(MY_WME)), 0, MYF(MY_WME | MY_NABP))) goto err; - if (log_type == LOG_NORMAL) + switch (log_type) { + case LOG_NORMAL: { char *end; #ifdef __NT__ @@ -200,8 +213,9 @@ void MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg, if (my_b_write(&log_file, (byte*) buff,(uint) (end-buff)) || flush_io_cache(&log_file)) goto err; + break; } - else if (log_type == LOG_NEW) + case LOG_NEW: { time_t skr=time(NULL); struct tm tm_tmp; @@ -217,48 +231,98 @@ void MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg, if (my_b_write(&log_file, (byte*) buff,(uint) strlen(buff)) || flush_io_cache(&log_file)) goto err; + break; } - else if (log_type == LOG_BIN) + case LOG_BIN: { - /* - Explanation of the boolean black magic: - if we are supposed to write magic number try write - clean up if failed - then if index_file has not been previously opened, try to open it - clean up if failed - */ - if ((do_magic && my_b_write(&log_file, (byte*) BINLOG_MAGIC, 4)) || - open_index(O_APPEND | O_RDWR | O_CREAT)) - goto err; - Start_log_event s; - bool error; - s.write(&log_file); - flush_io_cache(&log_file); - pthread_mutex_lock(&LOCK_index); - error=(my_write(index_file, (byte*) log_file_name, strlen(log_file_name), - MYF(MY_NABP | MY_WME)) || - my_write(index_file, (byte*) "\n", 1, MYF(MY_NABP | MY_WME))); - pthread_mutex_unlock(&LOCK_index); - if (error) + bool write_file_name_to_index_file=0; + + myf opt= MY_UNPACK_FILENAME; + if (!index_file_name_arg) { - close_index(); + index_file_name_arg= name; // Use same basename for index file + opt= MY_UNPACK_FILENAME | MY_REPLACE_EXT; + } + + if (!my_b_filelength(&log_file)) + { + /* + The binary log file was empty (probably newly created) + This is the normal case and happens when the user doesn't specify + an extension for the binary log files. + In this case we write a standard header to it. + */ + if (my_b_write(&log_file, (byte*) BINLOG_MAGIC, BIN_LOG_HEADER_SIZE)) + goto err; + bytes_written += BIN_LOG_HEADER_SIZE; + write_file_name_to_index_file=1; + } + + if (!my_b_inited(&index_file)) + { + /* + First open of this class instance + Create an index file that will hold all file names uses for logging. + Add new entries to the end of it. + */ + fn_format(index_file_name, index_file_name_arg, mysql_data_home, + ".index", opt); + if ((index_file_nr= my_open(index_file_name, + O_RDWR | O_CREAT | O_BINARY , + MYF(MY_WME))) < 0 || + init_io_cache(&index_file, index_file_nr, + IO_SIZE, WRITE_CACHE, + my_seek(index_file_nr,0L,MY_SEEK_END,MYF(0)), + 0, MYF(MY_WME))) + goto err; + } + else + { + safe_mutex_assert_owner(&LOCK_index); + reinit_io_cache(&index_file, WRITE_CACHE, my_b_filelength(&index_file), + 0, 0); + } + if (need_start_event && !no_auto_events) + { + need_start_event=0; + Start_log_event s; + s.set_log_pos(this); + s.write(&log_file); + } + if (flush_io_cache(&log_file)) goto err; + + if (write_file_name_to_index_file) + { + /* As this is a new log file, we write the file name to the index file */ + if (my_b_write(&index_file, (byte*) log_file_name, + strlen(log_file_name)) || + my_b_write(&index_file, (byte*) "\n", 1) || + flush_io_cache(&index_file)) + goto err; } + break; } - return; + case LOG_CLOSED: // Impossible + DBUG_ASSERT(1); + break; + } + DBUG_RETURN(0); err: - sql_print_error("Could not use %s for logging (error %d)", log_name,errno); + sql_print_error("Could not use %s for logging (error %d)", log_name, errno); if (file >= 0) my_close(file,MYF(0)); + if (index_file_nr >= 0) + my_close(index_file_nr,MYF(0)); end_io_cache(&log_file); - x_free(name); name=0; + end_io_cache(&index_file); + safeFree(name); log_type=LOG_CLOSED; - - return; - + DBUG_RETURN(1); } + int MYSQL_LOG::get_current_log(LOG_INFO* linfo) { pthread_mutex_lock(&LOCK_log); @@ -268,296 +332,616 @@ int MYSQL_LOG::get_current_log(LOG_INFO* linfo) return 0; } -// if log_name is "" we stop at the first entry -int MYSQL_LOG::find_first_log(LOG_INFO* linfo, const char* log_name) + +/* + Move all data up in a file in an filename index file + + SYNOPSIS + copy_up_file_and_fill() + index_file File to move + offset Move everything from here to beginning + + NOTE + File will be truncated to be 'offset' shorter or filled up with + newlines + + IMPLEMENTATION + We do the copy outside of the IO_CACHE as the cache buffers would just + make things slower and more complicated. + In most cases the copy loop should only do one read. + + RETURN VALUES + 0 ok +*/ + +static bool copy_up_file_and_fill(IO_CACHE *index_file, my_off_t offset) { - if (index_file < 0) - return LOG_INFO_INVALID; - int error = 0; - char* fname = linfo->log_file_name; - uint log_name_len = (uint) strlen(log_name); - IO_CACHE io_cache; - - // mutex needed because we need to make sure the file pointer does not move - // from under our feet - pthread_mutex_lock(&LOCK_index); - if (init_io_cache(&io_cache, index_file, IO_SIZE, READ_CACHE, (my_off_t) 0, - 0, MYF(MY_WME))) + int bytes_read; + my_off_t init_offset= offset; + File file= index_file->file; + byte io_buf[IO_SIZE*2]; + DBUG_ENTER("copy_up_file_and_fill"); + + for (;; offset+= bytes_read) { - error = LOG_INFO_SEEK; - goto err; + (void) my_seek(file, offset, MY_SEEK_SET, MYF(0)); + if ((bytes_read= (int) my_read(file, io_buf, sizeof(io_buf), MYF(MY_WME))) + < 0) + goto err; + if (!bytes_read) + break; // end of file + (void) my_seek(file, offset-init_offset, MY_SEEK_SET, MYF(0)); + if (my_write(file, (byte*) io_buf, bytes_read, MYF(MY_WME | MY_NABP))) + goto err; } - for(;;) + /* The following will either truncate the file or fill the end with \n' */ + if (my_chsize(file, offset - init_offset, '\n', MYF(MY_WME))) + goto err; + + /* Reset data in old index cache */ + reinit_io_cache(index_file, READ_CACHE, (my_off_t) 0, 0, 1); + DBUG_RETURN(0); + +err: + DBUG_RETURN(1); +} + + +/* + Find the position in the log-index-file for the given log name + + SYNOPSIS + find_log_pos() + linfo Store here the found log file name and position to + the NEXT log file name in the index file. + log_name Filename to find in the index file. + Is a null pointer if we want to read the first entry + need_lock Set this to 1 if the parent doesn't already have a + lock on LOCK_index + + NOTE + On systems without the truncate function the file will end with one or + more empty lines. These will be ignored when reading the file. + + RETURN VALUES + 0 ok + LOG_INFO_EOF End of log-index-file found + LOG_INFO_IO Got IO error while reading file +*/ + +int MYSQL_LOG::find_log_pos(LOG_INFO *linfo, const char *log_name, + bool need_lock) +{ + int error= 0; + char *fname= linfo->log_file_name; + uint log_name_len= log_name ? (uint) strlen(log_name) : 0; + DBUG_ENTER("find_log_pos"); + DBUG_PRINT("enter",("log_name: %s", log_name ? log_name : "NULL")); + + /* + Mutex needed because we need to make sure the file pointer does not move + from under our feet + */ + if (need_lock) + pthread_mutex_lock(&LOCK_index); + safe_mutex_assert_owner(&LOCK_index); + + /* As the file is flushed, we can't get an error here */ + (void) reinit_io_cache(&index_file, READ_CACHE, (my_off_t) 0, 0, 0); + + for (;;) { uint length; - if (!(length=my_b_gets(&io_cache, fname, FN_REFLEN-1))) + my_off_t offset= my_b_tell(&index_file); + /* If we get 0 or 1 characters, this is the end of the file */ + + if ((length= my_b_gets(&index_file, fname, FN_REFLEN)) <= 1) { - error = !io_cache.error ? LOG_INFO_EOF : LOG_INFO_IO; - goto err; + /* Did not find the given entry; Return not found or error */ + error= !index_file.error ? LOG_INFO_EOF : LOG_INFO_IO; + break; } - // if the log entry matches, empty string matching anything - if (!log_name_len || + // if the log entry matches, null string matching anything + if (!log_name || (log_name_len == length-1 && fname[log_name_len] == '\n' && !memcmp(fname, log_name, log_name_len))) { + DBUG_PRINT("info",("Found log file entry")); fname[length-1]=0; // remove last \n - linfo->index_file_offset = my_b_tell(&io_cache); + linfo->index_file_start_offset= offset; + linfo->index_file_offset = my_b_tell(&index_file); break; } } - error = 0; -err: - pthread_mutex_unlock(&LOCK_index); - end_io_cache(&io_cache); - return error; - + if (need_lock) + pthread_mutex_unlock(&LOCK_index); + DBUG_RETURN(error); } -int MYSQL_LOG::find_next_log(LOG_INFO* linfo) +/* + Find the position in the log-index-file for the given log name + + SYNOPSIS + find_next_log() + linfo Store here the next log file name and position to + the file name after that. + need_lock Set this to 1 if the parent doesn't already have a + lock on LOCK_index + + NOTE + - Before calling this function, one has to call find_log_pos() + to set up 'linfo' + - Mutex needed because we need to make sure the file pointer does not move + from under our feet + + RETURN VALUES + 0 ok + LOG_INFO_EOF End of log-index-file found + LOG_INFO_SEEK Could not allocate IO cache + LOG_INFO_IO Got IO error while reading file +*/ + +int MYSQL_LOG::find_next_log(LOG_INFO* linfo, bool need_lock) { - // mutex needed because we need to make sure the file pointer does not move - // from under our feet - if (index_file < 0) return LOG_INFO_INVALID; - int error = 0; - char* fname = linfo->log_file_name; - IO_CACHE io_cache; + int error= 0; uint length; + char *fname= linfo->log_file_name; - pthread_mutex_lock(&LOCK_index); - if (init_io_cache(&io_cache, index_file, IO_SIZE, - READ_CACHE, (my_off_t) linfo->index_file_offset, 0, - MYF(MY_WME))) - { - error = LOG_INFO_SEEK; - goto err; - } - if (!(length=my_b_gets(&io_cache, fname, FN_REFLEN))) + if (need_lock) + pthread_mutex_lock(&LOCK_index); + safe_mutex_assert_owner(&LOCK_index); + + /* As the file is flushed, we can't get an error here */ + (void) reinit_io_cache(&index_file, READ_CACHE, linfo->index_file_offset, 0, + 0); + + linfo->index_file_start_offset= linfo->index_file_offset; + if ((length=my_b_gets(&index_file, fname, FN_REFLEN)) <= 1) { - error = !io_cache.error ? LOG_INFO_EOF : LOG_INFO_IO; + error = !index_file.error ? LOG_INFO_EOF : LOG_INFO_IO; goto err; } fname[length-1]=0; // kill /n - linfo->index_file_offset = my_b_tell(&io_cache); - error = 0; + linfo->index_file_offset = my_b_tell(&index_file); err: - pthread_mutex_unlock(&LOCK_index); - end_io_cache(&io_cache); + if (need_lock) + pthread_mutex_unlock(&LOCK_index); return error; } - -int MYSQL_LOG::purge_logs(THD* thd, const char* to_log) + +/* + Delete all logs refered to in the index file + Start writing to a new log file. The new index file will only contain + this file. + + SYNOPSIS + reset_logs() + thd Thread + + NOTE + If not called from slave thread, write start event to new log + + + RETURN VALUES + 0 ok + 1 error +*/ + +bool MYSQL_LOG::reset_logs(THD* thd) { - if (index_file < 0) return LOG_INFO_INVALID; - if (no_rotate) return LOG_INFO_PURGE_NO_ROTATE; - int error; - char fname[FN_REFLEN]; - char *p; - uint fname_len, i; - bool logs_to_purge_inited = 0, logs_to_keep_inited = 0, found_log = 0; - DYNAMIC_ARRAY logs_to_purge, logs_to_keep; - my_off_t purge_offset ; - LINT_INIT(purge_offset); - IO_CACHE io_cache; - + LOG_INFO linfo; + bool error=0; + const char* save_name; + enum_log_type save_log_type; + DBUG_ENTER("reset_logs"); + + /* + We need to get both locks to be sure that no one is trying to + write to the index log file. + */ + pthread_mutex_lock(&LOCK_log); pthread_mutex_lock(&LOCK_index); - - if (init_io_cache(&io_cache,index_file, IO_SIZE*2, READ_CACHE, (my_off_t) 0, - 0, MYF(MY_WME))) - { - error = LOG_INFO_MEM; - goto err; - } - if (my_init_dynamic_array(&logs_to_purge, sizeof(char*), 1024, 1024)) + + /* Save variables so that we can reopen the log */ + save_name=name; + name=0; // Protect against free + save_log_type=log_type; + close(0); // Don't close the index file + + /* First delete all old log files */ + + if (find_log_pos(&linfo, NullS, 0)) { - error = LOG_INFO_MEM; + error=1; goto err; } - logs_to_purge_inited = 1; - if (my_init_dynamic_array(&logs_to_keep, sizeof(char*), 1024, 1024)) + for (;;) { - error = LOG_INFO_MEM; - goto err; + my_delete(linfo.log_file_name, MYF(MY_WME)); + if (find_next_log(&linfo, 0)) + break; } - logs_to_keep_inited = 1; - - for(;;) - { - my_off_t init_purge_offset= my_b_tell(&io_cache); - if (!(fname_len=my_b_gets(&io_cache, fname, FN_REFLEN))) - { - if(!io_cache.error) - break; - error = LOG_INFO_IO; - goto err; - } + /* Start logging with a new file */ + close(1); // Close index file + my_delete(index_file_name, MYF(MY_WME)); // Reset (open will update) + if (!thd->slave_thread) + need_start_event=1; + open(save_name, save_log_type, 0, index_file_name, + io_cache_type, no_auto_events); + my_free((gptr) save_name, MYF(0)); - fname[--fname_len]=0; // kill \n - if(!memcmp(fname, to_log, fname_len + 1 )) - { - found_log = 1; - purge_offset = init_purge_offset; - } - - // if one of the logs before the target is in use - if(!found_log && log_in_use(fname)) - { - error = LOG_INFO_IN_USE; - goto err; - } - - if (!(p = sql_memdup(fname, fname_len+1)) || - insert_dynamic(found_log ? &logs_to_keep : &logs_to_purge, - (gptr) &p)) - { - error = LOG_INFO_MEM; - goto err; - } - } - - end_io_cache(&io_cache); - if(!found_log) +err: + pthread_mutex_unlock(&LOCK_index); + pthread_mutex_unlock(&LOCK_log); + DBUG_RETURN(error); +} + + +/* + Delete the current log file, remove it from index file and start on next + + SYNOPSIS + purge_first_log() + rli Relay log information + + NOTE + - This is only called from the slave-execute thread when it has read + all commands from a log and want to switch to a new log. + - When this happens, we should never be in an active transaction as + a transaction is always written as a single block to the binary log. + + IMPLEMENTATION + - Protects index file with LOCK_index + - Delete first log file, + - Copy all file names after this one to the front of the index file + - If the OS has truncate, truncate the file, else fill it with \n' + - Read the first file name from the index file and store in rli->linfo + + RETURN VALUES + 0 ok + LOG_INFO_EOF End of log-index-file found + LOG_INFO_SEEK Could not allocate IO cache + LOG_INFO_IO Got IO error while reading file +*/ + +int MYSQL_LOG::purge_first_log(struct st_relay_log_info* rli) +{ + int error; + DBUG_ENTER("purge_first_log"); + + /* + Test pre-conditions. + + Assume that we have previously read the first log and + stored it in rli->relay_log_name + */ + DBUG_ASSERT(is_open()); + DBUG_ASSERT(rli->slave_running == 1); + DBUG_ASSERT(!strcmp(rli->linfo.log_file_name,rli->relay_log_name)); + DBUG_ASSERT(rli->linfo.index_file_offset == + strlen(rli->relay_log_name) + 1); + + /* We have already processed the relay log, so it's safe to delete it */ + my_delete(rli->relay_log_name, MYF(0)); + pthread_mutex_lock(&LOCK_index); + if (copy_up_file_and_fill(&index_file, rli->linfo.index_file_offset)) { - error = LOG_INFO_EOF; + error= LOG_INFO_IO; goto err; } + + /* + Update the space counter used by all relay logs + Ok to broadcast after the critical region as there is no risk of + the mutex being destroyed by this thread later - this helps save + context switches + */ + pthread_mutex_lock(&rli->log_space_lock); + rli->log_space_total -= rli->relay_log_pos; + pthread_mutex_unlock(&rli->log_space_lock); + pthread_cond_broadcast(&rli->log_space_cond); - for(i = 0; i < logs_to_purge.elements; i++) - { - char* l; - get_dynamic(&logs_to_purge, (gptr)&l, i); - if (my_delete(l, MYF(MY_WME))) - sql_print_error("Error deleting %s during purge", l); - } - - // if we get killed -9 here, the sysadmin would have to do a small - // vi job on the log index file after restart - otherwise, this should - // be safe -#ifdef HAVE_FTRUNCATE - if (ftruncate(index_file,0)) + /* + Read the next log file name from the index file and pass it back to + the caller + */ + if ((error=find_log_pos(&rli->linfo, NullS, 0 /*no mutex*/))) { - sql_print_error( -"Could not truncate the binlog index file during log purge for write"); - error = LOG_INFO_FATAL; + char buff[22]; + sql_print_error("next log error: %d offset: %s log: %s", + error, + llstr(rli->linfo.index_file_offset,buff), + rli->linfo.log_file_name); goto err; } - my_seek(index_file, 0, MY_SEEK_CUR,MYF(MY_WME)); -#else - my_close(index_file, MYF(MY_WME)); - my_delete(index_file_name, MYF(MY_WME)); - if(!(index_file = my_open(index_file_name, - O_CREAT | O_BINARY | O_RDWR | O_APPEND, - MYF(MY_WME)))) - { - sql_print_error( -"Could not re-open the binlog index file during log purge for write"); - error = LOG_INFO_FATAL; + /* + Reset position to current log. This involves setting both of the + position variables: + */ + rli->relay_log_pos = BIN_LOG_HEADER_SIZE; + rli->pending = 0; + strmake(rli->relay_log_name,rli->linfo.log_file_name, + sizeof(rli->relay_log_name)-1); + + /* Store where we are in the new file for the execution thread */ + flush_relay_log_info(rli); + +err: + pthread_mutex_unlock(&LOCK_index); + DBUG_RETURN(error); +} + + +/* + Remove all logs before the given log from disk and from the index file. + + SYNOPSIS + purge_logs() + thd Thread pointer + to_log Delete all log file name before this file. This file is not + deleted + + NOTES + If any of the logs before the deleted one is in use, + only purge logs up to this one. + + RETURN VALUES + 0 ok + LOG_INFO_PURGE_NO_ROTATE Binary file that can't be rotated + LOG_INFO_EOF to_log not found +*/ + +int MYSQL_LOG::purge_logs(THD* thd, const char* to_log) +{ + int error; + LOG_INFO log_info; + DBUG_ENTER("purge_logs"); + + if (no_rotate) + DBUG_RETURN(LOG_INFO_PURGE_NO_ROTATE); + + pthread_mutex_lock(&LOCK_index); + if ((error=find_log_pos(&log_info, to_log, 0 /*no mutex*/))) goto err; + + /* + File name exists in index file; Delete until we find this file + or a file that is used. + */ + if ((error=find_log_pos(&log_info, NullS, 0 /*no mutex*/))) + goto err; + while (strcmp(to_log,log_info.log_file_name) && + !log_in_use(log_info.log_file_name)) + { + /* It's not fatal even if we can't delete a log file */ + my_delete(log_info.log_file_name, MYF(0)); + if (find_next_log(&log_info, 0)) + break; } -#endif - - for(i = 0; i < logs_to_keep.elements; i++) + + /* + If we get killed -9 here, the sysadmin would have to edit + the log index file after restart - otherwise, this should be safe + */ + + if (copy_up_file_and_fill(&index_file, log_info.index_file_start_offset)) { - char* l; - get_dynamic(&logs_to_keep, (gptr)&l, i); - if (my_write(index_file, (byte*) l, strlen(l), MYF(MY_WME|MY_NABP)) || - my_write(index_file, (byte*) "\n", 1, MYF(MY_WME|MY_NABP))) - { - error = LOG_INFO_FATAL; - goto err; - } + error= LOG_INFO_IO; + goto err; } - // now update offsets - adjust_linfo_offsets(purge_offset); - error = 0; + // now update offsets in index file for running threads + adjust_linfo_offsets(log_info.index_file_start_offset); err: pthread_mutex_unlock(&LOCK_index); - if(logs_to_purge_inited) - delete_dynamic(&logs_to_purge); - if(logs_to_keep_inited) - delete_dynamic(&logs_to_keep); - end_io_cache(&io_cache); - return error; + DBUG_RETURN(error); } -// we assume that buf has at least FN_REFLEN bytes alloced +/* + Create a new log file name + + SYNOPSIS + make_log_name() + buf buf of at least FN_REFLEN where new name is stored + + NOTE + If file name will be longer then FN_REFLEN it will be truncated +*/ + void MYSQL_LOG::make_log_name(char* buf, const char* log_ident) { - buf[0] = 0; // In case of error - if (inited) + if (inited) // QQ When is this not true ? { - int dir_len = dirname_length(log_file_name); - int ident_len = (uint) strlen(log_ident); - if (dir_len + ident_len + 1 > FN_REFLEN) - return; // protection agains malicious buffer overflow - - memcpy(buf, log_file_name, dir_len); - // copy filename + end null - memcpy(buf + dir_len, log_ident, ident_len + 1); + uint dir_len = dirname_length(log_file_name); + if (dir_len > FN_REFLEN) + dir_len=FN_REFLEN-1; + strnmov(buf, log_file_name, dir_len); + strmake(buf+dir_len, log_ident, FN_REFLEN - dir_len); } } -bool MYSQL_LOG::is_active(const char* log_file_name) + +/* + Check if we are writing/reading to the given log file +*/ + +bool MYSQL_LOG::is_active(const char *log_file_name_arg) { - return inited && !strcmp(log_file_name, this->log_file_name); + return inited && !strcmp(log_file_name, log_file_name_arg); } -void MYSQL_LOG::new_file(bool inside_mutex) + +/* + Start writing to a new log file or reopen the old file + + SYNOPSIS + new_file() + need_lock Set to 1 (default) if caller has not locked + LOCK_log and LOCK_index + + NOTE + The new file name is stored last in the index file +*/ + +void MYSQL_LOG::new_file(bool need_lock) { + char new_name[FN_REFLEN], *new_name_ptr, *old_name; + enum_log_type save_log_type; + if (!is_open()) - return; + return; // Should never happen - if (!inside_mutex) - VOID(pthread_mutex_lock(&LOCK_log)); + if (need_lock) + { + pthread_mutex_lock(&LOCK_log); + pthread_mutex_lock(&LOCK_index); + } + safe_mutex_assert_owner(&LOCK_log); + safe_mutex_assert_owner(&LOCK_index); - char new_name[FN_REFLEN], *old_name = name; - + // Reuse old name if not binlog and not update log + new_name_ptr= name; + + /* + Only rotate open logs that are marked non-rotatable + (binlog with constant name are non-rotatable) + */ if (!no_rotate) { /* - only rotate open logs that are marked non-rotatable - (binlog with constant name are non-rotatable) + If user hasn't specified an extension, generate a new log name + We have to do this here and not in open as we want to store the + new file name in the current binary log file. */ if (generate_new_name(new_name, name)) - { - if (!inside_mutex) - VOID(pthread_mutex_unlock(&LOCK_log)); - return; // Something went wrong - } + goto end; + new_name_ptr=new_name; + if (log_type == LOG_BIN) { + if (!no_auto_events) + { + /* + We log the whole file name for log file as the user may decide + to change base names at some point. + */ + THD* thd = current_thd; + Rotate_log_event r(thd,new_name+dirname_length(new_name)); + r.set_log_pos(this); + + /* + Because this log rotation could have been initiated by a master of + the slave running with log-bin, we set the flag on rotate + event to prevent infinite log rotation loop + */ + if (thd->slave_thread) + r.flags|= LOG_EVENT_FORCED_ROTATE_F; + r.write(&log_file); + bytes_written += r.get_event_len(); + } /* - We log the whole file name for log file as the user may decide - to change base names at some point. + Update needs to be signalled even if there is no rotate event + log rotation should give the waiting thread a signal to + discover EOF and move on to the next log. */ - Rotate_log_event r(new_name+dirname_length(new_name)); - r.write(&log_file); - VOID(pthread_cond_broadcast(&COND_binlog_update)); + signal_update(); } } - else - strmov(new_name, old_name); // Reopen old file name - name=0; + old_name=name; + save_log_type=log_type; + name=0; // Don't free name close(); - open(old_name, log_type, new_name); + open(old_name, save_log_type, new_name_ptr, index_file_name, io_cache_type, + no_auto_events); my_free(old_name,MYF(0)); - last_time=query_start=0; - write_error=0; - if (!inside_mutex) - VOID(pthread_mutex_unlock(&LOCK_log)); +end: + if (need_lock) + { + pthread_mutex_unlock(&LOCK_index); + pthread_mutex_unlock(&LOCK_log); + } } +bool MYSQL_LOG::append(Log_event* ev) +{ + bool error = 0; + pthread_mutex_lock(&LOCK_log); + + DBUG_ASSERT(log_file.type == SEQ_READ_APPEND); + /* + Log_event::write() is smart enough to use my_b_write() or + my_b_append() depending on the kind of cache we have. + */ + if (ev->write(&log_file)) + { + error=1; + goto err; + } + bytes_written += ev->get_event_len(); + if ((uint) my_b_append_tell(&log_file) > max_binlog_size) + { + pthread_mutex_lock(&LOCK_index); + new_file(0); + pthread_mutex_unlock(&LOCK_index); + } + +err: + pthread_mutex_unlock(&LOCK_log); + signal_update(); // Safe as we don't call close + return error; +} + + +bool MYSQL_LOG::appendv(const char* buf, uint len,...) +{ + bool error= 0; + va_list(args); + va_start(args,len); + + DBUG_ASSERT(log_file.type == SEQ_READ_APPEND); + + pthread_mutex_lock(&LOCK_log); + do + { + if (my_b_append(&log_file,(byte*) buf,len)) + { + error= 1; + goto err; + } + bytes_written += len; + } while ((buf=va_arg(args,const char*)) && (len=va_arg(args,uint))); + + if ((uint) my_b_append_tell(&log_file) > max_binlog_size) + { + pthread_mutex_lock(&LOCK_index); + new_file(0); + pthread_mutex_unlock(&LOCK_index); + } + +err: + pthread_mutex_unlock(&LOCK_log); + if (!error) + signal_update(); + return error; +} + + +/* + Write to normal (not rotable) log + This is the format for the 'normal', 'slow' and 'update' logs. +*/ + bool MYSQL_LOG::write(THD *thd,enum enum_server_command command, const char *format,...) { @@ -578,7 +962,7 @@ bool MYSQL_LOG::write(THD *thd,enum enum_server_command command, if (thd) { // Normal thread if ((thd->options & OPTION_LOG_OFF) && - (thd->master_access & PROCESS_ACL)) + (thd->master_access & SUPER_ACL)) { VOID(pthread_mutex_unlock(&LOCK_log)); return 0; // No logging @@ -637,110 +1021,122 @@ bool MYSQL_LOG::write(THD *thd,enum enum_server_command command, return 0; } -/* Write to binary log in a format to be used for replication */ -bool MYSQL_LOG::write(Query_log_event* event_info) +/* + Write an event to the binary log +*/ + +bool MYSQL_LOG::write(Log_event* event_info) { - /* In most cases this is only called if 'is_open()' is true */ bool error=0; - bool should_rotate = 0; + DBUG_ENTER("MYSQL_LOG::write(event)"); if (!inited) // Can't use mutex if not init - return 0; - VOID(pthread_mutex_lock(&LOCK_log)); + { + DBUG_PRINT("error",("not initied")); + DBUG_RETURN(0); + } + pthread_mutex_lock(&LOCK_log); + + /* In most cases this is only called if 'is_open()' is true */ if (is_open()) { + bool should_rotate = 0; THD *thd=event_info->thd; - IO_CACHE *file = (event_info->cache_stmt ? &thd->transaction.trans_log : + const char *local_db = event_info->get_db(); +#ifdef USING_TRANSACTIONS + IO_CACHE *file = ((event_info->get_cache_stmt()) ? + &thd->transaction.trans_log : &log_file); - if ((!(thd->options & OPTION_BIN_LOG) && - (thd->master_access & PROCESS_ACL)) || - !db_ok(event_info->db, binlog_do_db, binlog_ignore_db)) +#else + IO_CACHE *file = &log_file; +#endif + if ((thd && !(thd->options & OPTION_BIN_LOG) && + (thd->master_access & SUPER_ACL)) || + (local_db && !db_ok(local_db, binlog_do_db, binlog_ignore_db))) { VOID(pthread_mutex_unlock(&LOCK_log)); - return 0; + DBUG_PRINT("error",("!db_ok")); + DBUG_RETURN(0); } error=1; - - if (file == &thd->transaction.trans_log - && !my_b_tell(&thd->transaction.trans_log)) { - - /* Add the "BEGIN" and "COMMIT" in the binlog around transactions - which may contain more than 1 SQL statement. If we run with - AUTOCOMMIT=1, then MySQL immediately writes each SQL statement to - the binlog when the statement has been completed. No need to add - "BEGIN" ... "COMMIT" around such statements. Otherwise, MySQL uses - thd->transaction.trans_log to cache the SQL statements until the - explicit commit, and at the commit writes the contents in .trans_log - to the binlog. - - We write the "BEGIN" mark first in the buffer (.trans_log) where we - store the SQL statements for a transaction. At the transaction commit - we will add the "COMMIT mark and write the buffer to the binlog. - The function my_b_tell above returns != 0 if there already is data - in the buffer. */ - - int save_query_length = thd->query_length; - - thd->query_length = 5; /* length of string BEGIN */ - - Query_log_event qinfo(thd, "BEGIN", TRUE); - - error = ((&qinfo)->write(file)); - - thd->query_length = save_query_length; - - if (error) - goto err; - } - - if (thd->last_insert_id_used) - { - Intvar_log_event e((uchar)LAST_INSERT_ID_EVENT, thd->current_insert_id); - if(thd->server_id) - e.server_id = thd->server_id; - if (e.write(file)) - goto err; - } - if (thd->insert_id_used) - { - Intvar_log_event e((uchar)INSERT_ID_EVENT, thd->last_insert_id); - if(thd->server_id) - e.server_id = thd->server_id; - if (e.write(file)) - goto err; - } - if (thd->convert_set) + /* + No check for auto events flag here - this write method should + never be called if auto-events are enabled + */ + if (thd) { - char buf[1024] = "SET CHARACTER SET "; - char* p = strend(buf); - p = strmov(p, thd->convert_set->name); - int save_query_length = thd->query_length; - // just in case somebody wants it later - thd->query_length = (uint)(p - buf); - Query_log_event e(thd, buf); - if (e.write(file)) - goto err; - thd->query_length = save_query_length; // clean up + if (thd->last_insert_id_used) + { + Intvar_log_event e(thd,(uchar) LAST_INSERT_ID_EVENT, + thd->current_insert_id); + e.set_log_pos(this); + if (thd->server_id) + e.server_id = thd->server_id; + if (e.write(file)) + goto err; + } + if (thd->insert_id_used) + { + Intvar_log_event e(thd,(uchar) INSERT_ID_EVENT,thd->last_insert_id); + e.set_log_pos(this); + if (thd->server_id) + e.server_id = thd->server_id; + if (e.write(file)) + goto err; + } + if (thd->rand_used) + { + Rand_log_event e(thd,thd->rand_saved_seed1,thd->rand_saved_seed2); + e.set_log_pos(this); + if (e.write(file)) + goto err; + } + if (thd->variables.convert_set) + { + char buf[256], *p; + p= strmov(strmov(buf, "SET CHARACTER SET "), + thd->variables.convert_set->name); + Query_log_event e(thd, buf, (ulong) (p - buf), 0); + e.set_log_pos(this); + if (e.write(file)) + goto err; + } } + event_info->set_log_pos(this); if (event_info->write(file) || file == &log_file && flush_io_cache(file)) goto err; error=0; - should_rotate = (file == &log_file && my_b_tell(file) >= max_binlog_size); - - /* Tell for transactional table handlers up to which position in the - binlog file we wrote. The table handler can store this info, and - after crash recovery print for the user the offset of the last - transactions which were recovered. Actually, we must also call - the table handler commit here, protected by the LOCK_log mutex, - because otherwise the transactions may end up in a different order - in the table handler log! */ - - if (file == &log_file) { - error = ha_report_binlog_offset_and_commit(thd, log_file_name, - file->pos_in_file); + + /* + Tell for transactional table handlers up to which position in the + binlog file we wrote. The table handler can store this info, and + after crash recovery print for the user the offset of the last + transactions which were recovered. Actually, we must also call + the table handler commit here, protected by the LOCK_log mutex, + because otherwise the transactions may end up in a different order + in the table handler log! + */ + + if (file == &log_file) + { + /* + LOAD DATA INFILE in AUTOCOMMIT=1 mode writes to the binlog + chunks also before it is successfully completed. We only report + the binlog write and do the commit inside the transactional table + handler if the log event type is appropriate. + */ + + if (event_info->get_type_code() == QUERY_EVENT + || event_info->get_type_code() == EXEC_LOAD_EVENT) + { + error = ha_report_binlog_offset_and_commit(thd, log_file_name, + file->pos_in_file); + } + + should_rotate= (my_b_tell(file) >= (my_off_t) max_binlog_size); } err: @@ -753,118 +1149,136 @@ err: write_error=1; } if (file == &log_file) - VOID(pthread_cond_broadcast(&COND_binlog_update)); + signal_update(); + if (should_rotate) + { + pthread_mutex_lock(&LOCK_index); + new_file(0); // inside mutex + pthread_mutex_unlock(&LOCK_index); + } } - if (should_rotate) - new_file(1); // inside mutex - VOID(pthread_mutex_unlock(&LOCK_log)); - return error; + + pthread_mutex_unlock(&LOCK_log); + DBUG_RETURN(error); } + +uint MYSQL_LOG::next_file_id() +{ + uint res; + pthread_mutex_lock(&LOCK_log); + res = file_id++; + pthread_mutex_unlock(&LOCK_log); + return res; +} + + /* Write a cached log entry to the binary log - We only come here if there is something in the cache. - 'cache' needs to be reinitialized after this functions returns. + + NOTE + - We only come here if there is something in the cache. + - The thing in the cache is always a complete transaction + - 'cache' needs to be reinitialized after this functions returns. + + IMPLEMENTATION + - To support transaction over replication, we wrap the transaction + with BEGIN/COMMIT in the binary log. */ bool MYSQL_LOG::write(THD *thd, IO_CACHE *cache) { VOID(pthread_mutex_lock(&LOCK_log)); - bool error=1; + DBUG_ENTER("MYSQL_LOG::write(cache"); - if (is_open()) + if (is_open()) // Should always be true { uint length; - if (reinit_io_cache(cache, READ_CACHE, 0, 0, 0)) + /* + Add the "BEGIN" and "COMMIT" in the binlog around transactions + which may contain more than 1 SQL statement. If we run with + AUTOCOMMIT=1, then MySQL immediately writes each SQL statement to + the binlog when the statement has been completed. No need to add + "BEGIN" ... "COMMIT" around such statements. Otherwise, MySQL uses + thd->transaction.trans_log to cache the SQL statements until the + explicit commit, and at the commit writes the contents in .trans_log + to the binlog. + + We write the "BEGIN" mark first in the buffer (.trans_log) where we + store the SQL statements for a transaction. At the transaction commit + we will add the "COMMIT mark and write the buffer to the binlog. + */ { - sql_print_error(ER(ER_ERROR_ON_WRITE), cache->file_name, errno); - goto err; + Query_log_event qinfo(thd, "BEGIN", 5, TRUE); + if (qinfo.write(&log_file)) + goto err; } + /* Read from the file used to cache the queries .*/ + if (reinit_io_cache(cache, READ_CACHE, 0, 0, 0)) + goto err; length=my_b_bytes_in_cache(cache); do { - if (my_b_write(&log_file, cache->rc_pos, length)) - { - if (!write_error) - sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno); + /* Write data to the binary log file */ + if (my_b_write(&log_file, cache->read_pos, length)) goto err; - } - cache->rc_pos=cache->rc_end; // Mark buffer used up + cache->read_pos=cache->read_end; // Mark buffer used up } while ((length=my_b_fill(cache))); - if (flush_io_cache(&log_file)) + + /* + We write the command "COMMIT" as the last SQL command in the + binlog segment cached for this transaction + */ + { - if (!write_error) - sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno); - goto err; + Query_log_event qinfo(thd, "COMMIT", 6, TRUE); + if (qinfo.write(&log_file) || flush_io_cache(&log_file)) + goto err; } if (cache->error) // Error on read { sql_print_error(ER(ER_ERROR_ON_READ), cache->file_name, errno); + write_error=1; // Don't give more errors goto err; } - error = ha_report_binlog_offset_and_commit(thd, log_file_name, - log_file.pos_in_file); - if (error) + if ((ha_report_binlog_offset_and_commit(thd, log_file_name, + log_file.pos_in_file))) goto err; - + signal_update(); if (my_b_tell(&log_file) >= (my_off_t) max_binlog_size) - new_file(1); // inside mutex + { + pthread_mutex_lock(&LOCK_index); + new_file(0); // inside mutex + pthread_mutex_unlock(&LOCK_index); + } + } - error=0; + VOID(pthread_mutex_unlock(&LOCK_log)); + DBUG_RETURN(0); err: - if (error) - write_error=1; - else - VOID(pthread_cond_broadcast(&COND_binlog_update)); - + if (!write_error) + { + write_error= 1; + sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno); + } VOID(pthread_mutex_unlock(&LOCK_log)); - - return error; + DBUG_RETURN(1); } -bool MYSQL_LOG::write(Load_log_event* event_info) -{ - bool error=0; - bool should_rotate = 0; - - if (inited) - { - VOID(pthread_mutex_lock(&LOCK_log)); - if (is_open()) - { - THD *thd=event_info->thd; - if ((thd->options & OPTION_BIN_LOG) || - !(thd->master_access & PROCESS_ACL)) - { - if (event_info->write(&log_file) || flush_io_cache(&log_file)) - { - if (!write_error) - sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno); - error=write_error=1; - } - should_rotate = (my_b_tell(&log_file) >= max_binlog_size); - VOID(pthread_cond_broadcast(&COND_binlog_update)); - } - } - - if(should_rotate) - new_file(1); // inside mutex - - VOID(pthread_mutex_unlock(&LOCK_log)); - } - - - return error; -} +/* + Write update log in a format suitable for incremental backup + NOTE + - This code should be deleted in MySQL 5,0 as the binary log + is a full replacement for the update log. -/* Write update log in a format suitable for incremental backup */ +*/ bool MYSQL_LOG::write(THD *thd,const char *query, uint query_length, - time_t query_start) + time_t query_start_arg) { bool error=0; if (is_open()) @@ -877,12 +1291,12 @@ bool MYSQL_LOG::write(THD *thd,const char *query, uint query_length, char buff[80],*end; end=buff; if (!(thd->options & OPTION_UPDATE_LOG) && - (thd->master_access & PROCESS_ACL)) + (thd->master_access & SUPER_ACL)) { VOID(pthread_mutex_unlock(&LOCK_log)); return 0; } - if ((specialflag & SPECIAL_LONG_LOG_FORMAT) || query_start) + if ((specialflag & SPECIAL_LONG_LOG_FORMAT) || query_start_arg) { current_time=time(NULL); if (current_time != last_time) @@ -910,13 +1324,13 @@ bool MYSQL_LOG::write(THD *thd,const char *query, uint query_length, thd->ip ? thd->ip : "") == (uint) -1) tmp_errno=errno; } - if (query_start) + if (query_start_arg) { /* For slow query log */ if (my_b_printf(&log_file, "# Query_time: %lu Lock_time: %lu Rows_sent: %lu Rows_examined: %lu\n", - (ulong) (current_time - query_start), - (ulong) (thd->time_after_lock - query_start), + (ulong) (current_time - query_start_arg), + (ulong) (thd->time_after_lock - query_start_arg), (ulong) thd->sent_row_count, (ulong) thd->examined_row_count) == (uint) -1) tmp_errno=errno; @@ -943,11 +1357,11 @@ bool MYSQL_LOG::write(THD *thd,const char *query, uint query_length, } if (thd->query_start_used) { - if (query_start != thd->query_start()) + if (query_start_arg != thd->query_start()) { - query_start=thd->query_start(); + query_start_arg=thd->query_start(); end=strmov(end,",timestamp="); - end=int10_to_str((long) query_start,end,10); + end=int10_to_str((long) query_start_arg,end,10); } } if (end != buff) @@ -984,41 +1398,109 @@ bool MYSQL_LOG::write(THD *thd,const char *query, uint query_length, return error; } +/* + Wait until we get a signal that the binary log has been updated + + SYNOPSIS + wait_for_update() + thd Thread variable + + NOTES + One must have a lock on LOCK_log before calling this function. + This lock will be freed before return! + + The reason for the above is that for enter_cond() / exit_cond() to + work the mutex must be got before enter_cond() but releases before + exit_cond(). + If you don't do it this way, you will get a deadlock in THD::awake() +*/ + + +void MYSQL_LOG:: wait_for_update(THD* thd) +{ + safe_mutex_assert_owner(&LOCK_log); + const char* old_msg = thd->enter_cond(&update_cond, &LOCK_log, + "Slave: waiting for binlog update"); + pthread_cond_wait(&update_cond, &LOCK_log); + pthread_mutex_unlock(&LOCK_log); // See NOTES + thd->exit_cond(old_msg); +} + + +/* + Close the log file + + SYNOPSIS + close() + exiting Set to 1 if we should also close the index file + This can be set to 0 if we are going to do call open + at once after close, in which case we don't want to + close the index file. + We only write a 'stop' event to the log if exiting is set + + NOTES + One can do an open on the object at once after doing a close. + The internal structures are not freed until cleanup() is called +*/ void MYSQL_LOG::close(bool exiting) { // One can't set log_type here! + DBUG_ENTER("MYSQL_LOG::close"); + DBUG_PRINT("enter",("exiting: %d", (int) exiting)); if (is_open()) { - File file=log_file.file; - if (log_type == LOG_BIN) + if (log_type == LOG_BIN && !no_auto_events && exiting) { Stop_log_event s; + s.set_log_pos(this); s.write(&log_file); - VOID(pthread_cond_broadcast(&COND_binlog_update)); + signal_update(); } end_io_cache(&log_file); - if (my_close(file,MYF(0)) < 0 && ! write_error) + if (my_close(log_file.file,MYF(0)) < 0 && ! write_error) { write_error=1; - sql_print_error(ER(ER_ERROR_ON_WRITE),name,errno); + sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno); } } - if (exiting && index_file >= 0) + + /* + The following test is needed even if is_open() is not set, as we may have + called a not complete close earlier and the index file is still open. + */ + + if (exiting && my_b_inited(&index_file)) { - if (my_close(index_file,MYF(0)) < 0 && ! write_error) + end_io_cache(&index_file); + if (my_close(index_file.file, MYF(0)) < 0 && ! write_error) { - write_error=1; - sql_print_error(ER(ER_ERROR_ON_WRITE),name,errno); + write_error= 1; + sql_print_error(ER(ER_ERROR_ON_WRITE), index_file_name, errno); } - index_file=-1; - log_type=LOG_CLOSED; } + log_type= LOG_CLOSED; safeFree(name); + DBUG_VOID_RETURN; } - /* Check if a string is a valid number */ - /* Output: TRUE -> number */ +/* + Check if a string is a valid number + + SYNOPSIS + test_if_number() + str String to test + res Store value here + allow_wildcards Set to 1 if we should ignore '%' and '_' + + NOTE + For the moment the allow_wildcards argument is not used + Should be move to some other file. + + RETURN VALUES + 1 String is a number + 0 Error +*/ static bool test_if_number(register const char *str, long *res, bool allow_wildcards) @@ -1089,7 +1571,6 @@ void sql_print_error(const char *format,...) } - void sql_perror(const char *message) { #ifdef HAVE_STRERROR @@ -1098,3 +1579,49 @@ void sql_perror(const char *message) perror(message); #endif } + +bool flush_error_log() +{ + bool result=0; + if (opt_error_log) + { + char err_renamed[FN_REFLEN], *end; + end= strmake(err_renamed,log_error_file,FN_REFLEN-4); + strmov(end, "-old"); +#ifdef __WIN__ + char err_temp[FN_REFLEN+4]; + /* + On Windows is necessary a temporary file for to rename + the current error file. + */ + strmov(strmov(err_temp, err_renamed),"-tmp"); + (void) my_delete(err_temp, MYF(0)); + if (freopen(err_temp,"a+",stdout)) + { + freopen(err_temp,"a+",stderr); + (void) my_delete(err_renamed, MYF(0)); + my_rename(log_error_file,err_renamed,MYF(0)); + if (freopen(log_error_file,"a+",stdout)) + freopen(log_error_file,"a+",stderr); + int fd, bytes; + char buf[IO_SIZE]; + if ((fd = my_open(err_temp, O_RDONLY, MYF(0))) >= 0) + { + while ((bytes = (int) my_read(fd, (byte*) buf, IO_SIZE, MYF(0))) > 0) + my_fwrite(stderr, (byte*) buf, (uint) strlen(buf),MYF(0)); + my_close(fd, MYF(0)); + } + (void) my_delete(err_temp, MYF(0)); + } + else + result= 1; +#else + my_rename(log_error_file,err_renamed,MYF(0)); + if (freopen(log_error_file,"a+",stdout)) + freopen(log_error_file,"a+",stderr); + else + result= 1; +#endif + } + return result; +} diff --git a/sql/log_event.cc b/sql/log_event.cc index c30d03adaf5..78470a2b198 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -20,39 +20,430 @@ #pragma implementation // gcc: Class implementation #endif #include "mysql_priv.h" +#include "slave.h" +#include <my_dir.h> #endif /* MYSQL_CLIENT */ +#include <assert.h> -static void pretty_print_char(FILE* file, int c) +inline int my_b_safe_write(IO_CACHE* file, const byte *buf, + int len) { + /* + Sasha: We are not writing this with the ? operator to avoid hitting + a possible compiler bug. At least gcc 2.95 cannot deal with + several layers of ternary operators that evaluated comma(,) operator + expressions inside - I do have a test case if somebody wants it + */ + if (file->type == SEQ_READ_APPEND) + return my_b_append(file, buf,len); + return my_b_write(file, buf,len); +} + +#ifdef MYSQL_CLIENT +static void pretty_print_str(FILE* file, char* str, int len) +{ + char* end = str + len; fputc('\'', file); - switch(c) { - case '\n': fprintf(file, "\\n"); break; - case '\r': fprintf(file, "\\r"); break; - case '\\': fprintf(file, "\\\\"); break; - case '\b': fprintf(file, "\\b"); break; - case '\'': fprintf(file, "\\'"); break; - case 0 : fprintf(file, "\\0"); break; - default: - fputc(c, file); - break; + while (str < end) + { + char c; + switch ((c=*str++)) { + case '\n': fprintf(file, "\\n"); break; + case '\r': fprintf(file, "\\r"); break; + case '\\': fprintf(file, "\\\\"); break; + case '\b': fprintf(file, "\\b"); break; + case '\t': fprintf(file, "\\t"); break; + case '\'': fprintf(file, "\\'"); break; + case 0 : fprintf(file, "\\0"); break; + default: + fputc(c, file); + break; + } } fputc('\'', file); } +#endif + +#ifndef MYSQL_CLIENT + +inline int ignored_error_code(int err_code) +{ + return use_slave_mask && bitmap_is_set(&slave_error_mask, err_code); +} + + +static void pretty_print_str(String* packet, char* str, int len) +{ + char* end = str + len; + packet->append('\''); + while (str < end) + { + char c; + switch ((c=*str++)) { + case '\n': packet->append( "\\n"); break; + case '\r': packet->append( "\\r"); break; + case '\\': packet->append( "\\\\"); break; + case '\b': packet->append( "\\b"); break; + case '\t': packet->append( "\\t"); break; + case '\'': packet->append( "\\'"); break; + case 0 : packet->append( "\\0"); break; + default: + packet->append((char)c); + break; + } + } + packet->append('\''); +} + + +static inline char* slave_load_file_stem(char*buf, uint file_id, + int event_server_id) +{ + fn_format(buf,"SQL_LOAD-",slave_load_tmpdir,"",0); /* 4+32); */ + buf = strend(buf); + buf = int10_to_str(::server_id, buf, 10); + *buf++ = '-'; + buf = int10_to_str(event_server_id, buf, 10); + *buf++ = '-'; + return int10_to_str(file_id, buf, 10); +} + +#endif + +const char* Log_event::get_type_str() +{ + switch(get_type_code()) { + case START_EVENT: return "Start"; + case STOP_EVENT: return "Stop"; + case QUERY_EVENT: return "Query"; + case ROTATE_EVENT: return "Rotate"; + case INTVAR_EVENT: return "Intvar"; + case LOAD_EVENT: return "Load"; + case NEW_LOAD_EVENT: return "New_load"; + case SLAVE_EVENT: return "Slave"; + case CREATE_FILE_EVENT: return "Create_file"; + case APPEND_BLOCK_EVENT: return "Append_block"; + case DELETE_FILE_EVENT: return "Delete_file"; + case EXEC_LOAD_EVENT: return "Exec_load"; + default: /* impossible */ return "Unknown"; + } +} + +#ifndef MYSQL_CLIENT +Log_event::Log_event(THD* thd_arg, uint16 flags_arg, bool using_trans) + :temp_buf(0), exec_time(0), cached_event_len(0), flags(flags_arg), + thd(thd_arg) +{ + server_id = thd->server_id; + when = thd->start_time; + log_pos = thd->log_pos; + cache_stmt= (using_trans && + (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN))); +} + + +Log_event::Log_event() + :temp_buf(0), exec_time(0), cached_event_len(0), flags(0), cache_stmt(0), + thd(0) +{ + server_id = ::server_id; + when = time(NULL); + log_pos=0; +} + +/* + Delete all temporary files used for SQL_LOAD. + + TODO + - When we get a 'server start' event, we should only remove + the files associated with the server id that just started. + Easily fixable by adding server_id as a prefix to the log files. +*/ + +static void cleanup_load_tmpdir() +{ + MY_DIR *dirp; + FILEINFO *file; + uint i; + if (!(dirp=my_dir(slave_load_tmpdir,MYF(MY_WME)))) + return; + + for (i=0 ; i < (uint)dirp->number_off_files; i++) + { + file=dirp->dir_entry+i; + if (is_prefix(file->name,"SQL_LOAD-")) + my_delete(file->name, MYF(0)); + } + + my_dirend(dirp); +} + +#endif + +Log_event::Log_event(const char* buf, bool old_format) + :temp_buf(0), cached_event_len(0), cache_stmt(0) +{ + when = uint4korr(buf); + server_id = uint4korr(buf + SERVER_ID_OFFSET); + if (old_format) + { + log_pos=0; + flags=0; + } + else + { + log_pos = uint4korr(buf + LOG_POS_OFFSET); + flags = uint2korr(buf + FLAGS_OFFSET); + } +#ifndef MYSQL_CLIENT + thd = 0; +#endif +} + + +#ifndef MYSQL_CLIENT + +int Log_event::exec_event(struct st_relay_log_info* rli) +{ + /* + rli is null when (as far as I (Guilhem) know) + the caller is + Load_log_event::exec_event *and* that one is called from + Execute_load_log_event::exec_event. + In this case, we don't do anything here ; + Execute_load_log_event::exec_event will call Log_event::exec_event + again later with the proper rli. + Strictly speaking, if we were sure that rli is null + only in the case discussed above, 'if (rli)' is useless here. + But as we are not 100% sure, keep it for now. + */ + if (rli) + { + if (rli->inside_transaction) + rli->inc_pending(get_event_len()); + else + { + rli->inc_pos(get_event_len(),log_pos); + flush_relay_log_info(rli); + } + } + return 0; +} + +void Log_event::pack_info(String* packet) +{ + net_store_data(packet, "", 0); +} + +void Query_log_event::pack_info(String* packet) +{ + char buf[256]; + String tmp(buf, sizeof(buf)); + tmp.length(0); + if (db && db_len) + { + tmp.append("use "); + tmp.append(db, db_len); + tmp.append("; ", 2); + } + + if (query && q_len) + tmp.append(query, q_len); + net_store_data(packet, (char*)tmp.ptr(), tmp.length()); +} + +void Start_log_event::pack_info(String* packet) +{ + char buf1[256]; + String tmp(buf1, sizeof(buf1)); + tmp.length(0); + char buf[22]; + + tmp.append("Server ver: "); + tmp.append(server_version); + tmp.append(", Binlog ver: "); + tmp.append(llstr(binlog_version, buf)); + net_store_data(packet, tmp.ptr(), tmp.length()); +} + +void Load_log_event::pack_info(String* packet) +{ + char buf[256]; + String tmp(buf, sizeof(buf)); + tmp.length(0); + if (db && db_len) + { + tmp.append("use "); + tmp.append(db, db_len); + tmp.append("; ", 2); + } + + tmp.append("LOAD DATA INFILE '"); + tmp.append(fname, fname_len); + tmp.append("' ", 2); + if (sql_ex.opt_flags && REPLACE_FLAG ) + tmp.append(" REPLACE "); + else if (sql_ex.opt_flags && IGNORE_FLAG ) + tmp.append(" IGNORE "); + + tmp.append("INTO TABLE "); + tmp.append(table_name); + if (sql_ex.field_term_len) + { + tmp.append(" FIELDS TERMINATED BY "); + pretty_print_str(&tmp, sql_ex.field_term, sql_ex.field_term_len); + } + + if (sql_ex.enclosed_len) + { + if (sql_ex.opt_flags && OPT_ENCLOSED_FLAG ) + tmp.append(" OPTIONALLY "); + tmp.append( " ENCLOSED BY "); + pretty_print_str(&tmp, sql_ex.enclosed, sql_ex.enclosed_len); + } + + if (sql_ex.escaped_len) + { + tmp.append( " ESCAPED BY "); + pretty_print_str(&tmp, sql_ex.escaped, sql_ex.escaped_len); + } + + if (sql_ex.line_term_len) + { + tmp.append(" LINES TERMINATED BY "); + pretty_print_str(&tmp, sql_ex.line_term, sql_ex.line_term_len); + } + + if (sql_ex.line_start_len) + { + tmp.append(" LINES STARTING BY "); + pretty_print_str(&tmp, sql_ex.line_start, sql_ex.line_start_len); + } + + if ((int)skip_lines > 0) + tmp.append( " IGNORE %ld LINES ", (long) skip_lines); + + if (num_fields) + { + uint i; + const char* field = fields; + tmp.append(" ("); + for (i = 0; i < num_fields; i++) + { + if (i) + tmp.append(" ,"); + tmp.append( field); + + field += field_lens[i] + 1; + } + tmp.append(')'); + } + + net_store_data(packet, tmp.ptr(), tmp.length()); +} + +void Rotate_log_event::pack_info(String* packet) +{ + char buf1[256], buf[22]; + String tmp(buf1, sizeof(buf1)); + tmp.length(0); + tmp.append(new_log_ident, ident_len); + tmp.append(";pos="); + tmp.append(llstr(pos,buf)); + if (flags & LOG_EVENT_FORCED_ROTATE_F) + tmp.append("; forced by master"); + net_store_data(packet, tmp.ptr(), tmp.length()); +} + +void Intvar_log_event::pack_info(String* packet) +{ + char buf1[256], buf[22]; + String tmp(buf1, sizeof(buf1)); + tmp.length(0); + tmp.append(get_var_type_name()); + tmp.append('='); + tmp.append(llstr(val, buf)); + net_store_data(packet, tmp.ptr(), tmp.length()); +} + +void Rand_log_event::pack_info(String* packet) +{ + char buf1[256], *pos; + pos=strmov(buf1,"rand_seed1="); + pos=int10_to_str((long) seed1, pos, 10); + pos=strmov(pos, ",rand_seed2="); + pos=int10_to_str((long) seed2, pos, 10); + net_store_data(packet, buf1, (uint) (pos-buf1)); +} + +void Slave_log_event::pack_info(String* packet) +{ + char buf1[256], buf[22], *end; + String tmp(buf1, sizeof(buf1)); + tmp.length(0); + tmp.append("host="); + tmp.append(master_host); + tmp.append(",port="); + end= int10_to_str((long) master_port, buf, 10); + tmp.append(buf, (uint32) (end-buf)); + tmp.append(",log="); + tmp.append(master_log); + tmp.append(",pos="); + tmp.append(llstr(master_pos,buf)); + net_store_data(packet, tmp.ptr(), tmp.length()); +} + + +void Log_event::init_show_field_list(List<Item>* field_list) +{ + field_list->push_back(new Item_empty_string("Log_name", 20)); + field_list->push_back(new Item_empty_string("Pos", 20)); + field_list->push_back(new Item_empty_string("Event_type", 20)); + field_list->push_back(new Item_empty_string("Server_id", 20)); + field_list->push_back(new Item_empty_string("Orig_log_pos", 20)); + field_list->push_back(new Item_empty_string("Info", 20)); +} + +/* + * only called by SHOW BINLOG EVENTS + */ +int Log_event::net_send(THD* thd_arg, const char* log_name, my_off_t pos) +{ + String* packet = &thd_arg->packet; + const char* p = strrchr(log_name, FN_LIBCHAR); + const char* event_type; + if (p) + log_name = p + 1; + + packet->length(0); + net_store_data(packet, log_name, strlen(log_name)); + net_store_data(packet, (longlong) pos); + event_type = get_type_str(); + net_store_data(packet, event_type, strlen(event_type)); + net_store_data(packet, server_id); + net_store_data(packet, (longlong) log_pos); + pack_info(packet); + return my_net_write(&thd_arg->net, (char*) packet->ptr(), packet->length()); +} + +#endif /* MYSQL_CLIENT */ + int Query_log_event::write(IO_CACHE* file) { return query ? Log_event::write(file) : -1; } + int Log_event::write(IO_CACHE* file) { return (write_header(file) || write_data(file)) ? -1 : 0; } + int Log_event::write_header(IO_CACHE* file) { - // make sure to change this when the header gets bigger char buf[LOG_EVENT_HEADER_LEN]; char* pos = buf; int4store(pos, (ulong) when); // timestamp @@ -63,7 +454,11 @@ int Log_event::write_header(IO_CACHE* file) long tmp=get_data_size() + LOG_EVENT_HEADER_LEN; int4store(pos, tmp); pos += 4; - return (my_b_write(file, (byte*) buf, (uint) (pos - buf))); + int4store(pos, log_pos); + pos += 4; + int2store(pos, flags); + pos += 2; + return (my_b_safe_write(file, (byte*) buf, (uint) (pos - buf))); } #ifndef MYSQL_CLIENT @@ -72,215 +467,224 @@ int Log_event::read_log_event(IO_CACHE* file, String* packet, pthread_mutex_t* log_lock) { ulong data_len; + int result=0; char buf[LOG_EVENT_HEADER_LEN]; + DBUG_ENTER("read_log_event"); + if (log_lock) pthread_mutex_lock(log_lock); if (my_b_read(file, (byte*) buf, sizeof(buf))) { - if (log_lock) pthread_mutex_unlock(log_lock); - // if the read hits eof, we must report it as eof - // so the caller will know it can go into cond_wait to be woken up - // on the next update to the log - if(!file->error) return LOG_READ_EOF; - return file->error > 0 ? LOG_READ_TRUNC: LOG_READ_IO; + /* + If the read hits eof, we must report it as eof so the caller + will know it can go into cond_wait to be woken up on the next + update to the log. + */ + DBUG_PRINT("error",("file->error: %d", file->error)); + if (!file->error) + result= LOG_READ_EOF; + else + result= (file->error > 0 ? LOG_READ_TRUNC : LOG_READ_IO); + goto end; } - data_len = uint4korr(buf + EVENT_LEN_OFFSET); - if (data_len < LOG_EVENT_HEADER_LEN || data_len > max_allowed_packet) + data_len= uint4korr(buf + EVENT_LEN_OFFSET); + if (data_len < LOG_EVENT_HEADER_LEN || + data_len > current_thd->variables.max_allowed_packet) { - if (log_lock) pthread_mutex_unlock(log_lock); - return (data_len < LOG_EVENT_HEADER_LEN) ? LOG_READ_BOGUS : - LOG_READ_TOO_LARGE; + DBUG_PRINT("error",("data_len: %ld", data_len)); + result= ((data_len < LOG_EVENT_HEADER_LEN) ? LOG_READ_BOGUS : + LOG_READ_TOO_LARGE); + goto end; } packet->append(buf, sizeof(buf)); - data_len -= LOG_EVENT_HEADER_LEN; + data_len-= LOG_EVENT_HEADER_LEN; if (data_len) { if (packet->append(file, data_len)) { - if(log_lock) - pthread_mutex_unlock(log_lock); - // here we should never hit eof in a non-error condtion - // eof means we are reading the event partially, which should - // never happen - return file->error >= 0 ? LOG_READ_TRUNC: LOG_READ_IO; + /* + Here we should never hit EOF in a non-error condition. + EOF means we are reading the event partially, which should + never happen. + */ + result= file->error >= 0 ? LOG_READ_TRUNC: LOG_READ_IO; + /* Implicit goto end; */ } } - if (log_lock) pthread_mutex_unlock(log_lock); - return 0; + +end: + if (log_lock) + pthread_mutex_unlock(log_lock); + DBUG_RETURN(result); } #endif // MYSQL_CLIENT #ifndef MYSQL_CLIENT -#define UNLOCK_MUTEX if(log_lock) pthread_mutex_unlock(log_lock); +#define UNLOCK_MUTEX if (log_lock) pthread_mutex_unlock(log_lock); +#define LOCK_MUTEX if (log_lock) pthread_mutex_lock(log_lock); +#define max_allowed_packet current_thd->variables.max_allowed_packet #else #define UNLOCK_MUTEX +#define LOCK_MUTEX #endif // allocates memory - the caller is responsible for clean-up #ifndef MYSQL_CLIENT -Log_event* Log_event::read_log_event(IO_CACHE* file, pthread_mutex_t* log_lock) +Log_event* Log_event::read_log_event(IO_CACHE* file, + pthread_mutex_t* log_lock, + bool old_format) #else -Log_event* Log_event::read_log_event(IO_CACHE* file) +Log_event* Log_event::read_log_event(IO_CACHE* file, bool old_format) #endif { - time_t timestamp; - uint32 server_id; - - char buf[LOG_EVENT_HEADER_LEN-4]; -#ifndef MYSQL_CLIENT - if(log_lock) pthread_mutex_lock(log_lock); -#endif - if (my_b_read(file, (byte *) buf, sizeof(buf))) - { - UNLOCK_MUTEX - return NULL; - } - timestamp = uint4korr(buf); - server_id = uint4korr(buf + 5); - - switch(buf[EVENT_TYPE_OFFSET]) - { - case QUERY_EVENT: + char head[LOG_EVENT_HEADER_LEN]; + uint header_size= old_format ? OLD_HEADER_LEN : LOG_EVENT_HEADER_LEN; + + LOCK_MUTEX; + if (my_b_read(file, (byte *) head, header_size)) { - Query_log_event* q = new Query_log_event(file, timestamp, server_id); - UNLOCK_MUTEX - if (!q->query) - { - delete q; - q=NULL; - } - return q; + UNLOCK_MUTEX; + return 0; } - - case LOAD_EVENT: + + uint data_len = uint4korr(head + EVENT_LEN_OFFSET); + char *buf= 0; + const char *error= 0; + Log_event *res= 0; + + if (data_len > max_allowed_packet) { - Load_log_event* l = new Load_log_event(file, timestamp, server_id); - UNLOCK_MUTEX - if (!l->table_name) - { - delete l; - l=NULL; - } - return l; + error = "Event too big"; + goto err; } - - case ROTATE_EVENT: + if (data_len < header_size) { - Rotate_log_event* r = new Rotate_log_event(file, timestamp, server_id); - UNLOCK_MUTEX - if (!r->new_log_ident) - { - delete r; - r=NULL; - } - return r; + error = "Event too small"; + goto err; } - case INTVAR_EVENT: + // some events use the extra byte to null-terminate strings + if (!(buf = my_malloc(data_len+1, MYF(MY_WME)))) { - Intvar_log_event* e = new Intvar_log_event(file, timestamp, server_id); - UNLOCK_MUTEX - if (e->type == INVALID_INT_EVENT) - { - delete e; - e=NULL; - } - return e; + error = "Out of memory"; + goto err; } - - case START_EVENT: - { - Start_log_event* e = new Start_log_event(file, timestamp, server_id); - UNLOCK_MUTEX - return e; - } - case STOP_EVENT: - { - Stop_log_event* e = new Stop_log_event(file, timestamp, server_id); - UNLOCK_MUTEX - return e; - } - default: - break; + buf[data_len] = 0; + memcpy(buf, head, header_size); + if (my_b_read(file, (byte*) buf + header_size, data_len - header_size)) + { + error = "read error"; + goto err; } + if ((res = read_log_event(buf, data_len, &error, old_format))) + res->register_temp_buf(buf); - // default - UNLOCK_MUTEX - return NULL; +err: + UNLOCK_MUTEX; + if (error) + { + sql_print_error("Error in Log_event::read_log_event(): '%s', \ +data_len=%d,event_type=%d",error,data_len,head[EVENT_TYPE_OFFSET]); + my_free(buf, MYF(MY_ALLOW_ZERO_PTR)); + /* + The SQL slave thread will check if file->error<0 to know + if there was an I/O error. Even if there is no "low-level" I/O errors + with 'file', any of the high-level above errors is worrying + enough to stop the SQL thread now ; as we are skipping the current event, + going on with reading and successfully executing other events can + only corrupt the slave's databases. So stop. + */ + file->error= -1; + } + return res; } -Log_event* Log_event::read_log_event(const char* buf, int event_len) + +Log_event* Log_event::read_log_event(const char* buf, int event_len, + const char **error, bool old_format) { - if(event_len < EVENT_LEN_OFFSET || - (uint)event_len != uint4korr(buf+EVENT_LEN_OFFSET)) + if (event_len < EVENT_LEN_OFFSET || + (uint) event_len != uint4korr(buf+EVENT_LEN_OFFSET)) + { + *error="Sanity check failed"; // Needed to free buffer return NULL; // general sanity check - will fail on a partial read + } - switch(buf[EVENT_TYPE_OFFSET]) - { + Log_event* ev = NULL; + + switch(buf[EVENT_TYPE_OFFSET]) { case QUERY_EVENT: - { - Query_log_event* q = new Query_log_event(buf, event_len); - if (!q->query) - { - delete q; - return NULL; - } - - return q; - } - + ev = new Query_log_event(buf, event_len, old_format); + break; case LOAD_EVENT: - { - Load_log_event* l = new Load_log_event(buf, event_len); - if (!l->table_name) - { - delete l; - return NULL; - } - - return l; - } - + ev = new Create_file_log_event(buf, event_len, old_format); + break; + case NEW_LOAD_EVENT: + ev = new Load_log_event(buf, event_len, old_format); + break; case ROTATE_EVENT: - { - Rotate_log_event* r = new Rotate_log_event(buf, event_len); - if (!r->new_log_ident) - { - delete r; - return NULL; - } - - return r; - } - case START_EVENT: return new Start_log_event(buf); - case STOP_EVENT: return new Stop_log_event(buf); - case INTVAR_EVENT: return new Intvar_log_event(buf); + ev = new Rotate_log_event(buf, event_len, old_format); + break; + case SLAVE_EVENT: + ev = new Slave_log_event(buf, event_len); + break; + case CREATE_FILE_EVENT: + ev = new Create_file_log_event(buf, event_len, old_format); + break; + case APPEND_BLOCK_EVENT: + ev = new Append_block_log_event(buf, event_len); + break; + case DELETE_FILE_EVENT: + ev = new Delete_file_log_event(buf, event_len); + break; + case EXEC_LOAD_EVENT: + ev = new Execute_load_log_event(buf, event_len); + break; + case START_EVENT: + ev = new Start_log_event(buf, old_format); + break; + case STOP_EVENT: + ev = new Stop_log_event(buf, old_format); + break; + case INTVAR_EVENT: + ev = new Intvar_log_event(buf, old_format); + break; + case RAND_EVENT: + ev = new Rand_log_event(buf, old_format); + break; default: break; } - return NULL; // default value + if (!ev || !ev->is_valid()) + { + *error= "Found invalid event in binary log"; + delete ev; + return 0; + } + ev->cached_event_len = event_len; + return ev; } + +#ifdef MYSQL_CLIENT void Log_event::print_header(FILE* file) { + char llbuff[22]; fputc('#', file); print_timestamp(file); - fprintf(file, " server id %d ", server_id); + fprintf(file, " server id %d log_pos %s ", server_id, + llstr(log_pos,llbuff)); } void Log_event::print_timestamp(FILE* file, time_t* ts) { -#ifdef MYSQL_SERVER - struct tm tm_tmp; -#endif struct tm *res; if (!ts) - { ts = &when; - } -#ifdef MYSQL_SERVER +#ifdef MYSQL_SERVER // This is always false + struct tm tm_tmp; localtime_r(ts,(res= &tm_tmp)); #else res=localtime(ts); @@ -321,6 +725,7 @@ void Stop_log_event::print(FILE* file, bool short_form, char* last_db) void Rotate_log_event::print(FILE* file, bool short_form, char* last_db) { + char buf[22]; if (short_form) return; @@ -329,134 +734,131 @@ void Rotate_log_event::print(FILE* file, bool short_form, char* last_db) if (new_log_ident) my_fwrite(file, (byte*) new_log_ident, (uint)ident_len, MYF(MY_NABP | MY_WME)); - fprintf(file, "\n"); + fprintf(file, " pos: %s", llstr(pos, buf)); + if (flags & LOG_EVENT_FORCED_ROTATE_F) + fprintf(file," forced by master"); + fputc('\n', file); fflush(file); } -Rotate_log_event::Rotate_log_event(IO_CACHE* file, time_t when_arg, - uint32 server_id): - Log_event(when_arg, 0, 0, server_id),new_log_ident(NULL),alloced(0) -{ - char *tmp_ident; - char buf[4]; - - if (my_b_read(file, (byte*) buf, sizeof(buf))) - return; - ulong event_len; - event_len = uint4korr(buf); - if (event_len < ROTATE_EVENT_OVERHEAD) - return; +#endif /* #ifdef MYSQL_CLIENT */ - ident_len = (uchar)(event_len - ROTATE_EVENT_OVERHEAD); - if (!(tmp_ident = (char*) my_malloc((uint)ident_len, MYF(MY_WME)))) - return; - if (my_b_read( file, (byte*) tmp_ident, (uint) ident_len)) - { - my_free((gptr) tmp_ident, MYF(0)); - return; - } - new_log_ident = tmp_ident; - alloced = 1; -} - -Start_log_event::Start_log_event(const char* buf) :Log_event(buf) +Start_log_event::Start_log_event(const char* buf, + bool old_format) + :Log_event(buf, old_format) { - buf += EVENT_LEN_OFFSET + 4; // skip even length - binlog_version = uint2korr(buf); - memcpy(server_version, buf + 2, sizeof(server_version)); - created = uint4korr(buf + 2 + sizeof(server_version)); + buf += (old_format) ? OLD_HEADER_LEN : LOG_EVENT_HEADER_LEN; + binlog_version = uint2korr(buf+ST_BINLOG_VER_OFFSET); + memcpy(server_version, buf+ST_SERVER_VER_OFFSET, + ST_SERVER_VER_LEN); + created = uint4korr(buf+ST_CREATED_OFFSET); } int Start_log_event::write_data(IO_CACHE* file) { - char buff[sizeof(server_version)+2+4]; - int2store(buff,binlog_version); - memcpy(buff+2,server_version,sizeof(server_version)); - int4store(buff+2+sizeof(server_version),created); - return (my_b_write(file, (byte*) buff, sizeof(buff)) ? -1 : 0); + char buff[START_HEADER_LEN]; + int2store(buff + ST_BINLOG_VER_OFFSET,binlog_version); + memcpy(buff + ST_SERVER_VER_OFFSET,server_version,ST_SERVER_VER_LEN); + int4store(buff + ST_CREATED_OFFSET,created); + return (my_b_safe_write(file, (byte*) buff, sizeof(buff)) ? -1 : 0); } -Rotate_log_event::Rotate_log_event(const char* buf, int event_len): - Log_event(buf),new_log_ident(NULL),alloced(0) + +Rotate_log_event::Rotate_log_event(const char* buf, int event_len, + bool old_format) + :Log_event(buf, old_format),new_log_ident(NULL),alloced(0) { - // the caller will ensure that event_len is what we have at - // EVENT_LEN_OFFSET - if(event_len < ROTATE_EVENT_OVERHEAD) + // The caller will ensure that event_len is what we have at EVENT_LEN_OFFSET + int header_size = (old_format) ? OLD_HEADER_LEN : LOG_EVENT_HEADER_LEN; + uint ident_offset; + if (event_len < header_size) return; - - ident_len = (uchar)(event_len - ROTATE_EVENT_OVERHEAD); - if (!(new_log_ident = (char*) my_memdup((byte*) buf + LOG_EVENT_HEADER_LEN, - (uint) ident_len, MYF(MY_WME)))) + buf += header_size; + if (old_format) + { + ident_len = (uint)(event_len - OLD_HEADER_LEN); + pos = 4; + ident_offset = 0; + } + else + { + ident_len = (uint)(event_len - ROTATE_EVENT_OVERHEAD); + pos = uint8korr(buf + R_POS_OFFSET); + ident_offset = ROTATE_HEADER_LEN; + } + set_if_smaller(ident_len,FN_REFLEN-1); + if (!(new_log_ident= my_strdup_with_length((byte*) buf + + ident_offset, + (uint) ident_len, + MYF(MY_WME)))) return; - alloced = 1; } + int Rotate_log_event::write_data(IO_CACHE* file) { - return my_b_write(file, (byte*) new_log_ident, (uint) ident_len) ? -1 :0; + char buf[ROTATE_HEADER_LEN]; + int8store(buf, pos + R_POS_OFFSET); + return (my_b_safe_write(file, (byte*)buf, ROTATE_HEADER_LEN) || + my_b_safe_write(file, (byte*)new_log_ident, (uint) ident_len)); } -Query_log_event::Query_log_event(IO_CACHE* file, time_t when_arg, - uint32 server_id): - Log_event(when_arg,0,0,server_id),data_buf(0),query(NULL),db(NULL) -{ - char buf[QUERY_HEADER_LEN + 4]; - ulong data_len; - if (my_b_read(file, (byte*) buf, sizeof(buf))) - return; // query == NULL will tell the - // caller there was a problem - data_len = uint4korr(buf); - if (data_len < QUERY_EVENT_OVERHEAD) - return; // tear-drop attack protection :) - - data_len -= QUERY_EVENT_OVERHEAD; - exec_time = uint4korr(buf + 8); - db_len = (uint)buf[12]; - error_code = uint2korr(buf + 13); - - /* Allocate one byte extra for end \0 */ - if (!(data_buf = (char*) my_malloc(data_len+1, MYF(MY_WME)))) - return; - if (my_b_read( file, (byte*) data_buf, data_len)) - { - my_free((gptr) data_buf, MYF(0)); - data_buf = 0; - return; - } - thread_id = uint4korr(buf + 4); - db = data_buf; - query=data_buf + db_len + 1; - q_len = data_len - 1 - db_len; - *((char*) query + q_len) = 0; // Safety +#ifndef MYSQL_CLIENT +Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg, + ulong query_length, bool using_trans) + :Log_event(thd_arg, 0, using_trans), data_buf(0), query(query_arg), + db(thd_arg->db), q_len((uint32) query_length), + error_code(thd_arg->killed ? ER_SERVER_SHUTDOWN: thd_arg->net.last_errno), + thread_id(thd_arg->thread_id) +{ + time_t end_time; + time(&end_time); + exec_time = (ulong) (end_time - thd->start_time); + db_len = (db) ? (uint32) strlen(db) : 0; } +#endif -Query_log_event::Query_log_event(const char* buf, int event_len): - Log_event(buf),data_buf(0), query(NULL), db(NULL) +Query_log_event::Query_log_event(const char* buf, int event_len, + bool old_format) + :Log_event(buf, old_format),data_buf(0), query(NULL), db(NULL) { - if ((uint)event_len < QUERY_EVENT_OVERHEAD) - return; ulong data_len; - buf += EVENT_LEN_OFFSET; - data_len = event_len - QUERY_EVENT_OVERHEAD; + if (old_format) + { + if ((uint)event_len < OLD_HEADER_LEN + QUERY_HEADER_LEN) + return; + data_len = event_len - (QUERY_HEADER_LEN + OLD_HEADER_LEN); + buf += OLD_HEADER_LEN; + } + else + { + if ((uint)event_len < QUERY_EVENT_OVERHEAD) + return; + data_len = event_len - QUERY_EVENT_OVERHEAD; + buf += LOG_EVENT_HEADER_LEN; + } - exec_time = uint4korr(buf + 8); - error_code = uint2korr(buf + 13); + exec_time = uint4korr(buf + Q_EXEC_TIME_OFFSET); + error_code = uint2korr(buf + Q_ERR_CODE_OFFSET); if (!(data_buf = (char*) my_malloc(data_len + 1, MYF(MY_WME)))) return; - memcpy(data_buf, buf + QUERY_HEADER_LEN + 4, data_len); - thread_id = uint4korr(buf + 4); + memcpy(data_buf, buf + Q_DATA_OFFSET, data_len); + thread_id = uint4korr(buf + Q_THREAD_ID_OFFSET); db = data_buf; - db_len = (uint)buf[12]; + db_len = (uint)buf[Q_DB_LEN_OFFSET]; query=data_buf + db_len + 1; q_len = data_len - 1 - db_len; *((char*)query+q_len) = 0; } + +#ifdef MYSQL_CLIENT + void Query_log_event::print(FILE* file, bool short_form, char* last_db) { char buff[40],*end; // Enough for SET TIMESTAMP @@ -469,11 +871,11 @@ void Query_log_event::print(FILE* file, bool short_form, char* last_db) bool same_db = 0; - if(db && last_db) - { - if(!(same_db = !memcmp(last_db, db, db_len + 1))) - memcpy(last_db, db, db_len + 1); - } + if (db && last_db) + { + if (!(same_db = !memcmp(last_db, db, db_len + 1))) + memcpy(last_db, db, db_len + 1); + } if (db && db[0] && !same_db) fprintf(file, "use %s;\n", db); @@ -484,170 +886,371 @@ void Query_log_event::print(FILE* file, bool short_form, char* last_db) my_fwrite(file, (byte*) query, q_len, MYF(MY_NABP | MY_WME)); fprintf(file, ";\n"); } +#endif + int Query_log_event::write_data(IO_CACHE* file) { - if (!query) return -1; + if (!query) + return -1; char buf[QUERY_HEADER_LEN]; - char* pos = buf; - int4store(pos, thread_id); - pos += 4; - int4store(pos, exec_time); - pos += 4; - *pos++ = (char)db_len; - int2store(pos, error_code); - pos += 2; - - return (my_b_write(file, (byte*) buf, (uint)(pos - buf)) || - my_b_write(file, (db) ? (byte*) db : (byte*)"", db_len + 1) || - my_b_write(file, (byte*) query, q_len)) ? -1 : 0; + int4store(buf + Q_THREAD_ID_OFFSET, thread_id); + int4store(buf + Q_EXEC_TIME_OFFSET, exec_time); + buf[Q_DB_LEN_OFFSET] = (char) db_len; + int2store(buf + Q_ERR_CODE_OFFSET, error_code); + + return (my_b_safe_write(file, (byte*) buf, QUERY_HEADER_LEN) || + my_b_safe_write(file, (db) ? (byte*) db : (byte*)"", db_len + 1) || + my_b_safe_write(file, (byte*) query, q_len)) ? -1 : 0; } -Intvar_log_event:: Intvar_log_event(IO_CACHE* file, time_t when_arg, - uint32 server_id) - :Log_event(when_arg,0,0,server_id), type(INVALID_INT_EVENT) +Intvar_log_event::Intvar_log_event(const char* buf, bool old_format) + :Log_event(buf, old_format) { - char buf[9+4]; - if (!my_b_read(file, (byte*) buf, sizeof(buf))) - { - type = buf[4]; - val = uint8korr(buf+1+4); - } + buf += (old_format) ? OLD_HEADER_LEN : LOG_EVENT_HEADER_LEN; + type = buf[I_TYPE_OFFSET]; + val = uint8korr(buf+I_VAL_OFFSET); } -Intvar_log_event::Intvar_log_event(const char* buf):Log_event(buf) +const char* Intvar_log_event::get_var_type_name() { - buf += LOG_EVENT_HEADER_LEN; - type = buf[0]; - val = uint8korr(buf+1); + switch(type) { + case LAST_INSERT_ID_EVENT: return "LAST_INSERT_ID"; + case INSERT_ID_EVENT: return "INSERT_ID"; + default: /* impossible */ return "UNKNOWN"; + } } int Intvar_log_event::write_data(IO_CACHE* file) { char buf[9]; - buf[0] = type; - int8store(buf + 1, val); - return my_b_write(file, (byte*) buf, sizeof(buf)); + buf[I_TYPE_OFFSET] = type; + int8store(buf + I_VAL_OFFSET, val); + return my_b_safe_write(file, (byte*) buf, sizeof(buf)); } +#ifdef MYSQL_CLIENT void Intvar_log_event::print(FILE* file, bool short_form, char* last_db) { char llbuff[22]; - if(!short_form) + const char *msg; + LINT_INIT(msg); + + if (!short_form) { print_header(file); fprintf(file, "\tIntvar\n"); } fprintf(file, "SET "); - switch(type) - { + switch (type) { case LAST_INSERT_ID_EVENT: - fprintf(file, "LAST_INSERT_ID = "); + msg="LAST_INSERT_ID"; break; case INSERT_ID_EVENT: - fprintf(file, "INSERT_ID = "); + msg="INSERT_ID"; break; } - fprintf(file, "%s;\n", llstr(val,llbuff)); + fprintf(file, "%s=%s;\n", msg, llstr(val,llbuff)); fflush(file); - +} +#endif + +/***************************************************************************** + * + * Rand log event + * + ****************************************************************************/ +Rand_log_event::Rand_log_event(const char* buf, bool old_format) + :Log_event(buf, old_format) +{ + buf += (old_format) ? OLD_HEADER_LEN : LOG_EVENT_HEADER_LEN; + seed1 = uint8korr(buf+RAND_SEED1_OFFSET); + seed2 = uint8korr(buf+RAND_SEED2_OFFSET); +} + +int Rand_log_event::write_data(IO_CACHE* file) +{ + char buf[16]; + int8store(buf + RAND_SEED1_OFFSET, seed1); + int8store(buf + RAND_SEED2_OFFSET, seed2); + return my_b_safe_write(file, (byte*) buf, sizeof(buf)); } -int Load_log_event::write_data(IO_CACHE* file) +#ifdef MYSQL_CLIENT +void Rand_log_event::print(FILE* file, bool short_form, char* last_db) +{ + char llbuff[22],llbuff2[22]; + if (!short_form) + { + print_header(file); + fprintf(file, "\tRand\n"); + } + fprintf(file, "SET @@RAND_SEED1=%s, @@RAND_SEED2=%s;\n", + llstr(seed1, llbuff),llstr(seed2, llbuff2)); + fflush(file); +} +#endif + +int Load_log_event::write_data_header(IO_CACHE* file) { char buf[LOAD_HEADER_LEN]; - int4store(buf, thread_id); - int4store(buf + 4, exec_time); - int4store(buf + 8, skip_lines); - buf[12] = (char)table_name_len; - buf[13] = (char)db_len; - int4store(buf + 14, num_fields); - - if(my_b_write(file, (byte*)buf, sizeof(buf)) || - my_b_write(file, (byte*)&sql_ex, sizeof(sql_ex))) - return 1; + int4store(buf + L_THREAD_ID_OFFSET, thread_id); + int4store(buf + L_EXEC_TIME_OFFSET, exec_time); + int4store(buf + L_SKIP_LINES_OFFSET, skip_lines); + buf[L_TBL_LEN_OFFSET] = (char)table_name_len; + buf[L_DB_LEN_OFFSET] = (char)db_len; + int4store(buf + L_NUM_FIELDS_OFFSET, num_fields); + return my_b_safe_write(file, (byte*)buf, LOAD_HEADER_LEN); +} +int Load_log_event::write_data_body(IO_CACHE* file) +{ + if (sql_ex.write_data(file)) + return 1; if (num_fields && fields && field_lens) { - if(my_b_write(file, (byte*)field_lens, num_fields) || - my_b_write(file, (byte*)fields, field_block_len)) + if (my_b_safe_write(file, (byte*)field_lens, num_fields) || + my_b_safe_write(file, (byte*)fields, field_block_len)) return 1; } - if(my_b_write(file, (byte*)table_name, table_name_len + 1) || - my_b_write(file, (byte*)db, db_len + 1) || - my_b_write(file, (byte*)fname, fname_len)) + return (my_b_safe_write(file, (byte*)table_name, table_name_len + 1) || + my_b_safe_write(file, (byte*)db, db_len + 1) || + my_b_safe_write(file, (byte*)fname, fname_len)); +} + + + +static bool write_str(IO_CACHE *file, char *str, byte length) +{ + return (my_b_safe_write(file, &length, 1) || + my_b_safe_write(file, (byte*) str, (int) length)); +} + + +int sql_ex_info::write_data(IO_CACHE* file) +{ + if (new_format()) + { + return (write_str(file, field_term, field_term_len) || + write_str(file, enclosed, enclosed_len) || + write_str(file, line_term, line_term_len) || + write_str(file, line_start, line_start_len) || + write_str(file, escaped, escaped_len) || + my_b_safe_write(file,(byte*) &opt_flags,1)); + } + else + { + old_sql_ex old_ex; + old_ex.field_term= *field_term; + old_ex.enclosed= *enclosed; + old_ex.line_term= *line_term; + old_ex.line_start= *line_start; + old_ex.escaped= *escaped; + old_ex.opt_flags= opt_flags; + old_ex.empty_flags=empty_flags; + return my_b_safe_write(file, (byte*) &old_ex, sizeof(old_ex)); + } +} + + +static inline int read_str(char * &buf, char *buf_end, char * &str, + uint8 &len) +{ + if (buf + (uint) (uchar) *buf >= buf_end) return 1; + len = (uint8) *buf; + str= buf+1; + buf+= (uint) len+1; return 0; } -Load_log_event::Load_log_event(IO_CACHE* file, time_t when, uint32 server_id): - Log_event(when,0,0,server_id),data_buf(0),num_fields(0), - fields(0),field_lens(0),field_block_len(0), - table_name(0),db(0),fname(0) + +char* sql_ex_info::init(char* buf,char* buf_end,bool use_new_format) { - char buf[LOAD_HEADER_LEN + 4]; - ulong data_len; - if (my_b_read(file, (byte*)buf, sizeof(buf)) || - my_b_read(file, (byte*)&sql_ex, sizeof(sql_ex))) - return; + cached_new_format = use_new_format; + if (use_new_format) + { + empty_flags=0; + /* + The code below assumes that buf will not disappear from + under our feet during the lifetime of the event. This assumption + holds true in the slave thread if the log is in new format, but is not + the case when we have old format because we will be reusing net buffer + to read the actual file before we write out the Create_file event. + */ + if (read_str(buf, buf_end, field_term, field_term_len) || + read_str(buf, buf_end, enclosed, enclosed_len) || + read_str(buf, buf_end, line_term, line_term_len) || + read_str(buf, buf_end, line_start, line_start_len) || + read_str(buf, buf_end, escaped, escaped_len)) + return 0; + opt_flags = *buf++; + } + else + { + field_term_len= enclosed_len= line_term_len= line_start_len= escaped_len=1; + field_term = buf++; // Use first byte in string + enclosed= buf++; + line_term= buf++; + line_start= buf++; + escaped= buf++; + opt_flags = *buf++; + empty_flags= *buf++; + if (empty_flags & FIELD_TERM_EMPTY) + field_term_len=0; + if (empty_flags & ENCLOSED_EMPTY) + enclosed_len=0; + if (empty_flags & LINE_TERM_EMPTY) + line_term_len=0; + if (empty_flags & LINE_START_EMPTY) + line_start_len=0; + if (empty_flags & ESCAPED_EMPTY) + escaped_len=0; + } + return buf; +} - data_len = uint4korr(buf) - LOAD_EVENT_OVERHEAD; - if (!(data_buf = (char*)my_malloc(data_len + 1, MYF(MY_WME)))) - return; - if (my_b_read(file, (byte*)data_buf, data_len)) - return; - copy_log_event(buf,data_len); + +#ifndef MYSQL_CLIENT +Load_log_event::Load_log_event(THD* thd_arg, sql_exchange* ex, + const char* db_arg, const char* table_name_arg, + List<Item>& fields_arg, + enum enum_duplicates handle_dup, + bool using_trans) + :Log_event(thd_arg, 0, using_trans),thread_id(thd_arg->thread_id), + num_fields(0),fields(0), + field_lens(0),field_block_len(0), + table_name(table_name_arg ? table_name_arg : ""), + db(db_arg), fname(ex->file_name) +{ + time_t end_time; + time(&end_time); + exec_time = (ulong) (end_time - thd_arg->start_time); + /* db can never be a zero pointer in 4.0 */ + db_len = (uint32) strlen(db); + table_name_len = (uint32) strlen(table_name); + fname_len = (fname) ? (uint) strlen(fname) : 0; + sql_ex.field_term = (char*) ex->field_term->ptr(); + sql_ex.field_term_len = (uint8) ex->field_term->length(); + sql_ex.enclosed = (char*) ex->enclosed->ptr(); + sql_ex.enclosed_len = (uint8) ex->enclosed->length(); + sql_ex.line_term = (char*) ex->line_term->ptr(); + sql_ex.line_term_len = (uint8) ex->line_term->length(); + sql_ex.line_start = (char*) ex->line_start->ptr(); + sql_ex.line_start_len = (uint8) ex->line_start->length(); + sql_ex.escaped = (char*) ex->escaped->ptr(); + sql_ex.escaped_len = (uint8) ex->escaped->length(); + sql_ex.opt_flags = 0; + sql_ex.cached_new_format = -1; + + if (ex->dumpfile) + sql_ex.opt_flags |= DUMPFILE_FLAG; + if (ex->opt_enclosed) + sql_ex.opt_flags |= OPT_ENCLOSED_FLAG; + + sql_ex.empty_flags = 0; + + switch (handle_dup) { + case DUP_IGNORE: sql_ex.opt_flags |= IGNORE_FLAG; break; + case DUP_REPLACE: sql_ex.opt_flags |= REPLACE_FLAG; break; + case DUP_ERROR: break; + } + + if (!ex->field_term->length()) + sql_ex.empty_flags |= FIELD_TERM_EMPTY; + if (!ex->enclosed->length()) + sql_ex.empty_flags |= ENCLOSED_EMPTY; + if (!ex->line_term->length()) + sql_ex.empty_flags |= LINE_TERM_EMPTY; + if (!ex->line_start->length()) + sql_ex.empty_flags |= LINE_START_EMPTY; + if (!ex->escaped->length()) + sql_ex.empty_flags |= ESCAPED_EMPTY; + + skip_lines = ex->skip_lines; + + List_iterator<Item> li(fields_arg); + field_lens_buf.length(0); + fields_buf.length(0); + Item* item; + while ((item = li++)) + { + num_fields++; + uchar len = (uchar) strlen(item->name); + field_block_len += len + 1; + fields_buf.append(item->name, len + 1); + field_lens_buf.append((char*)&len, 1); + } + + field_lens = (const uchar*)field_lens_buf.ptr(); + fields = fields_buf.ptr(); } -Load_log_event::Load_log_event(const char* buf, int event_len): - Log_event(buf),data_buf(0),num_fields(0),fields(0), +#endif + +/* + The caller must do buf[event_len] = 0 before he starts using the + constructed event. +*/ + +Load_log_event::Load_log_event(const char* buf, int event_len, + bool old_format) + :Log_event(buf, old_format),num_fields(0),fields(0), field_lens(0),field_block_len(0), table_name(0),db(0),fname(0) { - ulong data_len; - - if((uint)event_len < (LOAD_EVENT_OVERHEAD + LOG_EVENT_HEADER_LEN)) + if (!event_len) // derived class, will call copy_log_event() itself return; - buf += EVENT_LEN_OFFSET; - memcpy(&sql_ex, buf + LOAD_HEADER_LEN + 4, sizeof(sql_ex)); - data_len = event_len; - - if(!(data_buf = (char*)my_malloc(data_len + 1, MYF(MY_WME)))) - return; - memcpy(data_buf, buf + 22 + sizeof(sql_ex), data_len); - copy_log_event(buf, data_len); + copy_log_event(buf, event_len, old_format); } -void Load_log_event::copy_log_event(const char *buf, ulong data_len) +int Load_log_event::copy_log_event(const char *buf, ulong event_len, + bool old_format) { - thread_id = uint4korr(buf+4); - exec_time = uint4korr(buf+8); - skip_lines = uint4korr(buf + 12); - table_name_len = (uint)buf[16]; - db_len = (uint)buf[17]; - num_fields = uint4korr(buf + 18); + uint data_len; + char* buf_end = (char*)buf + event_len; + uint header_len= old_format ? OLD_HEADER_LEN : LOG_EVENT_HEADER_LEN; + const char* data_head = buf + header_len; + thread_id = uint4korr(data_head + L_THREAD_ID_OFFSET); + exec_time = uint4korr(data_head + L_EXEC_TIME_OFFSET); + skip_lines = uint4korr(data_head + L_SKIP_LINES_OFFSET); + table_name_len = (uint)data_head[L_TBL_LEN_OFFSET]; + db_len = (uint)data_head[L_DB_LEN_OFFSET]; + num_fields = uint4korr(data_head + L_NUM_FIELDS_OFFSET); + int body_offset = ((buf[EVENT_TYPE_OFFSET] == LOAD_EVENT) ? + LOAD_HEADER_LEN + header_len : + get_data_body_offset()); + + if ((int) event_len < body_offset) + return 1; + /* + Sql_ex.init() on success returns the pointer to the first byte after + the sql_ex structure, which is the start of field lengths array. + */ + if (!(field_lens=(uchar*)sql_ex.init((char*)buf + body_offset, + buf_end, + buf[EVENT_TYPE_OFFSET] != LOAD_EVENT))) + return 1; + + data_len = event_len - body_offset; if (num_fields > data_len) // simple sanity check against corruption - return; - - field_lens = (uchar*) data_buf; - uint i; - for (i = 0; i < num_fields; i++) - { + return 1; + for (uint i = 0; i < num_fields; i++) field_block_len += (uint)field_lens[i] + 1; - } + fields = (char*)field_lens + num_fields; - - *((char*)data_buf+data_len) = 0; table_name = fields + field_block_len; db = table_name + table_name_len + 1; fname = db + db_len + 1; - fname_len = data_len - 2 - db_len - table_name_len - num_fields - - field_block_len; + fname_len = strlen(fname); + // null termination is accomplished by the caller doing buf[event_len]=0 + return 0; } +#ifdef MYSQL_CLIENT void Load_log_event::print(FILE* file, bool short_form, char* last_db) { @@ -659,67 +1262,66 @@ void Load_log_event::print(FILE* file, bool short_form, char* last_db) } bool same_db = 0; - - if(db && last_db) - { - if(!(same_db = !memcmp(last_db, db, db_len + 1))) - memcpy(last_db, db, db_len + 1); - } + if (db && last_db) + { + if (!(same_db = !memcmp(last_db, db, db_len + 1))) + memcpy(last_db, db, db_len + 1); + } - if(db && db[0] && !same_db) + if (db && db[0] && !same_db) fprintf(file, "use %s;\n", db); - fprintf(file, "LOAD DATA INFILE '%s' ", fname); + fprintf(file, "LOAD DATA INFILE '%-*s' ", fname_len, fname); - if(sql_ex.opt_flags && REPLACE_FLAG ) + if (sql_ex.opt_flags && REPLACE_FLAG ) fprintf(file," REPLACE "); - else if(sql_ex.opt_flags && IGNORE_FLAG ) + else if (sql_ex.opt_flags && IGNORE_FLAG ) fprintf(file," IGNORE "); fprintf(file, "INTO TABLE %s ", table_name); - if(!(sql_ex.empty_flags & FIELD_TERM_EMPTY)) + if (sql_ex.field_term) { fprintf(file, " FIELDS TERMINATED BY "); - pretty_print_char(file, sql_ex.field_term); + pretty_print_str(file, sql_ex.field_term, sql_ex.field_term_len); } - if(!(sql_ex.empty_flags & ENCLOSED_EMPTY)) + if (sql_ex.enclosed) { - if(sql_ex.opt_flags && OPT_ENCLOSED_FLAG ) + if (sql_ex.opt_flags && OPT_ENCLOSED_FLAG ) fprintf(file," OPTIONALLY "); fprintf(file, " ENCLOSED BY "); - pretty_print_char(file, sql_ex.enclosed); + pretty_print_str(file, sql_ex.enclosed, sql_ex.enclosed_len); } - if(!(sql_ex.empty_flags & ESCAPED_EMPTY)) + if (sql_ex.escaped) { fprintf(file, " ESCAPED BY "); - pretty_print_char(file, sql_ex.escaped); + pretty_print_str(file, sql_ex.escaped, sql_ex.escaped_len); } - if(!(sql_ex.empty_flags & LINE_TERM_EMPTY)) + if (sql_ex.line_term) { fprintf(file," LINES TERMINATED BY "); - pretty_print_char(file, sql_ex.line_term); + pretty_print_str(file, sql_ex.line_term, sql_ex.line_term_len); } - if(!(sql_ex.empty_flags & LINE_START_EMPTY)) + if (sql_ex.line_start) { fprintf(file," LINES STARTING BY "); - pretty_print_char(file, sql_ex.line_start); + pretty_print_str(file, sql_ex.line_start, sql_ex.line_start_len); } - if((int)skip_lines > 0) + if ((int)skip_lines > 0) fprintf(file, " IGNORE %ld LINES ", (long) skip_lines); if (num_fields) { uint i; const char* field = fields; - fprintf( file, " ("); - for(i = 0; i < num_fields; i++) + fprintf(file, " ("); + for (i = 0; i < num_fields; i++) { - if(i) + if (i) fputc(',', file); fprintf(file, field); @@ -731,18 +1333,889 @@ void Load_log_event::print(FILE* file, bool short_form, char* last_db) fprintf(file, ";\n"); } +#endif /* #ifdef MYSQL_CLIENT */ + #ifndef MYSQL_CLIENT -void Load_log_event::set_fields(List<Item> &fields) +void Log_event::set_log_pos(MYSQL_LOG* log) +{ + if (!log_pos) + log_pos = my_b_tell(&log->log_file); +} + + +void Load_log_event::set_fields(List<Item> &field_list) { uint i; - const char* field = this->fields; - for(i = 0; i < num_fields; i++) + const char *field= fields; + for (i= 0; i < num_fields; i++) + { + field_list.push_back(new Item_field(db, table_name, field)); + field+= field_lens[i] + 1; + } +} + + +Slave_log_event::Slave_log_event(THD* thd_arg, + struct st_relay_log_info* rli): + Log_event(thd_arg,0,0),mem_pool(0),master_host(0) +{ + DBUG_ENTER("Slave_log_event"); + if (!rli->inited) // QQ When can this happen ? + DBUG_VOID_RETURN; + + MASTER_INFO* mi = rli->mi; + // TODO: re-write this better without holding both locks at the same time + pthread_mutex_lock(&mi->data_lock); + pthread_mutex_lock(&rli->data_lock); + master_host_len = strlen(mi->host); + master_log_len = strlen(rli->master_log_name); + // on OOM, just do not initialize the structure and print the error + if ((mem_pool = (char*)my_malloc(get_data_size() + 1, + MYF(MY_WME)))) + { + master_host = mem_pool + SL_MASTER_HOST_OFFSET ; + memcpy(master_host, mi->host, master_host_len + 1); + master_log = master_host + master_host_len + 1; + memcpy(master_log, rli->master_log_name, master_log_len + 1); + master_port = mi->port; + master_pos = rli->master_log_pos; + DBUG_PRINT("info", ("master_log: %s pos: %d", master_log, + (ulong) master_pos)); + } + else + sql_print_error("Out of memory while recording slave event"); + pthread_mutex_unlock(&rli->data_lock); + pthread_mutex_unlock(&mi->data_lock); + DBUG_VOID_RETURN; +} + +#endif /* ! MYSQL_CLIENT */ + + +Slave_log_event::~Slave_log_event() +{ + my_free(mem_pool, MYF(MY_ALLOW_ZERO_PTR)); +} + +#ifdef MYSQL_CLIENT + +void Slave_log_event::print(FILE* file, bool short_form, char* last_db) +{ + char llbuff[22]; + if (short_form) + return; + print_header(file); + fputc('\n', file); + fprintf(file, "Slave: master_host: '%s' master_port: %d \ +master_log: '%s' master_pos: %s\n", + master_host, master_port, master_log, llstr(master_pos, llbuff)); +} + +#endif /* MYSQL_CLIENT */ + +int Slave_log_event::get_data_size() +{ + return master_host_len + master_log_len + 1 + SL_MASTER_HOST_OFFSET; +} + +int Slave_log_event::write_data(IO_CACHE* file) +{ + int8store(mem_pool + SL_MASTER_POS_OFFSET, master_pos); + int2store(mem_pool + SL_MASTER_PORT_OFFSET, master_port); + // log and host are already there + return my_b_safe_write(file, (byte*)mem_pool, get_data_size()); +} + + +void Slave_log_event::init_from_mem_pool(int data_size) +{ + master_pos = uint8korr(mem_pool + SL_MASTER_POS_OFFSET); + master_port = uint2korr(mem_pool + SL_MASTER_PORT_OFFSET); + master_host = mem_pool + SL_MASTER_HOST_OFFSET; + master_host_len = strlen(master_host); + // safety + master_log = master_host + master_host_len + 1; + if (master_log > mem_pool + data_size) + { + master_host = 0; + return; + } + master_log_len = strlen(master_log); +} + +Slave_log_event::Slave_log_event(const char* buf, int event_len) + :Log_event(buf,0),mem_pool(0),master_host(0) +{ + event_len -= LOG_EVENT_HEADER_LEN; + if (event_len < 0) + return; + if (!(mem_pool = (char*) my_malloc(event_len + 1, MYF(MY_WME)))) + return; + memcpy(mem_pool, buf + LOG_EVENT_HEADER_LEN, event_len); + mem_pool[event_len] = 0; + init_from_mem_pool(event_len); +} + +#ifndef MYSQL_CLIENT +Create_file_log_event:: +Create_file_log_event(THD* thd_arg, sql_exchange* ex, + const char* db_arg, const char* table_name_arg, + List<Item>& fields_arg, enum enum_duplicates handle_dup, + char* block_arg, uint block_len_arg, bool using_trans) + :Load_log_event(thd_arg,ex,db_arg,table_name_arg,fields_arg,handle_dup, + using_trans), + fake_base(0),block(block_arg),block_len(block_len_arg), + file_id(thd_arg->file_id = mysql_bin_log.next_file_id()) +{ + sql_ex.force_new_format(); +} +#endif + +int Create_file_log_event::write_data_body(IO_CACHE* file) +{ + int res; + if ((res = Load_log_event::write_data_body(file)) || fake_base) + return res; + return (my_b_safe_write(file, (byte*) "", 1) || + my_b_safe_write(file, (byte*) block, block_len)); +} + +int Create_file_log_event::write_data_header(IO_CACHE* file) +{ + int res; + if ((res = Load_log_event::write_data_header(file)) || fake_base) + return res; + byte buf[CREATE_FILE_HEADER_LEN]; + int4store(buf + CF_FILE_ID_OFFSET, file_id); + return my_b_safe_write(file, buf, CREATE_FILE_HEADER_LEN); +} + +int Create_file_log_event::write_base(IO_CACHE* file) +{ + int res; + fake_base = 1; // pretend we are Load event + res = write(file); + fake_base = 0; + return res; +} + +Create_file_log_event::Create_file_log_event(const char* buf, int len, + bool old_format) + :Load_log_event(buf,0,old_format),fake_base(0),block(0),inited_from_old(0) +{ + int block_offset; + if (copy_log_event(buf,len,old_format)) + return; + if (!old_format) + { + file_id = uint4korr(buf + LOG_EVENT_HEADER_LEN + + + LOAD_HEADER_LEN + CF_FILE_ID_OFFSET); + // + 1 for \0 terminating fname + block_offset = (LOG_EVENT_HEADER_LEN + Load_log_event::get_data_size() + + CREATE_FILE_HEADER_LEN + 1); + if (len < block_offset) + return; + block = (char*)buf + block_offset; + block_len = len - block_offset; + } + else + { + sql_ex.force_new_format(); + inited_from_old = 1; + } +} + + +#ifdef MYSQL_CLIENT +void Create_file_log_event::print(FILE* file, bool short_form, + char* last_db) +{ + if (short_form) + return; + Load_log_event::print(file, 1, last_db); + fprintf(file, " file_id: %d block_len: %d\n", file_id, block_len); +} +#endif + +#ifndef MYSQL_CLIENT +void Create_file_log_event::pack_info(String* packet) +{ + char buf1[256],buf[22], *end; + String tmp(buf1, sizeof(buf1)); + tmp.length(0); + tmp.append("db="); + tmp.append(db, db_len); + tmp.append(";table="); + tmp.append(table_name, table_name_len); + tmp.append(";file_id="); + end= int10_to_str((long) file_id, buf, 10); + tmp.append(buf, (uint32) (end-buf)); + tmp.append(";block_len="); + end= int10_to_str((long) block_len, buf, 10); + tmp.append(buf, (uint32) (end-buf)); + net_store_data(packet, (char*) tmp.ptr(), tmp.length()); +} +#endif + +#ifndef MYSQL_CLIENT +Append_block_log_event::Append_block_log_event(THD* thd_arg, char* block_arg, + uint block_len_arg, + bool using_trans) + :Log_event(thd_arg,0, using_trans), block(block_arg), + block_len(block_len_arg), file_id(thd_arg->file_id) +{ +} +#endif + +Append_block_log_event::Append_block_log_event(const char* buf, int len) + :Log_event(buf, 0),block(0) +{ + if ((uint)len < APPEND_BLOCK_EVENT_OVERHEAD) + return; + file_id = uint4korr(buf + LOG_EVENT_HEADER_LEN + AB_FILE_ID_OFFSET); + block = (char*)buf + APPEND_BLOCK_EVENT_OVERHEAD; + block_len = len - APPEND_BLOCK_EVENT_OVERHEAD; +} + +int Append_block_log_event::write_data(IO_CACHE* file) +{ + byte buf[APPEND_BLOCK_HEADER_LEN]; + int4store(buf + AB_FILE_ID_OFFSET, file_id); + return (my_b_safe_write(file, buf, APPEND_BLOCK_HEADER_LEN) || + my_b_safe_write(file, (byte*) block, block_len)); +} + +#ifdef MYSQL_CLIENT +void Append_block_log_event::print(FILE* file, bool short_form, + char* last_db) +{ + if (short_form) + return; + print_header(file); + fputc('\n', file); + fprintf(file, "#Append_block: file_id: %d block_len: %d\n", + file_id, block_len); +} +#endif + +#ifndef MYSQL_CLIENT +void Append_block_log_event::pack_info(String* packet) +{ + char buf1[256]; + sprintf(buf1, ";file_id=%u;block_len=%u", file_id, block_len); + net_store_data(packet, buf1); +} + +Delete_file_log_event::Delete_file_log_event(THD* thd_arg, bool using_trans) + :Log_event(thd_arg, 0, using_trans), file_id(thd_arg->file_id) +{ +} +#endif + + +Delete_file_log_event::Delete_file_log_event(const char* buf, int len) + :Log_event(buf, 0),file_id(0) +{ + if ((uint)len < DELETE_FILE_EVENT_OVERHEAD) + return; + file_id = uint4korr(buf + LOG_EVENT_HEADER_LEN + AB_FILE_ID_OFFSET); +} + + +int Delete_file_log_event::write_data(IO_CACHE* file) +{ + byte buf[DELETE_FILE_HEADER_LEN]; + int4store(buf + DF_FILE_ID_OFFSET, file_id); + return my_b_safe_write(file, buf, DELETE_FILE_HEADER_LEN); +} + +#ifdef MYSQL_CLIENT +void Delete_file_log_event::print(FILE* file, bool short_form, + char* last_db) +{ + if (short_form) + return; + print_header(file); + fputc('\n', file); + fprintf(file, "#Delete_file: file_id=%u\n", file_id); +} +#endif + +#ifndef MYSQL_CLIENT +void Delete_file_log_event::pack_info(String* packet) +{ + char buf1[64]; + sprintf(buf1, ";file_id=%u", (uint) file_id); + net_store_data(packet, buf1); +} +#endif + + +#ifndef MYSQL_CLIENT +Execute_load_log_event::Execute_load_log_event(THD* thd_arg, bool using_trans) + :Log_event(thd_arg, 0, using_trans), file_id(thd_arg->file_id) +{ +} +#endif + +Execute_load_log_event::Execute_load_log_event(const char* buf, int len) + :Log_event(buf, 0), file_id(0) +{ + if ((uint)len < EXEC_LOAD_EVENT_OVERHEAD) + return; + file_id = uint4korr(buf + LOG_EVENT_HEADER_LEN + EL_FILE_ID_OFFSET); +} + +int Execute_load_log_event::write_data(IO_CACHE* file) +{ + byte buf[EXEC_LOAD_HEADER_LEN]; + int4store(buf + EL_FILE_ID_OFFSET, file_id); + return my_b_safe_write(file, buf, EXEC_LOAD_HEADER_LEN); +} + +#ifdef MYSQL_CLIENT +void Execute_load_log_event::print(FILE* file, bool short_form, + char* last_db) +{ + if (short_form) + return; + print_header(file); + fputc('\n', file); + fprintf(file, "#Exec_load: file_id=%d\n", + file_id); +} +#endif +#ifndef MYSQL_CLIENT +void Execute_load_log_event::pack_info(String* packet) +{ + char buf[64]; + sprintf(buf, ";file_id=%u", (uint) file_id); + net_store_data(packet, buf); +} +#endif + +#ifndef MYSQL_CLIENT +int Query_log_event::exec_event(struct st_relay_log_info* rli) +{ + int expected_error,actual_error = 0; + init_sql_alloc(&thd->mem_root, 8192,0); + thd->db = rewrite_db((char*)db); + + /* + InnoDB internally stores the master log position it has processed so far; + position to store is really pos + pending + event_len + since we must store the pos of the END of the current log event + */ + rli->event_len= get_event_len(); + + if (db_ok(thd->db, replicate_do_db, replicate_ignore_db)) + { + thd->set_time((time_t)when); + thd->current_tablenr = 0; + thd->query_length= q_len; + VOID(pthread_mutex_lock(&LOCK_thread_count)); + thd->query = (char*)query; + thd->query_id = query_id++; + VOID(pthread_mutex_unlock(&LOCK_thread_count)); + thd->query_error = 0; // clear error + thd->net.last_errno = 0; + thd->net.last_error[0] = 0; + thd->slave_proxy_id = thread_id; // for temp tables + + /* + Sanity check to make sure the master did not get a really bad + error on the query. + */ + if (ignored_error_code((expected_error = error_code)) || + !check_expected_error(thd,rli,expected_error)) { - fields.push_back(new Item_field(db, table_name, field)); - field += field_lens[i] + 1; + mysql_log.write(thd,COM_QUERY,"%s",thd->query); + DBUG_PRINT("query",("%s",thd->query)); + mysql_parse(thd, thd->query, q_len); + + /* + Set a flag if we are inside an transaction so that we can restart + the transaction from the start if we are killed + + This will only be done if we are supporting transactional tables + in the slave. + */ + if (!strcmp(thd->query,"BEGIN")) + rli->inside_transaction= opt_using_transactions; + else if (!strcmp(thd->query,"COMMIT")) + rli->inside_transaction=0; + + if ((expected_error != (actual_error = thd->net.last_errno)) && + expected_error && + !ignored_error_code(actual_error) && + !ignored_error_code(expected_error)) + { + const char* errmsg = "Slave: did not get the expected error\ + running query from master - expected: '%s' (%d), got '%s' (%d)"; + sql_print_error(errmsg, ER_SAFE(expected_error), + expected_error, + actual_error ? thd->net.last_error: "no error", + actual_error); + thd->query_error = 1; + } + else if (expected_error == actual_error || + ignored_error_code(actual_error)) + { + thd->query_error = 0; + *rli->last_slave_error = 0; + rli->last_slave_errno = 0; + } + } + else + { + // master could be inconsistent, abort and tell DBA to check/fix it + VOID(pthread_mutex_lock(&LOCK_thread_count)); + thd->db = thd->query = 0; + VOID(pthread_mutex_unlock(&LOCK_thread_count)); + thd->variables.convert_set = 0; + close_thread_tables(thd); + free_root(&thd->mem_root,0); + return 1; } + } + thd->db= 0; // prevent db from being freed + VOID(pthread_mutex_lock(&LOCK_thread_count)); + thd->query= 0; // just to be sure + VOID(pthread_mutex_unlock(&LOCK_thread_count)); + // assume no convert for next query unless set explictly + thd->variables.convert_set = 0; + close_thread_tables(thd); + + if (thd->query_error || thd->fatal_error) + { + slave_print_error(rli,actual_error, "error '%s' on query '%s'", + actual_error ? thd->net.last_error : + "unexpected success or fatal error", query); + free_root(&thd->mem_root,0); + return 1; + } + free_root(&thd->mem_root,0); + return Log_event::exec_event(rli); +} + +/* + Does the data loading job when executing a LOAD DATA on the slave + + SYNOPSIS + Load_log_event::exec_event + net + rli + use_rli_only_for_errors - if set to 1, rli is provided to + Load_log_event::exec_event only for this + function to have RPL_LOG_NAME and + rli->last_slave_error, both being used by + error reports. rli's position advancing + is skipped (done by the caller which is + Execute_load_log_event::exec_event). + - if set to 0, rli is provided for full use, + i.e. for error reports and position + advancing. + + DESCRIPTION + Does the data loading job when executing a LOAD DATA on the slave + + RETURN VALUE + 0 Success + 1 Failure +*/ + +int Load_log_event::exec_event(NET* net, struct st_relay_log_info* rli, + bool use_rli_only_for_errors) +{ + init_sql_alloc(&thd->mem_root, 8192,0); + thd->db = rewrite_db((char*)db); + DBUG_ASSERT(thd->query == 0); + thd->query = 0; // Should not be needed + thd->query_error = 0; + + if (db_ok(thd->db, replicate_do_db, replicate_ignore_db)) + { + thd->set_time((time_t)when); + thd->current_tablenr = 0; + VOID(pthread_mutex_lock(&LOCK_thread_count)); + thd->query_id = query_id++; + VOID(pthread_mutex_unlock(&LOCK_thread_count)); + + TABLE_LIST tables; + bzero((char*) &tables,sizeof(tables)); + tables.db = thd->db; + tables.alias = tables.real_name = (char*)table_name; + tables.lock_type = TL_WRITE; + tables.updating= 1; + // the table will be opened in mysql_load + if (table_rules_on && !tables_ok(thd, &tables)) + { + // TODO: this is a bug - this needs to be moved to the I/O thread + if (net) + skip_load_data_infile(net); + } + else + { + char llbuff[22]; + enum enum_duplicates handle_dup = DUP_IGNORE; + if (sql_ex.opt_flags && REPLACE_FLAG) + handle_dup = DUP_REPLACE; + sql_exchange ex((char*)fname, sql_ex.opt_flags && + DUMPFILE_FLAG ); + String field_term(sql_ex.field_term,sql_ex.field_term_len); + String enclosed(sql_ex.enclosed,sql_ex.enclosed_len); + String line_term(sql_ex.line_term,sql_ex.line_term_len); + String line_start(sql_ex.line_start,sql_ex.line_start_len); + String escaped(sql_ex.escaped,sql_ex.escaped_len); + + ex.opt_enclosed = (sql_ex.opt_flags & OPT_ENCLOSED_FLAG); + if (sql_ex.empty_flags & FIELD_TERM_EMPTY) + ex.field_term->length(0); + + ex.skip_lines = skip_lines; + List<Item> field_list; + set_fields(field_list); + thd->slave_proxy_id = thd->thread_id; + if (net) + { + // mysql_load will use thd->net to read the file + thd->net.vio = net->vio; + /* + Make sure the client does not get confused about the packet sequence + */ + thd->net.pkt_nr = net->pkt_nr; + } + if (mysql_load(thd, &ex, &tables, field_list, handle_dup, net != 0, + TL_WRITE)) + thd->query_error = 1; + if (thd->cuted_fields) + /* + log_pos is the position of the LOAD + event in the master log + */ + sql_print_error("Slave: load data infile at position %s in log \ +'%s' produced %d warning(s)", llstr(log_pos,llbuff), RPL_LOG_NAME, + thd->cuted_fields ); + if (net) + net->pkt_nr= thd->net.pkt_nr; + } + } + else + { + /* + We will just ask the master to send us /dev/null if we do not + want to load the data. + TODO: this a bug - needs to be done in I/O thread + */ + if (net) + skip_load_data_infile(net); + } + + thd->net.vio = 0; + thd->db= 0; // prevent db from being freed + close_thread_tables(thd); + if (thd->query_error) + { + int sql_error = thd->net.last_errno; + if (!sql_error) + sql_error = ER_UNKNOWN_ERROR; + + slave_print_error(rli,sql_error, + "Slave: Error '%s' running load data infile ", + ER_SAFE(sql_error)); + free_root(&thd->mem_root,0); + return 1; + } + free_root(&thd->mem_root,0); + + if (thd->fatal_error) + { + sql_print_error("Slave: Fatal error running LOAD DATA INFILE "); + return 1; + } + + return ( use_rli_only_for_errors ? 0 : Log_event::exec_event(rli) ); +} + + +/* + The master started + + IMPLEMENTATION + - To handle the case where the master died without a stop event, + we clean up all temporary tables + locks that we got. + + TODO + - Remove all active user locks + - If we have an active transaction at this point, the master died + in the middle while writing the transaction to the binary log. + In this case we should stop the slave. +*/ + +int Start_log_event::exec_event(struct st_relay_log_info* rli) +{ + /* All temporary tables was deleted on the master */ + close_temporary_tables(thd); + /* + If we have old format, load_tmpdir is cleaned up by the I/O thread + */ + if (!rli->mi->old_format) + cleanup_load_tmpdir(); + return Log_event::exec_event(rli); +} + + +/* + The master stopped. Clean up all temporary tables + locks that the + master may have set. + + TODO + - Remove all active user locks +*/ + +int Stop_log_event::exec_event(struct st_relay_log_info* rli) +{ + // do not clean up immediately after rotate event + if (rli->master_log_pos > BIN_LOG_HEADER_SIZE) + { + close_temporary_tables(thd); + cleanup_load_tmpdir(); + } + /* + We do not want to update master_log pos because we get a rotate event + before stop, so by now master_log_name is set to the next log. + If we updated it, we will have incorrect master coordinates and this + could give false triggers in MASTER_POS_WAIT() that we have reached + the target position when in fact we have not. + */ + rli->inc_pos(get_event_len(), 0); + flush_relay_log_info(rli); + return 0; +} + + +/* + Got a rotate log even from the master + + IMPLEMENTATION + This is mainly used so that we can later figure out the logname and + position for the master. + + We can't rotate the slave as this will cause infinitive rotations + in a A -> B -> A setup. + + RETURN VALUES + 0 ok + */ + +int Rotate_log_event::exec_event(struct st_relay_log_info* rli) +{ + char* log_name = rli->master_log_name; + DBUG_ENTER("Rotate_log_event::exec_event"); + + pthread_mutex_lock(&rli->data_lock); + memcpy(log_name, new_log_ident, ident_len+1); + rli->master_log_pos = pos; + rli->relay_log_pos += get_event_len(); + DBUG_PRINT("info", ("master_log_pos: %d", (ulong) rli->master_log_pos)); + pthread_mutex_unlock(&rli->data_lock); + pthread_cond_broadcast(&rli->data_cond); + flush_relay_log_info(rli); + DBUG_RETURN(0); } -#endif + +int Intvar_log_event::exec_event(struct st_relay_log_info* rli) +{ + switch (type) { + case LAST_INSERT_ID_EVENT: + thd->last_insert_id_used = 1; + thd->last_insert_id = val; + break; + case INSERT_ID_EVENT: + thd->next_insert_id = val; + break; + } + rli->inc_pending(get_event_len()); + return 0; +} + +int Rand_log_event::exec_event(struct st_relay_log_info* rli) +{ + thd->rand.seed1 = (ulong) seed1; + thd->rand.seed2 = (ulong) seed2; + rli->inc_pending(get_event_len()); + return 0; +} + +int Slave_log_event::exec_event(struct st_relay_log_info* rli) +{ + if (mysql_bin_log.is_open()) + mysql_bin_log.write(this); + return Log_event::exec_event(rli); +} + +int Create_file_log_event::exec_event(struct st_relay_log_info* rli) +{ + char fname_buf[FN_REFLEN+10]; + char *p; + int fd = -1; + IO_CACHE file; + int error = 1; + + bzero((char*)&file, sizeof(file)); + p = slave_load_file_stem(fname_buf, file_id, server_id); + strmov(p, ".info"); // strmov takes less code than memcpy + if ((fd = my_open(fname_buf, O_WRONLY|O_CREAT|O_BINARY|O_TRUNC, + MYF(MY_WME))) < 0 || + init_io_cache(&file, fd, IO_SIZE, WRITE_CACHE, (my_off_t)0, 0, + MYF(MY_WME|MY_NABP))) + { + slave_print_error(rli,my_errno, "Could not open file '%s'", fname_buf); + goto err; + } + + // a trick to avoid allocating another buffer + strmov(p, ".data"); + fname = fname_buf; + fname_len = (uint)(p-fname) + 5; + if (write_base(&file)) + { + strmov(p, ".info"); // to have it right in the error message + slave_print_error(rli,my_errno, "Could not write to file '%s'", fname_buf); + goto err; + } + end_io_cache(&file); + my_close(fd, MYF(0)); + + // fname_buf now already has .data, not .info, because we did our trick + if ((fd = my_open(fname_buf, O_WRONLY|O_CREAT|O_BINARY|O_TRUNC, + MYF(MY_WME))) < 0) + { + slave_print_error(rli,my_errno, "Could not open file '%s'", fname_buf); + goto err; + } + if (my_write(fd, (byte*) block, block_len, MYF(MY_WME+MY_NABP))) + { + slave_print_error(rli,my_errno, "Write to '%s' failed", fname_buf); + goto err; + } + if (mysql_bin_log.is_open()) + mysql_bin_log.write(this); + error=0; // Everything is ok + +err: + if (error) + end_io_cache(&file); + if (fd >= 0) + my_close(fd, MYF(0)); + return error ? 1 : Log_event::exec_event(rli); +} + +int Delete_file_log_event::exec_event(struct st_relay_log_info* rli) +{ + char fname[FN_REFLEN+10]; + char *p= slave_load_file_stem(fname, file_id, server_id); + memcpy(p, ".data", 6); + (void) my_delete(fname, MYF(MY_WME)); + memcpy(p, ".info", 6); + (void) my_delete(fname, MYF(MY_WME)); + if (mysql_bin_log.is_open()) + mysql_bin_log.write(this); + return Log_event::exec_event(rli); +} + +int Append_block_log_event::exec_event(struct st_relay_log_info* rli) +{ + char fname[FN_REFLEN+10]; + char *p= slave_load_file_stem(fname, file_id, server_id); + int fd; + int error = 1; + + memcpy(p, ".data", 6); + if ((fd = my_open(fname, O_WRONLY|O_APPEND|O_BINARY, MYF(MY_WME))) < 0) + { + slave_print_error(rli,my_errno, "Could not open file '%s'", fname); + goto err; + } + if (my_write(fd, (byte*) block, block_len, MYF(MY_WME+MY_NABP))) + { + slave_print_error(rli,my_errno, "Write to '%s' failed", fname); + goto err; + } + if (mysql_bin_log.is_open()) + mysql_bin_log.write(this); + error=0; + +err: + if (fd >= 0) + my_close(fd, MYF(0)); + return error ? error : Log_event::exec_event(rli); +} + +int Execute_load_log_event::exec_event(struct st_relay_log_info* rli) +{ + char fname[FN_REFLEN+10]; + char *p= slave_load_file_stem(fname, file_id, server_id); + int fd; + int error = 1; + ulong save_options; + IO_CACHE file; + Load_log_event* lev = 0; + + memcpy(p, ".info", 6); + if ((fd = my_open(fname, O_RDONLY|O_BINARY, MYF(MY_WME))) < 0 || + init_io_cache(&file, fd, IO_SIZE, READ_CACHE, (my_off_t)0, 0, + MYF(MY_WME|MY_NABP))) + { + slave_print_error(rli,my_errno, "Could not open file '%s'", fname); + goto err; + } + if (!(lev = (Load_log_event*)Log_event::read_log_event(&file, + (pthread_mutex_t*)0, + (bool)0)) || + lev->get_type_code() != NEW_LOAD_EVENT) + { + slave_print_error(rli,0, "File '%s' appears corrupted", fname); + goto err; + } + /* + We want to disable binary logging in slave thread because we need the file + events to appear in the same order as they do on the master relative to + other events, so that we can preserve ascending order of log sequence + numbers - needed to handle failover . + */ + save_options = thd->options; + thd->options &= ~ (ulong) (OPTION_BIN_LOG); + lev->thd = thd; + /* + lev->exec_event should use rli only for errors + i.e. should not advance rli's position + */ + if (lev->exec_event(0,rli,1)) + { + slave_print_error(rli,my_errno, "Failed executing load from '%s'", fname); + thd->options = save_options; + goto err; + } + thd->options = save_options; + (void) my_delete(fname, MYF(MY_WME)); + memcpy(p, ".data", 6); + (void) my_delete(fname, MYF(MY_WME)); + if (mysql_bin_log.is_open()) + mysql_bin_log.write(this); + error = 0; + +err: + delete lev; + if (fd >= 0) + { + my_close(fd, MYF(0)); + end_io_cache(&file); + } + return error ? error : Log_event::exec_event(rli); +} + +#endif /* !MYSQL_CLIENT */ diff --git a/sql/log_event.h b/sql/log_event.h index 39ab1f7c6b4..5b9f30b3afd 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -15,8 +15,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef _LOG_EVENT_H -#define _LOG_EVENT_H +#ifndef _log_event_h +#define _log_event_h #ifdef __EMX__ #undef write // remove pthread.h macro definition, conflict with write() class member @@ -34,46 +34,236 @@ #define LOG_READ_TOO_LARGE -7 #define LOG_EVENT_OFFSET 4 -#define BINLOG_VERSION 1 +#define BINLOG_VERSION 3 + +/* + We could have used SERVER_VERSION_LENGTH, but this introduces an + obscure dependency - if somebody decided to change SERVER_VERSION_LENGTH + this would have broke the replication protocol +*/ +#define ST_SERVER_VER_LEN 50 + +#define DUMPFILE_FLAG 0x1 +#define OPT_ENCLOSED_FLAG 0x2 +#define REPLACE_FLAG 0x4 +#define IGNORE_FLAG 0x8 + +#define FIELD_TERM_EMPTY 0x1 +#define ENCLOSED_EMPTY 0x2 +#define LINE_TERM_EMPTY 0x4 +#define LINE_START_EMPTY 0x8 +#define ESCAPED_EMPTY 0x10 + +struct old_sql_ex +{ + char field_term; + char enclosed; + char line_term; + char line_start; + char escaped; + char opt_flags; + char empty_flags; +}; + +#define NUM_LOAD_DELIM_STRS 5 + +struct sql_ex_info +{ + char* field_term; + char* enclosed; + char* line_term; + char* line_start; + char* escaped; + int cached_new_format; + uint8 field_term_len,enclosed_len,line_term_len,line_start_len, escaped_len; + char opt_flags; + char empty_flags; + + // store in new format even if old is possible + void force_new_format() { cached_new_format = 1;} + int data_size() + { + return (new_format() ? + field_term_len + enclosed_len + line_term_len + + line_start_len + escaped_len + 6 : 7); + } + int write_data(IO_CACHE* file); + char* init(char* buf,char* buf_end,bool use_new_format); + bool new_format() + { + return ((cached_new_format != -1) ? cached_new_format : + (cached_new_format=(field_term_len > 1 || + enclosed_len > 1 || + line_term_len > 1 || line_start_len > 1 || + escaped_len > 1))); + } +}; + +/* + Binary log consists of events. Each event has a fixed length header, + followed by possibly variable ( depending on the type of event) length + data body. The data body consists of an optional fixed length segment + (post-header), and an optional variable length segment. See #defines and + comments below for the format specifics +*/ + +/* event-specific post-header sizes */ +#define LOG_EVENT_HEADER_LEN 19 +#define OLD_HEADER_LEN 13 +#define QUERY_HEADER_LEN (4 + 4 + 1 + 2) +#define LOAD_HEADER_LEN (4 + 4 + 4 + 1 +1 + 4) +#define START_HEADER_LEN (2 + ST_SERVER_VER_LEN + 4) +#define ROTATE_HEADER_LEN 8 +#define CREATE_FILE_HEADER_LEN 4 +#define APPEND_BLOCK_HEADER_LEN 4 +#define EXEC_LOAD_HEADER_LEN 4 +#define DELETE_FILE_HEADER_LEN 4 + +/* event header offsets */ -#define LOG_EVENT_HEADER_LEN 13 -#define QUERY_HEADER_LEN (sizeof(uint32) + sizeof(uint32) + \ - sizeof(uchar) + sizeof(uint16)) -#define LOAD_HEADER_LEN (sizeof(uint32) + sizeof(uint32) + \ - + sizeof(uint32) + 2 + sizeof(uint32)) -#define EVENT_LEN_OFFSET 9 #define EVENT_TYPE_OFFSET 4 -#define QUERY_EVENT_OVERHEAD (LOG_EVENT_HEADER_LEN+QUERY_HEADER_LEN) -#define ROTATE_EVENT_OVERHEAD LOG_EVENT_HEADER_LEN -#define LOAD_EVENT_OVERHEAD (LOG_EVENT_HEADER_LEN+LOAD_HEADER_LEN+sizeof(sql_ex_info)) +#define SERVER_ID_OFFSET 5 +#define EVENT_LEN_OFFSET 9 +#define LOG_POS_OFFSET 13 +#define FLAGS_OFFSET 17 + +/* start event post-header */ + +#define ST_BINLOG_VER_OFFSET 0 +#define ST_SERVER_VER_OFFSET 2 +#define ST_CREATED_OFFSET (ST_SERVER_VER_OFFSET + ST_SERVER_VER_LEN) + +/* slave event post-header */ + +#define SL_MASTER_PORT_OFFSET 8 +#define SL_MASTER_POS_OFFSET 0 +#define SL_MASTER_HOST_OFFSET 10 + +/* query event post-header */ + +#define Q_THREAD_ID_OFFSET 0 +#define Q_EXEC_TIME_OFFSET 4 +#define Q_DB_LEN_OFFSET 8 +#define Q_ERR_CODE_OFFSET 9 +#define Q_DATA_OFFSET QUERY_HEADER_LEN + +/* Intvar event post-header */ + +#define I_TYPE_OFFSET 0 +#define I_VAL_OFFSET 1 + +/* Rand event post-header */ + +#define RAND_SEED1_OFFSET 0 +#define RAND_SEED2_OFFSET 8 + +/* Load event post-header */ + +#define L_THREAD_ID_OFFSET 0 +#define L_EXEC_TIME_OFFSET 4 +#define L_SKIP_LINES_OFFSET 8 +#define L_TBL_LEN_OFFSET 12 +#define L_DB_LEN_OFFSET 13 +#define L_NUM_FIELDS_OFFSET 14 +#define L_SQL_EX_OFFSET 18 +#define L_DATA_OFFSET LOAD_HEADER_LEN + +/* Rotate event post-header */ + +#define R_POS_OFFSET 0 +#define R_IDENT_OFFSET 8 + +#define CF_FILE_ID_OFFSET 0 +#define CF_DATA_OFFSET CREATE_FILE_HEADER_LEN + +#define AB_FILE_ID_OFFSET 0 +#define AB_DATA_OFFSET APPEND_BLOCK_HEADER_LEN + +#define EL_FILE_ID_OFFSET 0 + +#define DF_FILE_ID_OFFSET 0 + +#define QUERY_EVENT_OVERHEAD (LOG_EVENT_HEADER_LEN+QUERY_HEADER_LEN) +#define QUERY_DATA_OFFSET (LOG_EVENT_HEADER_LEN+QUERY_HEADER_LEN) +#define ROTATE_EVENT_OVERHEAD (LOG_EVENT_HEADER_LEN+ROTATE_HEADER_LEN) +#define LOAD_EVENT_OVERHEAD (LOG_EVENT_HEADER_LEN+LOAD_HEADER_LEN) +#define CREATE_FILE_EVENT_OVERHEAD (LOG_EVENT_HEADER_LEN+\ + +LOAD_HEADER_LEN+CREATE_FILE_HEADER_LEN) +#define DELETE_FILE_EVENT_OVERHEAD (LOG_EVENT_HEADER_LEN+DELETE_FILE_HEADER_LEN) +#define EXEC_LOAD_EVENT_OVERHEAD (LOG_EVENT_HEADER_LEN+EXEC_LOAD_HEADER_LEN) +#define APPEND_BLOCK_EVENT_OVERHEAD (LOG_EVENT_HEADER_LEN+APPEND_BLOCK_HEADER_LEN) + #define BINLOG_MAGIC "\xfe\x62\x69\x6e" -enum Log_event_type { START_EVENT = 1, QUERY_EVENT =2, - STOP_EVENT=3, ROTATE_EVENT = 4, INTVAR_EVENT=5, - LOAD_EVENT=6}; -enum Int_event_type { INVALID_INT_EVENT = 0, LAST_INSERT_ID_EVENT = 1, INSERT_ID_EVENT = 2 - }; +#define LOG_EVENT_TIME_F 0x1 +#define LOG_EVENT_FORCED_ROTATE_F 0x2 + +enum Log_event_type +{ + START_EVENT = 1, QUERY_EVENT =2, STOP_EVENT=3, ROTATE_EVENT = 4, + INTVAR_EVENT=5, LOAD_EVENT=6, SLAVE_EVENT=7, CREATE_FILE_EVENT=8, + APPEND_BLOCK_EVENT=9, EXEC_LOAD_EVENT=10, DELETE_FILE_EVENT=11, + NEW_LOAD_EVENT=12, RAND_EVENT=13 +}; + +enum Int_event_type +{ + INVALID_INT_EVENT = 0, LAST_INSERT_ID_EVENT = 1, INSERT_ID_EVENT = 2 +}; + #ifndef MYSQL_CLIENT class String; +class MYSQL_LOG; +class THD; #endif -extern ulong server_id; +struct st_relay_log_info; class Log_event { public: + my_off_t log_pos; + char *temp_buf; time_t when; ulong exec_time; - int valid_exec_time; // if false, the exec time setting is bogus uint32 server_id; + uint cached_event_len; + uint16 flags; + bool cache_stmt; +#ifndef MYSQL_CLIENT + THD* thd; + + Log_event(THD* thd_arg, uint16 flags_arg, bool cache_stmt); + Log_event(); + // if mutex is 0, the read will proceed without mutex + static Log_event* read_log_event(IO_CACHE* file, + pthread_mutex_t* log_lock, + bool old_format); + static int read_log_event(IO_CACHE* file, String* packet, + pthread_mutex_t* log_lock); + void set_log_pos(MYSQL_LOG* log); + virtual void pack_info(String* packet); + int net_send(THD* thd, const char* log_name, my_off_t pos); + static void init_show_field_list(List<Item>* field_list); + virtual int exec_event(struct st_relay_log_info* rli); + virtual const char* get_db() + { + return thd ? thd->db : 0; + } +#else + // avoid having to link mysqlbinlog against libpthread + static Log_event* read_log_event(IO_CACHE* file, bool old_format); + virtual void print(FILE* file, bool short_form = 0, char* last_db = 0) = 0; + void print_timestamp(FILE* file, time_t *ts = 0); + void print_header(FILE* file); +#endif static void *operator new(size_t size) { return (void*) my_malloc((uint)size, MYF(MY_WME|MY_FAE)); } - static void operator delete(void *ptr, size_t size) { my_free((gptr) ptr, MYF(MY_WME|MY_ALLOW_ZERO_PTR)); @@ -81,43 +271,36 @@ public: int write(IO_CACHE* file); int write_header(IO_CACHE* file); - virtual int write_data(IO_CACHE* file __attribute__((unused))) { return 0; } + virtual int write_data(IO_CACHE* file) + { return write_data_header(file) || write_data_body(file); } + virtual int write_data_header(IO_CACHE* file __attribute__((unused))) + { return 0; } + virtual int write_data_body(IO_CACHE* file __attribute__((unused))) + { return 0; } virtual Log_event_type get_type_code() = 0; - Log_event(time_t when_arg, ulong exec_time_arg = 0, - int valid_exec_time_arg = 0, uint32 server_id_arg = 0): - when(when_arg), exec_time(exec_time_arg), - valid_exec_time(valid_exec_time_arg) + virtual bool is_valid() = 0; + inline bool get_cache_stmt() { return cache_stmt; } + Log_event(const char* buf, bool old_format); + virtual ~Log_event() { free_temp_buf();} + void register_temp_buf(char* buf) { temp_buf = buf; } + void free_temp_buf() { - server_id = server_id_arg ? server_id_arg : (::server_id); + if (temp_buf) + { + my_free(temp_buf, MYF(0)); + temp_buf = 0; + } } - - Log_event(const char* buf): valid_exec_time(0) + virtual int get_data_size() { return 0;} + virtual int get_data_body_offset() { return 0; } + int get_event_len() { - when = uint4korr(buf); - server_id = uint4korr(buf + 5); + return (cached_event_len ? cached_event_len : + (cached_event_len = LOG_EVENT_HEADER_LEN + get_data_size())); } - - virtual ~Log_event() {} - - virtual int get_data_size() { return 0;} - virtual void print(FILE* file, bool short_form = 0, char* last_db = 0) = 0; - - void print_timestamp(FILE* file, time_t *ts = 0); - void print_header(FILE* file); - -#ifndef MYSQL_CLIENT - // if mutex is 0, the read will proceed without mutex - static Log_event* read_log_event(IO_CACHE* file, pthread_mutex_t* log_lock); -#else // avoid having to link mysqlbinlog against libpthread - static Log_event* read_log_event(IO_CACHE* file); -#endif - static Log_event* read_log_event(const char* buf, int event_len); - -#ifndef MYSQL_CLIENT - static int read_log_event(IO_CACHE* file, String* packet, - pthread_mutex_t* log_lock); -#endif - + static Log_event* read_log_event(const char* buf, int event_len, + const char **error, bool old_format); + const char* get_type_str(); }; @@ -128,35 +311,27 @@ protected: public: const char* query; const char* db; - uint32 q_len; // if we already know the length of the query string - // we pass it here, so we would not have to call strlen() - // otherwise, set it to 0, in which case, we compute it with strlen() + /* + If we already know the length of the query string + we pass it with q_len, so we would not have to call strlen() + otherwise, set it to 0, in which case, we compute it with strlen() + */ + uint32 q_len; uint32 db_len; uint16 error_code; ulong thread_id; -#if !defined(MYSQL_CLIENT) - THD* thd; - bool cache_stmt; - Query_log_event(THD* thd_arg, const char* query_arg, bool using_trans=0): - Log_event(thd_arg->start_time,0,1,thd_arg->server_id), data_buf(0), - query(query_arg), db(thd_arg->db), q_len(thd_arg->query_length), - error_code(thd_arg->killed ? ER_SERVER_SHUTDOWN: thd_arg->net.last_errno), - thread_id(thd_arg->thread_id), thd(thd_arg), - cache_stmt(using_trans && - (thd_arg->options & (OPTION_NOT_AUTO_COMMIT | OPTION_BEGIN))) - { - time_t end_time; - time(&end_time); - exec_time = (ulong) (end_time - thd->start_time); - db_len = (db) ? (uint32) strlen(db) : 0; - // do not log stray system errors such as EE_WRITE - if (error_code < ERRMOD) - error_code = 0; - } +#ifndef MYSQL_CLIENT + + Query_log_event(THD* thd_arg, const char* query_arg, ulong query_length, + bool using_trans); + const char* get_db() { return db; } + void pack_info(String* packet); + int exec_event(struct st_relay_log_info* rli); +#else + void print(FILE* file, bool short_form = 0, char* last_db = 0); #endif - Query_log_event(IO_CACHE* file, time_t when, uint32 server_id_arg); - Query_log_event(const char* buf, int event_len); + Query_log_event(const char* buf, int event_len, bool old_format); ~Query_log_event() { if (data_buf) @@ -167,46 +342,51 @@ public: Log_event_type get_type_code() { return QUERY_EVENT; } int write(IO_CACHE* file); int write_data(IO_CACHE* file); // returns 0 on success, -1 on error + bool is_valid() { return query != 0; } int get_data_size() { - return q_len + db_len + 2 + - sizeof(uint32) // thread_id - + sizeof(uint32) // exec_time - + sizeof(uint16) // error_code - ; + return (q_len + db_len + 2 + + 4 // thread_id + + 4 // exec_time + + 2 // error_code + ); } - - void print(FILE* file, bool short_form = 0, char* last_db = 0); }; -#define DUMPFILE_FLAG 0x1 -#define OPT_ENCLOSED_FLAG 0x2 -#define REPLACE_FLAG 0x4 -#define IGNORE_FLAG 0x8 -#define FIELD_TERM_EMPTY 0x1 -#define ENCLOSED_EMPTY 0x2 -#define LINE_TERM_EMPTY 0x4 -#define LINE_START_EMPTY 0x8 -#define ESCAPED_EMPTY 0x10 +class Slave_log_event: public Log_event +{ +protected: + char* mem_pool; + void init_from_mem_pool(int data_size); +public: + my_off_t master_pos; + char* master_host; + char* master_log; + int master_host_len; + int master_log_len; + uint16 master_port; +#ifndef MYSQL_CLIENT + Slave_log_event(THD* thd_arg, struct st_relay_log_info* rli); + void pack_info(String* packet); + int exec_event(struct st_relay_log_info* rli); +#else + void print(FILE* file, bool short_form = 0, char* last_db = 0); +#endif -struct sql_ex_info - { - char field_term; - char enclosed; - char line_term; - char line_start; - char escaped; - char opt_flags; // flags for the options - char empty_flags; // flags to indicate which of the terminating charact - } ; + Slave_log_event(const char* buf, int event_len); + ~Slave_log_event(); + int get_data_size(); + bool is_valid() { return master_host != 0; } + Log_event_type get_type_code() { return SLAVE_EVENT; } + int write_data(IO_CACHE* file ); +}; class Load_log_event: public Log_event { protected: - char* data_buf; - void copy_log_event(const char *buf, ulong data_len); + int copy_log_event(const char *buf, ulong event_len, bool old_format); public: ulong thread_id; @@ -217,109 +397,54 @@ public: const char* fields; const uchar* field_lens; uint32 field_block_len; - const char* table_name; const char* db; const char* fname; uint32 skip_lines; sql_ex_info sql_ex; - -#if !defined(MYSQL_CLIENT) - THD* thd; + +#ifndef MYSQL_CLIENT String field_lens_buf; String fields_buf; - Load_log_event(THD* thd, sql_exchange* ex, - const char *db_arg, const char* table_name_arg, - List<Item>& fields_arg, enum enum_duplicates handle_dup ): - Log_event(thd->start_time),data_buf(0),thread_id(thd->thread_id), - num_fields(0),fields(0),field_lens(0),field_block_len(0), - table_name(table_name_arg ? table_name_arg : ""), - db(db_arg ? db_arg : ""), - fname(ex->file_name), - thd(thd) + + Load_log_event(THD* thd, sql_exchange* ex, const char* db_arg, + const char* table_name_arg, + List<Item>& fields_arg, enum enum_duplicates handle_dup, + bool using_trans); + void set_fields(List<Item> &fields_arg); + void pack_info(String* packet); + const char* get_db() { return db; } + int exec_event(struct st_relay_log_info* rli) { - time_t end_time; - time(&end_time); - exec_time = (ulong) (end_time - thd->start_time); - valid_exec_time = 1; - db_len = (uint32) strlen(db); - table_name_len = (uint32) strlen(table_name); - fname_len = (fname) ? (uint) strlen(fname) : 0; - sql_ex.field_term = (*ex->field_term)[0]; - sql_ex.enclosed = (*ex->enclosed)[0]; - sql_ex.line_term = (*ex->line_term)[0]; - sql_ex.line_start = (*ex->line_start)[0]; - sql_ex.escaped = (*ex->escaped)[0]; - sql_ex.opt_flags = 0; - if(ex->dumpfile) - sql_ex.opt_flags |= DUMPFILE_FLAG; - if(ex->opt_enclosed) - sql_ex.opt_flags |= OPT_ENCLOSED_FLAG; - - sql_ex.empty_flags = 0; - - switch(handle_dup) { - case DUP_IGNORE: sql_ex.opt_flags |= IGNORE_FLAG; break; - case DUP_REPLACE: sql_ex.opt_flags |= REPLACE_FLAG; break; - case DUP_ERROR: break; - } - - if(!ex->field_term->length()) - sql_ex.empty_flags |= FIELD_TERM_EMPTY; - if(!ex->enclosed->length()) - sql_ex.empty_flags |= ENCLOSED_EMPTY; - if(!ex->line_term->length()) - sql_ex.empty_flags |= LINE_TERM_EMPTY; - if(!ex->line_start->length()) - sql_ex.empty_flags |= LINE_START_EMPTY; - if(!ex->escaped->length()) - sql_ex.empty_flags |= ESCAPED_EMPTY; - - skip_lines = ex->skip_lines; - - List_iterator<Item> li(fields_arg); - field_lens_buf.length(0); - fields_buf.length(0); - Item* item; - while((item = li++)) - { - num_fields++; - uchar len = (uchar) strlen(item->name); - field_block_len += len + 1; - fields_buf.append(item->name, len + 1); - field_lens_buf.append((char*)&len, 1); - } - - field_lens = (const uchar*)field_lens_buf.ptr(); - fields = fields_buf.ptr(); + return exec_event(thd->slave_net,rli,0); } - void set_fields(List<Item> &fields_arg); + int exec_event(NET* net, struct st_relay_log_info* rli, + bool use_rli_only_for_errors); +#else + void print(FILE* file, bool short_form = 0, char* last_db = 0); #endif - Load_log_event(IO_CACHE * file, time_t when, uint32 server_id_arg); - Load_log_event(const char* buf, int event_len); + Load_log_event(const char* buf, int event_len, bool old_format); ~Load_log_event() + {} + Log_event_type get_type_code() { - if (data_buf) - { - my_free((gptr) data_buf, MYF(0)); - } + return sql_ex.new_format() ? NEW_LOAD_EVENT: LOAD_EVENT; } - Log_event_type get_type_code() { return LOAD_EVENT; } - int write_data(IO_CACHE* file); // returns 0 on success, -1 on error + int write_data_header(IO_CACHE* file); + int write_data_body(IO_CACHE* file); + bool is_valid() { return table_name != 0; } int get_data_size() { - return table_name_len + 2 + db_len + 2 + fname_len - + 4 // thread_id - + 4 // exec_time - + 4 // skip_lines - + 4 // field block len - + sizeof(sql_ex) + field_block_len + num_fields*sizeof(uchar) ; - ; + return (table_name_len + 2 + db_len + 2 + fname_len + + 4 // thread_id + + 4 // exec_time + + 4 // skip_lines + + 4 // field block len + + sql_ex.data_size() + field_block_len + num_fields); } - - void print(FILE* file, bool short_form = 0, char* last_db = 0); + int get_data_body_offset() { return LOAD_EVENT_OVERHEAD; } }; extern char server_version[SERVER_VERSION_LENGTH]; @@ -329,101 +454,264 @@ class Start_log_event: public Log_event public: uint32 created; uint16 binlog_version; - char server_version[50]; - - Start_log_event() :Log_event(time(NULL)),binlog_version(BINLOG_VERSION) + char server_version[ST_SERVER_VER_LEN]; + +#ifndef MYSQL_CLIENT + Start_log_event() :Log_event(), binlog_version(BINLOG_VERSION) { created = (uint32) when; - memcpy(server_version, ::server_version, sizeof(server_version)); + memcpy(server_version, ::server_version, ST_SERVER_VER_LEN); } - Start_log_event(IO_CACHE* file, time_t when_arg, uint32 server_id_arg) : - Log_event(when_arg, 0, 0, server_id_arg) - { - char buf[sizeof(server_version) + 2 + 4 + 4]; - if (my_b_read(file, (byte*) buf, sizeof(buf))) - return; - binlog_version = uint2korr(buf+4); - memcpy(server_version, buf + 6, sizeof(server_version)); - server_version[sizeof(server_version)-1]=0; - created = uint4korr(buf + 6 + sizeof(server_version)); - } - Start_log_event(const char* buf); - + void pack_info(String* packet); + int exec_event(struct st_relay_log_info* rli); +#else + void print(FILE* file, bool short_form = 0, char* last_db = 0); +#endif + + Start_log_event(const char* buf, bool old_format); ~Start_log_event() {} Log_event_type get_type_code() { return START_EVENT;} int write_data(IO_CACHE* file); + bool is_valid() { return 1; } int get_data_size() { - // sizeof(binlog_version) + sizeof(server_version) sizeof(created) - return 2 + sizeof(server_version) + 4; + return START_HEADER_LEN; } - void print(FILE* file, bool short_form = 0, char* last_db = 0); }; + class Intvar_log_event: public Log_event { public: ulonglong val; uchar type; - Intvar_log_event(uchar type_arg, ulonglong val_arg) - :Log_event(time(NULL)),val(val_arg),type(type_arg) + +#ifndef MYSQL_CLIENT + Intvar_log_event(THD* thd_arg,uchar type_arg, ulonglong val_arg) + :Log_event(),val(val_arg),type(type_arg) {} - Intvar_log_event(IO_CACHE* file, time_t when, uint32 server_id_arg); - Intvar_log_event(const char* buf); + void pack_info(String* packet); + int exec_event(struct st_relay_log_info* rli); +#else + void print(FILE* file, bool short_form = 0, char* last_db = 0); +#endif + + Intvar_log_event(const char* buf, bool old_format); ~Intvar_log_event() {} Log_event_type get_type_code() { return INTVAR_EVENT;} + const char* get_var_type_name(); int get_data_size() { return sizeof(type) + sizeof(val);} int write_data(IO_CACHE* file); - - + bool is_valid() { return 1; } +}; + +/***************************************************************************** + * + * Rand log event class + * + ****************************************************************************/ +class Rand_log_event: public Log_event +{ + public: + ulonglong seed1; + ulonglong seed2; + +#ifndef MYSQL_CLIENT + Rand_log_event(THD* thd_arg, ulonglong seed1_arg, ulonglong seed2_arg) + :Log_event(thd_arg,0,0),seed1(seed1_arg),seed2(seed2_arg) + {} + void pack_info(String* packet); + int exec_event(struct st_relay_log_info* rli); +#else void print(FILE* file, bool short_form = 0, char* last_db = 0); +#endif + + Rand_log_event(const char* buf, bool old_format); + ~Rand_log_event() {} + Log_event_type get_type_code() { return RAND_EVENT;} + int get_data_size() { return sizeof(ulonglong) * 2; } + int write_data(IO_CACHE* file); + bool is_valid() { return 1; } }; + class Stop_log_event: public Log_event { public: - Stop_log_event() :Log_event(time(NULL)) +#ifndef MYSQL_CLIENT + Stop_log_event() :Log_event() + {} + int exec_event(struct st_relay_log_info* rli); +#else + void print(FILE* file, bool short_form = 0, char* last_db = 0); +#endif + + Stop_log_event(const char* buf, bool old_format): + Log_event(buf, old_format) {} - Stop_log_event(IO_CACHE* file, time_t when_arg, uint32 server_id_arg): - Log_event(when_arg,0,0,server_id_arg) - { - byte skip[4]; - my_b_read(file, skip, sizeof(skip)); // skip the event length - } - Stop_log_event(const char* buf):Log_event(buf) - { - } ~Stop_log_event() {} Log_event_type get_type_code() { return STOP_EVENT;} - void print(FILE* file, bool short_form = 0, char* last_db = 0); + bool is_valid() { return 1; } }; + class Rotate_log_event: public Log_event { public: const char* new_log_ident; - uchar ident_len; + ulonglong pos; + uint ident_len; bool alloced; - - Rotate_log_event(const char* new_log_ident_arg, uint ident_len_arg = 0) : - Log_event(time(NULL)), - new_log_ident(new_log_ident_arg), - ident_len(ident_len_arg ? ident_len_arg : (uint) strlen(new_log_ident_arg)), - alloced(0) +#ifndef MYSQL_CLIENT + Rotate_log_event(THD* thd_arg, const char* new_log_ident_arg, + uint ident_len_arg = 0, + ulonglong pos_arg = LOG_EVENT_OFFSET) + :Log_event(thd_arg,0,0), new_log_ident(new_log_ident_arg), + pos(pos_arg),ident_len(ident_len_arg ? ident_len_arg : + (uint) strlen(new_log_ident_arg)), alloced(0) {} - - Rotate_log_event(IO_CACHE* file, time_t when, uint32 server_id_arg) ; - Rotate_log_event(const char* buf, int event_len); + void pack_info(String* packet); + int exec_event(struct st_relay_log_info* rli); +#else + void print(FILE* file, bool short_form = 0, char* last_db = 0); +#endif + + Rotate_log_event(const char* buf, int event_len, bool old_format); ~Rotate_log_event() { if (alloced) my_free((gptr) new_log_ident, MYF(0)); } Log_event_type get_type_code() { return ROTATE_EVENT;} - int get_data_size() { return ident_len;} + int get_data_size() { return ident_len + ROTATE_HEADER_LEN;} + bool is_valid() { return new_log_ident != 0; } int write_data(IO_CACHE* file); - +}; + +/* the classes below are for the new LOAD DATA INFILE logging */ + +class Create_file_log_event: public Load_log_event +{ +protected: + /* + Pretend we are Load event, so we can write out just + our Load part - used on the slave when writing event out to + SQL_LOAD-*.info file + */ + bool fake_base; +public: + char* block; + uint block_len; + uint file_id; + bool inited_from_old; + +#ifndef MYSQL_CLIENT + Create_file_log_event(THD* thd, sql_exchange* ex, const char* db_arg, + const char* table_name_arg, + List<Item>& fields_arg, + enum enum_duplicates handle_dup, + char* block_arg, uint block_len_arg, + bool using_trans); + void pack_info(String* packet); + int exec_event(struct st_relay_log_info* rli); +#else void print(FILE* file, bool short_form = 0, char* last_db = 0); +#endif + + Create_file_log_event(const char* buf, int event_len, bool old_format); + ~Create_file_log_event() {} + + Log_event_type get_type_code() + { + return fake_base ? Load_log_event::get_type_code() : CREATE_FILE_EVENT; + } + int get_data_size() + { + return (fake_base ? Load_log_event::get_data_size() : + Load_log_event::get_data_size() + + 4 + 1 + block_len); + } + int get_data_body_offset() + { + return (fake_base ? LOAD_EVENT_OVERHEAD: + LOAD_EVENT_OVERHEAD + CREATE_FILE_HEADER_LEN); + } + bool is_valid() { return inited_from_old || block != 0; } + int write_data_header(IO_CACHE* file); + int write_data_body(IO_CACHE* file); + /* + Cut out Create_file extentions and + write it as Load event - used on the slave + */ + int write_base(IO_CACHE* file); }; + +class Append_block_log_event: public Log_event +{ +public: + char* block; + uint block_len; + uint file_id; + +#ifndef MYSQL_CLIENT + Append_block_log_event(THD* thd, char* block_arg, + uint block_len_arg, bool using_trans); + int exec_event(struct st_relay_log_info* rli); + void pack_info(String* packet); +#else + void print(FILE* file, bool short_form = 0, char* last_db = 0); #endif + + Append_block_log_event(const char* buf, int event_len); + ~Append_block_log_event() {} + Log_event_type get_type_code() { return APPEND_BLOCK_EVENT;} + int get_data_size() { return block_len + APPEND_BLOCK_HEADER_LEN ;} + bool is_valid() { return block != 0; } + int write_data(IO_CACHE* file); +}; + + +class Delete_file_log_event: public Log_event +{ +public: + uint file_id; + +#ifndef MYSQL_CLIENT + Delete_file_log_event(THD* thd, bool using_trans); + void pack_info(String* packet); + int exec_event(struct st_relay_log_info* rli); +#else + void print(FILE* file, bool short_form = 0, char* last_db = 0); +#endif + + Delete_file_log_event(const char* buf, int event_len); + ~Delete_file_log_event() {} + Log_event_type get_type_code() { return DELETE_FILE_EVENT;} + int get_data_size() { return DELETE_FILE_HEADER_LEN ;} + bool is_valid() { return file_id != 0; } + int write_data(IO_CACHE* file); +}; + +class Execute_load_log_event: public Log_event +{ +public: + uint file_id; + +#ifndef MYSQL_CLIENT + Execute_load_log_event(THD* thd, bool using_trans); + void pack_info(String* packet); + int exec_event(struct st_relay_log_info* rli); +#else + void print(FILE* file, bool short_form = 0, char* last_db = 0); +#endif + + Execute_load_log_event(const char* buf, int event_len); + ~Execute_load_log_event() {} + Log_event_type get_type_code() { return EXEC_LOAD_EVENT;} + int get_data_size() { return EXEC_LOAD_HEADER_LEN ;} + bool is_valid() { return file_id != 0; } + int write_data(IO_CACHE* file); +}; + +#endif /* _log_event_h */ diff --git a/sql/matherr.c b/sql/matherr.c index 8523a78ce94..ea0c15d2feb 100644 --- a/sql/matherr.c +++ b/sql/matherr.c @@ -1,22 +1,22 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Fix that we got POSTFIX_ERROR when doing unreasonable math (not core) */ -#include <global.h> +#include <my_global.h> #include <errno.h> /* Fix that we gets POSTFIX_ERROR when error in math */ diff --git a/sql/md5.c b/sql/md5.c deleted file mode 100644 index 4c2e80107b8..00000000000 --- a/sql/md5.c +++ /dev/null @@ -1,351 +0,0 @@ -/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm - */ - -/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All -rights reserved. - -License to copy and use this software is granted provided that it -is identified as the "RSA Data Security, Inc. MD5 Message-Digest -Algorithm" in all material mentioning or referencing this software -or this function. - -License is also granted to make and use derivative works provided -that such works are identified as "derived from the RSA Data -Security, Inc. MD5 Message-Digest Algorithm" in all material -mentioning or referencing the derived work. - -RSA Data Security, Inc. makes no representations concerning either -the merchantability of this software or the suitability of this -software for any particular purpose. It is provided "as is" -without express or implied warranty of any kind. - -These notices must be retained in any copies of any part of this -documentation and/or software. -*/ - -/* - Changes by Monty: - Replace of MD5_memset and MD5_memcpy with memset & memcpy -*/ - -#include <global.h> -#include <m_string.h> -#include "md5.h" - -/* Constants for MD5Transform routine. */ - -#define S11 7 -#define S12 12 -#define S13 17 -#define S14 22 -#define S21 5 -#define S22 9 -#define S23 14 -#define S24 20 -#define S31 4 -#define S32 11 -#define S33 16 -#define S34 23 -#define S41 6 -#define S42 10 -#define S43 15 -#define S44 21 - - -static void MD5Transform PROTO_LIST ((UINT4 [4], unsigned char [64])); -static void Encode PROTO_LIST - ((unsigned char *, UINT4 *, unsigned int)); -static void Decode PROTO_LIST - ((UINT4 *, unsigned char *, unsigned int)); -#ifdef OLD_CODE -static void MD5_memcpy PROTO_LIST ((POINTER, POINTER, unsigned int)); -static void MD5_memset PROTO_LIST ((POINTER, int, unsigned int)); -#else -#define MD5_memcpy(A,B,C) memcpy((char*) (A),(char*) (B), (C)) -#define MD5_memset(A,B,C) memset((char*) (A),(B), (C)) -#endif - -static unsigned char PADDING[64] = { - 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - -/* F, G, H and I are basic MD5 functions. - */ -#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) -#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) -#define H(x, y, z) ((x) ^ (y) ^ (z)) -#define I(x, y, z) ((y) ^ ((x) | (~z))) - -/* ROTATE_LEFT rotates x left n bits. - */ -#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) - -/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. -Rotation is separate from addition to prevent recomputation. - */ -#define FF(a, b, c, d, x, s, ac) { \ - (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ - (a) = ROTATE_LEFT ((a), (s)); \ - (a) += (b); \ - } -#define GG(a, b, c, d, x, s, ac) { \ - (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ - (a) = ROTATE_LEFT ((a), (s)); \ - (a) += (b); \ - } -#define HH(a, b, c, d, x, s, ac) { \ - (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ - (a) = ROTATE_LEFT ((a), (s)); \ - (a) += (b); \ - } -#define II(a, b, c, d, x, s, ac) { \ - (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ - (a) = ROTATE_LEFT ((a), (s)); \ - (a) += (b); \ - } - -/* MD5 initialization. Begins an MD5 operation, writing a new context. - */ -void MD5Init (MD5_CTX *context) /* context */ -{ - context->count[0] = context->count[1] = 0; - /* Load magic initialization constants. -*/ - context->state[0] = 0x67452301; - context->state[1] = 0xefcdab89; - context->state[2] = 0x98badcfe; - context->state[3] = 0x10325476; -} - -/* MD5 block update operation. Continues an MD5 message-digest - operation, processing another message block, and updating the - context. - */ -void MD5Update ( -MD5_CTX *context, /* context */ -unsigned char *input, /* input block */ -unsigned int inputLen) /* length of input block */ -{ - unsigned int i, idx, partLen; - - /* Compute number of bytes mod 64 */ - idx = (unsigned int)((context->count[0] >> 3) & 0x3F); - - - /* Update number of bits */ - if ((context->count[0] += ((UINT4)inputLen << 3)) - < ((UINT4)inputLen << 3)) - context->count[1]++; - context->count[1] += ((UINT4)inputLen >> 29); - - partLen = 64 - idx; - - /* Transform as many times as possible. -*/ - if (inputLen >= partLen) { - MD5_memcpy((POINTER)&context->buffer[idx], (POINTER)input, partLen); - MD5Transform(context->state, context->buffer); - - for (i = partLen; i + 63 < inputLen; i += 64) - MD5Transform (context->state, &input[i]); - - idx = 0; - } - else - i = 0; - - /* Buffer remaining input */ - MD5_memcpy((POINTER)&context->buffer[idx], (POINTER)&input[i], - inputLen-i); -} - -/* MD5 finalization. Ends an MD5 message-digest operation, writing the - the message digest and zeroizing the context. - */ -void MD5Final ( -unsigned char digest[16], /* message digest */ -MD5_CTX *context) /* context */ -{ - unsigned char bits[8]; - unsigned int idx, padLen; - - /* Save number of bits */ - Encode (bits, context->count, 8); - - /* Pad out to 56 mod 64. -*/ - idx = (unsigned int)((context->count[0] >> 3) & 0x3f); - padLen = (idx < 56) ? (56 - idx) : (120 - idx); - MD5Update (context, PADDING, padLen); - - /* Append length (before padding) */ - MD5Update (context, bits, 8); - - /* Store state in digest */ - Encode (digest, context->state, 16); - - /* Zeroize sensitive information. -*/ - MD5_memset ((POINTER)context, 0, sizeof (*context)); -} - -/* MD5 basic transformation. Transforms state based on block. - */ -static void MD5Transform ( -UINT4 state[4], -unsigned char block[64]) -{ - UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; - - Decode (x, block, 64); - - /* Round 1 */ - FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ - FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ - FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ - FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ - FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ - FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ - FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ - FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ - FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ - FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ - FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ - FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ - FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ - FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ - FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ - FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ - - /* Round 2 */ - GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ - GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ - GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ - GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ - GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ - GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ - GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ - GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ - GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ - GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ - GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ - GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ - GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ - GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ - GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ - GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ - - /* Round 3 */ - HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ - HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ - HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ - HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ - HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ - HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ - HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ - HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ - HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ - HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ - HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ - HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ - HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ - HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ - HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ - HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ - - /* Round 4 */ - II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ - II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ - II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ - II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ - II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ - II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ - II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ - II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ - II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ - II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ - II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ - II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ - II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ - II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ - II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ - II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ - - - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - - /* Zeroize sensitive information. -*/ - MD5_memset ((POINTER)x, 0, sizeof (x)); -} - -/* Encodes input (UINT4) into output (unsigned char). Assumes len is - a multiple of 4. - */ -static void Encode ( -unsigned char *output, -UINT4 *input, -unsigned int len) -{ - unsigned int i, j; - - for (i = 0, j = 0; j < len; i++, j += 4) { - output[j] = (unsigned char)(input[i] & 0xff); - output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); - output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); - output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); - } -} - - -/* Decodes input (unsigned char) into output (UINT4). Assumes len is - a multiple of 4. - */ -static void Decode ( -UINT4 *output, -unsigned char *input, -unsigned int len) -{ - unsigned int i, j; - - for (i = 0, j = 0; j < len; i++, j += 4) - output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | - (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24); -} - -/* Note: Replace "for loop" with standard memcpy if possible. - */ - -#ifndef MD5_memcpy -static void MD5_memcpy (output, input, len) -POINTER output; -POINTER input; -unsigned int len; -{ - unsigned int i; - - for (i = 0; i < len; i++) - output[i] = input[i]; -} -#endif - -/* Note: Replace "for loop" with standard memset if possible. - */ - -#ifndef MD5_memset -static void MD5_memset (output, value, len) -POINTER output; -int value; -unsigned int len; -{ - unsigned int i; - - for (i = 0; i < len; i++) - ((char *)output)[i] = (char)value; -} -#endif diff --git a/sql/md5.h b/sql/md5.h deleted file mode 100644 index 862129391f1..00000000000 --- a/sql/md5.h +++ /dev/null @@ -1,80 +0,0 @@ - -/* MD5.H - header file for MD5C.C - */ - -/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All -rights reserved. - -License to copy and use this software is granted provided that it -is identified as the "RSA Data Security, Inc. MD5 Message-Digest -Algorithm" in all material mentioning or referencing this software -or this function. - -License is also granted to make and use derivative works provided -that such works are identified as "derived from the RSA Data -Security, Inc. MD5 Message-Digest Algorithm" in all material -mentioning or referencing the derived work. - -RSA Data Security, Inc. makes no representations concerning either -the merchantability of this software or the suitability of this -software for any particular purpose. It is provided "as is" -without express or implied warranty of any kind. - -These notices must be retained in any copies of any part of this -documentation and/or software. - */ - -/* GLOBAL.H - RSAREF types and constants - */ - -/* PROTOTYPES should be set to one if and only if the compiler supports - function argument prototyping. -The following makes PROTOTYPES default to 0 if it has not already - been defined with C compiler flags. - */ - -/* egcs 1.1.2 under linux didn't defined it.... :( */ - -#ifndef PROTOTYPES -#define PROTOTYPES 1 /* Assume prototypes */ -#endif - -/* POINTER defines a generic pointer type */ -typedef unsigned char *POINTER; - -/* UINT2 defines a two byte word */ -typedef uint16 UINT2; /* Fix for MySQL / Alpha */ - -/* UINT4 defines a four byte word */ -typedef uint32 UINT4; /* Fix for MySQL / Alpha */ - -/* PROTO_LIST is defined depending on how PROTOTYPES is defined above. -If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it - returns an empty list. - */ -#if PROTOTYPES -#define PROTO_LIST(list) list -#else -#define PROTO_LIST(list) () -#endif - - -/* MD5 context. */ -typedef struct { - UINT4 state[4]; /* state (ABCD) */ - UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ - unsigned char buffer[64]; /* input buffer */ -} MD5_CTX; - -#ifdef __cplusplus -extern "C" { -#endif - void MD5Init PROTO_LIST ((MD5_CTX *)); - void MD5Update PROTO_LIST - ((MD5_CTX *, unsigned char *, unsigned int)); - void MD5Final PROTO_LIST ((unsigned char [16], MD5_CTX *)); - -#ifdef __cplusplus -} -#endif - diff --git a/sql/mf_iocache.cc b/sql/mf_iocache.cc index cddacaa820f..c79317cfeb3 100644 --- a/sql/mf_iocache.cc +++ b/sql/mf_iocache.cc @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -42,293 +42,6 @@ static void my_aiowait(my_aio_result *result); extern "C" { /* - ** if cachesize == 0 then use default cachesize (from s-file) - ** if file == -1 then real_open_cached_file() will be called. - ** returns 0 if ok - */ - -int init_io_cache(IO_CACHE *info, File file, uint cachesize, - enum cache_type type, my_off_t seek_offset, - pbool use_async_io, myf cache_myflags) -{ - uint min_cache; - DBUG_ENTER("init_io_cache"); - DBUG_PRINT("enter",("type: %d pos: %ld",(int) type, (ulong) seek_offset)); - - /* There is no file in net_reading */ - info->file= file; - if (!cachesize) - if (! (cachesize= my_default_record_cache_size)) - DBUG_RETURN(1); /* No cache requested */ - min_cache=use_async_io ? IO_SIZE*4 : IO_SIZE*2; - if (type == READ_CACHE) - { /* Assume file isn't growing */ - if (cache_myflags & MY_DONT_CHECK_FILESIZE) - { - cache_myflags &= ~MY_DONT_CHECK_FILESIZE; - } - else - { - my_off_t file_pos,end_of_file; - if ((file_pos=my_tell(file,MYF(0)) == MY_FILEPOS_ERROR)) - DBUG_RETURN(1); - end_of_file=my_seek(file,0L,MY_SEEK_END,MYF(0)); - if (end_of_file < seek_offset) - end_of_file=seek_offset; - VOID(my_seek(file,file_pos,MY_SEEK_SET,MYF(0))); - if ((my_off_t) cachesize > end_of_file-seek_offset+IO_SIZE*2-1) - { - cachesize=(uint) (end_of_file-seek_offset)+IO_SIZE*2-1; - use_async_io=0; /* No nead to use async */ - } - } - } - if ((int) type < (int) READ_NET) - { - for (;;) - { - cachesize=(uint) ((ulong) (cachesize + min_cache-1) & - (ulong) ~(min_cache-1)); - if (cachesize < min_cache) - cachesize = min_cache; - if ((info->buffer= - (byte*) my_malloc(cachesize, - MYF((cache_myflags & ~ MY_WME) | - (cachesize == min_cache ? MY_WME : 0)))) != 0) - break; /* Enough memory found */ - if (cachesize == min_cache) - DBUG_RETURN(2); /* Can't alloc cache */ - cachesize= (uint) ((long) cachesize*3/4); /* Try with less memory */ - } - } - else - info->buffer=0; - DBUG_PRINT("info",("init_io_cache: cachesize = %u",cachesize)); - info->pos_in_file= seek_offset; - info->read_length=info->buffer_length=cachesize; - info->seek_not_done= test(file >= 0 && type != READ_FIFO && - type != READ_NET); - info->myflags=cache_myflags & ~(MY_NABP | MY_FNABP); - info->rc_request_pos=info->rc_pos=info->buffer; - - if (type == READ_CACHE || type == READ_NET || type == READ_FIFO) - { - info->rc_end=info->buffer; /* Nothing in cache */ - } - else /* type == WRITE_CACHE */ - { - info->rc_end=info->buffer+info->buffer_length- (seek_offset & (IO_SIZE-1)); - } - /* end_of_file may be changed by user later */ - info->end_of_file= ((type == READ_NET || type == READ_FIFO ) ? 0 - : ~(my_off_t) 0); - info->type=type; - info->error=0; - info->read_function=(type == READ_NET) ? _my_b_net_read : _my_b_read; /* net | file */ -#ifdef HAVE_AIOWAIT - if (use_async_io && ! my_disable_async_io) - { - DBUG_PRINT("info",("Using async io")); - info->read_length/=2; - info->read_function=_my_b_async_read; - } - info->inited=info->aio_result.pending=0; -#endif - DBUG_RETURN(0); -} /* init_io_cache */ - - - /* Wait until current request is ready */ - -#ifdef HAVE_AIOWAIT -static void my_aiowait(my_aio_result *result) -{ - if (result->pending) - { - struct aio_result_t *tmp; - for (;;) - { - if ((int) (tmp=aiowait((struct timeval *) 0)) == -1) - { - if (errno == EINTR) - continue; - DBUG_PRINT("error",("No aio request, error: %d",errno)); - result->pending=0; /* Assume everythings is ok */ - break; - } - ((my_aio_result*) tmp)->pending=0; - if ((my_aio_result*) tmp == result) - break; - } - } - return; -} -#endif - - /* Use this to reset cache to start or other type */ - /* Some simple optimizing is done when reinit in current buffer */ - -my_bool reinit_io_cache(IO_CACHE *info, enum cache_type type, - my_off_t seek_offset, - pbool use_async_io __attribute__((unused)), - pbool clear_cache) -{ - DBUG_ENTER("reinit_io_cache"); - - info->seek_not_done= test(info->file >= 0); /* Seek not done */ - - /* If the whole file is in memory, avoid flushing to disk */ - if (! clear_cache && - seek_offset >= info->pos_in_file && - seek_offset <= info->pos_in_file + - (uint) (info->rc_end - info->rc_request_pos)) - { /* use current buffer */ - if (info->type == WRITE_CACHE && type == READ_CACHE) - { - info->rc_end=info->rc_pos; - info->end_of_file=my_b_tell(info); - } - else if (type == WRITE_CACHE) - { - if (info->type == READ_CACHE) - info->rc_end=info->buffer+info->buffer_length; - info->end_of_file = ~(my_off_t) 0; - } - info->rc_pos=info->rc_request_pos+(seek_offset-info->pos_in_file); -#ifdef HAVE_AIOWAIT - my_aiowait(&info->aio_result); /* Wait for outstanding req */ -#endif - } - else - { - /* - If we change from WRITE_CACHE to READ_CACHE, assume that everything - after the current positions should be ignored - */ - if (info->type == WRITE_CACHE && type == READ_CACHE) - info->end_of_file=my_b_tell(info); - /* No need to flush cache if we want to reuse it */ - if ((type != WRITE_CACHE || !clear_cache) && flush_io_cache(info)) - DBUG_RETURN(1); - if (info->pos_in_file != seek_offset) - { - info->pos_in_file=seek_offset; - info->seek_not_done=1; - } - info->rc_request_pos=info->rc_pos=info->buffer; - if (type == READ_CACHE || type == READ_NET || type == READ_FIFO) - { - info->rc_end=info->buffer; /* Nothing in cache */ - } - else - { - info->rc_end=info->buffer+info->buffer_length- - (seek_offset & (IO_SIZE-1)); - info->end_of_file= ((type == READ_NET || type == READ_FIFO) ? 0 : - ~(my_off_t) 0); - } - } - info->type=type; - info->error=0; - info->read_function=(type == READ_NET) ? _my_b_net_read : _my_b_read; -#ifdef HAVE_AIOWAIT - if (type != READ_NET) - { - if (use_async_io && ! my_disable_async_io && - ((ulong) info->buffer_length < - (ulong) (info->end_of_file - seek_offset))) - { - info->read_length=info->buffer_length/2; - info->read_function=_my_b_async_read; - } - } - info->inited=0; -#endif - DBUG_RETURN(0); -} /* init_io_cache */ - - - - /* - Read buffered. Returns 1 if can't read requested characters - This function is only called from the my_b_read() macro - when there isn't enough characters in the buffer to - satisfy the request. - Returns 0 we succeeded in reading all data - */ - -int _my_b_read(register IO_CACHE *info, byte *Buffer, uint Count) -{ - uint length,diff_length,left_length; - my_off_t max_length, pos_in_file; - - if ((left_length=(uint) (info->rc_end-info->rc_pos))) - { - dbug_assert(Count >= left_length); /* User is not using my_b_read() */ - memcpy(Buffer,info->rc_pos, (size_t) (left_length)); - Buffer+=left_length; - Count-=left_length; - } - /* pos_in_file always point on where info->buffer was read */ - pos_in_file=info->pos_in_file+(uint) (info->rc_end - info->buffer); - if (info->seek_not_done) - { /* File touched, do seek */ - VOID(my_seek(info->file,pos_in_file,MY_SEEK_SET,MYF(0))); - info->seek_not_done=0; - } - diff_length=(uint) (pos_in_file & (IO_SIZE-1)); - if (Count >= (uint) (IO_SIZE+(IO_SIZE-diff_length))) - { /* Fill first intern buffer */ - uint read_length; - if (info->end_of_file == pos_in_file) - { /* End of file */ - info->error=(int) left_length; - return 1; - } - length=(Count & (uint) ~(IO_SIZE-1))-diff_length; - if ((read_length=my_read(info->file,Buffer,(uint) length,info->myflags)) - != (uint) length) - { - info->error= read_length == (uint) -1 ? -1 : - (int) (read_length+left_length); - return 1; - } - Count-=length; - Buffer+=length; - pos_in_file+=length; - left_length+=length; - diff_length=0; - } - max_length=info->read_length-diff_length; - if (info->type != READ_FIFO && - (info->end_of_file - pos_in_file) < max_length) - max_length = info->end_of_file - pos_in_file; - if (!max_length) - { - if (Count) - { - info->error= left_length; /* We only got this many char */ - return 1; - } - length=0; /* Didn't read any chars */ - } - else if ((length=my_read(info->file,info->buffer,(uint) max_length, - info->myflags)) < Count || - length == (uint) -1) - { - if (length != (uint) -1) - memcpy(Buffer,info->buffer,(size_t) length); - info->error= length == (uint) -1 ? -1 : (int) (length+left_length); - return 1; - } - info->rc_pos=info->buffer+Count; - info->rc_end=info->buffer+length; - info->pos_in_file=pos_in_file; - memcpy(Buffer,info->buffer,(size_t) Count); - return 0; -} - - /* ** Read buffered from the net. ** Returns 1 if can't read requested characters ** Returns 0 if record read @@ -337,353 +50,38 @@ int _my_b_read(register IO_CACHE *info, byte *Buffer, uint Count) int _my_b_net_read(register IO_CACHE *info, byte *Buffer, uint Count __attribute__((unused))) { - int read_length; + ulong read_length; NET *net= &(current_thd)->net; + DBUG_ENTER("_my_b_net_read"); - if (info->end_of_file) - return 1; /* because my_b_get (no _) takes 1 byte at a time */ + if (!info->end_of_file) + DBUG_RETURN(1); /* because my_b_get (no _) takes 1 byte at a time */ read_length=my_net_read(net); - if (read_length == (int) packet_error) + if (read_length == packet_error) { info->error= -1; - return 1; + DBUG_RETURN(1); } if (read_length == 0) { - /* End of file from client */ - info->end_of_file = 1; return 1; + info->end_of_file= 0; /* End of file from client */ + DBUG_RETURN(1); } /* to set up stuff for my_b_get (no _) */ - info->rc_end = (info->rc_pos = (byte*) net->read_pos) + read_length; - Buffer[0] = info->rc_pos[0]; /* length is always 1 */ - info->rc_pos++; - return 0; -} - -#ifdef HAVE_AIOWAIT - -int _my_b_async_read(register IO_CACHE *info, byte *Buffer, uint Count) -{ - uint length,read_length,diff_length,left_length,use_length,org_Count; - my_off_t max_length; - my_off_t next_pos_in_file; - byte *read_buffer; - - memcpy(Buffer,info->rc_pos, - (size_t) (left_length=(uint) (info->rc_end-info->rc_pos))); - Buffer+=left_length; - org_Count=Count; - Count-=left_length; - - if (info->inited) - { /* wait for read block */ - info->inited=0; /* No more block to read */ - my_aiowait(&info->aio_result); /* Wait for outstanding req */ - if (info->aio_result.result.aio_errno) - { - if (info->myflags & MY_WME) - my_error(EE_READ, MYF(ME_BELL+ME_WAITTANG), - my_filename(info->file), - info->aio_result.result.aio_errno); - my_errno=info->aio_result.result.aio_errno; - info->error= -1; - return(1); - } - if (! (read_length = (uint) info->aio_result.result.aio_return) || - read_length == (uint) -1) - { - my_errno=0; /* For testing */ - info->error= (read_length == (uint) -1 ? -1 : - (int) (read_length+left_length)); - return(1); - } - info->pos_in_file+=(uint) (info->rc_end - info->rc_request_pos); - - if (info->rc_request_pos != info->buffer) - info->rc_request_pos=info->buffer; - else - info->rc_request_pos=info->buffer+info->read_length; - info->rc_pos=info->rc_request_pos; - next_pos_in_file=info->aio_read_pos+read_length; - - /* Check if pos_in_file is changed - (_ni_read_cache may have skipped some bytes) */ - - if (info->aio_read_pos < info->pos_in_file) - { /* Fix if skipped bytes */ - if (info->aio_read_pos + read_length < info->pos_in_file) - { - read_length=0; /* Skipp block */ - next_pos_in_file=info->pos_in_file; - } - else - { - my_off_t offset= (info->pos_in_file - info->aio_read_pos); - info->pos_in_file=info->aio_read_pos; /* Whe are here */ - info->rc_pos=info->rc_request_pos+offset; - read_length-=offset; /* Bytes left from rc_pos */ - } - } -#ifndef DBUG_OFF - if (info->aio_read_pos > info->pos_in_file) - { - my_errno=EINVAL; - return(info->read_length= -1); - } -#endif - /* Copy found bytes to buffer */ - length=min(Count,read_length); - memcpy(Buffer,info->rc_pos,(size_t) length); - Buffer+=length; - Count-=length; - left_length+=length; - info->rc_end=info->rc_pos+read_length; - info->rc_pos+=length; - } - else - next_pos_in_file=(info->pos_in_file+ (uint) - (info->rc_end - info->rc_request_pos)); - - /* If reading large blocks, or first read or read with skipp */ - if (Count) - { - if (next_pos_in_file == info->end_of_file) - { - info->error=(int) (read_length+left_length); - return 1; - } - VOID(my_seek(info->file,next_pos_in_file,MY_SEEK_SET,MYF(0))); - read_length=IO_SIZE*2- (uint) (next_pos_in_file & (IO_SIZE-1)); - if (Count < read_length) - { /* Small block, read to cache */ - if ((read_length=my_read(info->file,info->rc_request_pos, - read_length, info->myflags)) == (uint) -1) - return info->error= -1; - use_length=min(Count,read_length); - memcpy(Buffer,info->rc_request_pos,(size_t) use_length); - info->rc_pos=info->rc_request_pos+Count; - info->rc_end=info->rc_request_pos+read_length; - info->pos_in_file=next_pos_in_file; /* Start of block in cache */ - next_pos_in_file+=read_length; - - if (Count != use_length) - { /* Didn't find hole block */ - if (info->myflags & (MY_WME | MY_FAE | MY_FNABP) && Count != org_Count) - my_error(EE_EOFERR, MYF(ME_BELL+ME_WAITTANG), - my_filename(info->file),my_errno); - info->error=(int) (read_length+left_length); - return 1; - } - } - else - { /* Big block, don't cache it */ - if ((read_length=my_read(info->file,Buffer,(uint) Count,info->myflags)) - != Count) - { - info->error= read_length == (uint) -1 ? -1 : read_length+left_length; - return 1; - } - info->rc_pos=info->rc_end=info->rc_request_pos; - info->pos_in_file=(next_pos_in_file+=Count); - } - } - - /* Read next block with asyncronic io */ - max_length=info->end_of_file - next_pos_in_file; - diff_length=(next_pos_in_file & (IO_SIZE-1)); - - if (max_length > (my_off_t) info->read_length - diff_length) - max_length= (my_off_t) info->read_length - diff_length; - if (info->rc_request_pos != info->buffer) - read_buffer=info->buffer; - else - read_buffer=info->buffer+info->read_length; - info->aio_read_pos=next_pos_in_file; - if (max_length) - { - info->aio_result.result.aio_errno=AIO_INPROGRESS; /* Marker for test */ - DBUG_PRINT("aioread",("filepos: %ld length: %ld", - (ulong) next_pos_in_file,(ulong) max_length)); - if (aioread(info->file,read_buffer,(int) max_length, - (my_off_t) next_pos_in_file,MY_SEEK_SET, - &info->aio_result.result)) - { /* Skipp async io */ - my_errno=errno; - DBUG_PRINT("error",("got error: %d, aio_result: %d from aioread, async skipped", - errno, info->aio_result.result.aio_errno)); - if (info->rc_request_pos != info->buffer) - { - bmove(info->buffer,info->rc_request_pos, - (uint) (info->rc_end - info->rc_pos)); - info->rc_request_pos=info->buffer; - info->rc_pos-=info->read_length; - info->rc_end-=info->read_length; - } - info->read_length=info->buffer_length; /* Use hole buffer */ - info->read_function=_my_b_read; /* Use normal IO_READ next */ - } - else - info->inited=info->aio_result.pending=1; - } - return 0; /* Block read, async in use */ -} /* _my_b_async_read */ -#endif - - -/* Read one byte when buffer is empty */ - -int _my_b_get(IO_CACHE *info) -{ - byte buff; - if ((*(info)->read_function)(info,&buff,1)) - return my_b_EOF; - return (int) (uchar) buff; -} - - /* Returns != 0 if error on write */ - -int _my_b_write(register IO_CACHE *info, const byte *Buffer, uint Count) -{ - uint rest_length,length; - - rest_length=(uint) (info->rc_end - info->rc_pos); - memcpy(info->rc_pos,Buffer,(size_t) rest_length); - Buffer+=rest_length; - Count-=rest_length; - info->rc_pos+=rest_length; - if (info->pos_in_file+info->buffer_length > info->end_of_file) - { - my_errno=errno=EFBIG; - return info->error = -1; - } - if (flush_io_cache(info)) - return 1; - if (Count >= IO_SIZE) - { /* Fill first intern buffer */ - length=Count & (uint) ~(IO_SIZE-1); - if (info->seek_not_done) - { /* File touched, do seek */ - VOID(my_seek(info->file,info->pos_in_file,MY_SEEK_SET,MYF(0))); - info->seek_not_done=0; - } - if (my_write(info->file,Buffer,(uint) length,info->myflags | MY_NABP)) - return info->error= -1; - Count-=length; - Buffer+=length; - info->pos_in_file+=length; - } - memcpy(info->rc_pos,Buffer,(size_t) Count); - info->rc_pos+=Count; - return 0; -} - - -/* - Write a block to disk where part of the data may be inside the record - buffer. As all write calls to the data goes through the cache, - we will never get a seek over the end of the buffer -*/ - -int my_block_write(register IO_CACHE *info, const byte *Buffer, uint Count, - my_off_t pos) -{ - uint length; - int error=0; - - if (pos < info->pos_in_file) - { - /* Of no overlap, write everything without buffering */ - if (pos + Count <= info->pos_in_file) - return my_pwrite(info->file, Buffer, Count, pos, - info->myflags | MY_NABP); - /* Write the part of the block that is before buffer */ - length= (uint) (info->pos_in_file - pos); - if (my_pwrite(info->file, Buffer, length, pos, info->myflags | MY_NABP)) - info->error=error=-1; - Buffer+=length; - pos+= length; - Count-= length; - } - - /* Check if we want to write inside the used part of the buffer.*/ - length= (uint) (info->rc_end - info->buffer); - if (pos < info->pos_in_file + length) - { - uint offset= (uint) (pos - info->pos_in_file); - length-=offset; - if (length > Count) - length=Count; - memcpy(info->buffer+offset, Buffer, length); - Buffer+=length; - Count-= length; - /* Fix length of buffer if the new data was larger */ - if (info->buffer+length > info->rc_pos) - info->rc_pos=info->buffer+length; - if (!Count) - return (error); - } - /* Write at the end of the current buffer; This is the normal case */ - if (_my_b_write(info, Buffer, Count)) - error= -1; - return error; -} + info->read_end = (info->read_pos = (byte*) net->read_pos) + read_length; + Buffer[0] = info->read_pos[0]; /* length is always 1 */ - /* Flush write cache */ + /* + info->request_pos is used by log_loaded_block() to know the size + of the current block. + info->pos_in_file is used by log_loaded_block() too. + */ + info->pos_in_file+= read_length; + info->request_pos=info->read_pos; -int flush_io_cache(IO_CACHE *info) -{ - uint length; - DBUG_ENTER("flush_io_cache"); + info->read_pos++; - if (info->type == WRITE_CACHE) - { - if (info->file == -1) - { - if (real_open_cached_file(info)) - DBUG_RETURN((info->error= -1)); - } - if (info->rc_pos != info->buffer) - { - length=(uint) (info->rc_pos - info->buffer); - if (info->seek_not_done) - { /* File touched, do seek */ - if (my_seek(info->file,info->pos_in_file,MY_SEEK_SET,MYF(0)) == - MY_FILEPOS_ERROR) - DBUG_RETURN((info->error= -1)); - info->seek_not_done=0; - } - info->rc_pos=info->buffer; - info->pos_in_file+=length; - info->rc_end=(info->buffer+info->buffer_length- - (info->pos_in_file & (IO_SIZE-1))); - if (my_write(info->file,info->buffer,length,info->myflags | MY_NABP)) - DBUG_RETURN((info->error= -1)); - DBUG_RETURN(0); - } - } -#ifdef HAVE_AIOWAIT - else if (info->type != READ_NET) - { - my_aiowait(&info->aio_result); /* Wait for outstanding req */ - info->inited=0; - } -#endif DBUG_RETURN(0); } - -int end_io_cache(IO_CACHE *info) -{ - int error=0; - DBUG_ENTER("end_io_cache"); - if (info->buffer) - { - if (info->file != -1) /* File doesn't exist */ - error=flush_io_cache(info); - my_free((gptr) info->buffer,MYF(MY_WME)); - info->buffer=info->rc_pos=(byte*) 0; - } - DBUG_RETURN(error); -} /* end_io_cache */ - } /* extern "C" */ diff --git a/sql/mini_client.cc b/sql/mini_client.cc index 453f27822d9..38b3c22b91b 100644 --- a/sql/mini_client.cc +++ b/sql/mini_client.cc @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -20,27 +20,17 @@ Note: all file-global symbols must begin with mc_ , even the static ones, just in case we decide to make them external at some point - */ - -#define DONT_USE_RAID -#if defined(__WIN__) -#include <winsock.h> -#include <odbcinst.h> -/* Disable alarms */ -typedef my_bool ALARM; -#define thr_alarm_init(A) (*(A))=0 -#define thr_alarm_in_use(A) (*(A)) -#define thr_end_alarm(A) -#define thr_alarm(A,B,C) local_thr_alarm((A),(B),(C)) -inline int local_thr_alarm(my_bool *A,int B __attribute__((unused)),ALARM *C __attribute__((unused))) -{ - *A=1; - return 0; -} -#define thr_got_alarm(A) 0 -#endif +*/ -#include <global.h> +#include <my_global.h> +/* my_pthread must be included early to be able to fix things */ +#if defined(THREAD) +#include <my_pthread.h> /* because of signal() */ +#endif +#include <thr_alarm.h> +#include <mysql_embed.h> +#include <mysql_com.h> +#include <violite.h> #include <my_sys.h> #include <mysys_err.h> #include <m_string.h> @@ -50,15 +40,12 @@ inline int local_thr_alarm(my_bool *A,int B __attribute__((unused)),ALARM *C __a #include "mysql_version.h" #include "mysqld_error.h" #include "errmsg.h" -#include <violite.h> -#if defined( OS2) && defined( MYSQL_SERVER) +#if defined( OS2) && defined(MYSQL_SERVER) #undef ER #define ER CER #endif -extern ulong net_read_timeout; - extern "C" { // Because of SCO 3.2V4.2 #include <sys/stat.h> #include <signal.h> @@ -76,23 +63,27 @@ extern "C" { // Because of SCO 3.2V4.2 #ifdef HAVE_SYS_SELECT_H #include <sys/select.h> #endif -#endif +#endif /*!defined(MSDOS) && !defined(__WIN__) */ #ifdef HAVE_SYS_UN_H # include <sys/un.h> #endif -#if defined(THREAD) -#include <my_pthread.h> /* because of signal() */ -#include <thr_alarm.h> -#endif #ifndef INADDR_NONE #define INADDR_NONE -1 #endif - } -static void mc_end_server(MYSQL *mysql); +static void mc_free_rows(MYSQL_DATA *cur); +void mc_end_server(MYSQL *mysql); static int mc_sock_connect(File s, const struct sockaddr *name, uint namelen, uint to); static void mc_free_old_query(MYSQL *mysql); +static int mc_send_file_to_server(MYSQL *mysql, const char *filename); +static my_ulonglong mc_net_field_length_ll(uchar **packet); +static ulong mc_net_field_length(uchar **packet); +static int mc_read_one_row(MYSQL *mysql,uint fields,MYSQL_ROW row, + ulong *lengths); +static MYSQL_DATA *mc_read_rows(MYSQL *mysql,MYSQL_FIELD *mysql_fields, + uint fields); + #define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | CLIENT_LOCAL_FILES) @@ -189,8 +180,7 @@ HANDLE create_named_pipe(NET *net, uint connect_timeout, char **arg_host, ** Init MySQL structure or allocate one ****************************************************************************/ -MYSQL * STDCALL -mc_mysql_init(MYSQL *mysql) +MYSQL *mc_mysql_init(MYSQL *mysql) { init_client_errs(); if (!mysql) @@ -205,7 +195,7 @@ mc_mysql_init(MYSQL *mysql) #ifdef __WIN__ mysql->options.connect_timeout=20; #endif - mysql->net.timeout = slave_net_timeout; + mysql->net.read_timeout = slave_net_timeout; return mysql; } @@ -213,7 +203,7 @@ mc_mysql_init(MYSQL *mysql) ** Shut down connection **************************************************************************/ -static void +void mc_end_server(MYSQL *mysql) { DBUG_ENTER("mc_end_server"); @@ -304,11 +294,11 @@ static int mc_sock_connect(my_socket s, const struct sockaddr *name, FD_SET(s, &sfds); tv.tv_sec = (long) to; tv.tv_usec = 0; -#ifdef HPUX +#ifdef HPUX10 res = select(s+1, NULL, (int*) &sfds, NULL, &tv); #else res = select(s+1, NULL, &sfds, NULL, &tv); -#endif +#endif /* HPUX10 */ if (res <= 0) /* Never became writable */ return(-1); @@ -335,11 +325,11 @@ static int mc_sock_connect(my_socket s, const struct sockaddr *name, ** or packet is an error message *****************************************************************************/ -uint STDCALL +ulong mc_net_safe_read(MYSQL *mysql) { NET *net= &mysql->net; - uint len=0; + ulong len=0; if (net->vio != 0) len=my_net_read(net); @@ -351,7 +341,7 @@ mc_net_safe_read(MYSQL *mysql) if (socket_errno != SOCKET_EINTR) { mc_end_server(mysql); - if(net->last_errno != ER_NET_PACKET_TOO_LARGE) + if (net->last_errno != ER_NET_PACKET_TOO_LARGE) { net->last_errno=CR_SERVER_LOST; strmov(net->last_error,ER(net->last_errno)); @@ -359,7 +349,7 @@ mc_net_safe_read(MYSQL *mysql) else strmov(net->last_error, "Packet too large - increase \ max_allowed_packet on this server"); - } + } return(packet_error); } if (net->read_pos[0] == 255) @@ -372,7 +362,7 @@ max_allowed_packet on this server"); net->last_errno=uint2korr(pos); pos+=2; len-=2; - if(!net->last_errno) + if (!net->last_errno) net->last_errno = CR_UNKNOWN_ERROR; } else @@ -396,34 +386,38 @@ max_allowed_packet on this server"); } -char * STDCALL mc_mysql_error(MYSQL *mysql) +char *mc_mysql_error(MYSQL *mysql) { return (mysql)->net.last_error; } -int STDCALL mc_mysql_errno(MYSQL *mysql) +int mc_mysql_errno(MYSQL *mysql) { return (mysql)->net.last_errno; } -my_bool STDCALL mc_mysql_reconnect(MYSQL *mysql) + +my_bool mc_mysql_reconnect(MYSQL *mysql) { MYSQL tmp_mysql; DBUG_ENTER("mc_mysql_reconnect"); if (!mysql->reconnect) + { + mysql->net.last_errno=CR_SERVER_GONE_ERROR; + strmov(mysql->net.last_error, ER(mysql->net.last_errno)); DBUG_RETURN(1); - + } mc_mysql_init(&tmp_mysql); tmp_mysql.options=mysql->options; if (!mc_mysql_connect(&tmp_mysql,mysql->host,mysql->user,mysql->passwd, - mysql->db, mysql->port, mysql->unix_socket, - mysql->client_flag)) - { - tmp_mysql.reconnect=0; - mc_mysql_close(&tmp_mysql); - DBUG_RETURN(1); - } + mysql->db, mysql->port, mysql->unix_socket, + mysql->client_flag, mysql->net.read_timeout)) + { + mysql->net.last_errno= tmp_mysql.net.last_errno; + strmov(mysql->net.last_error, tmp_mysql.net.last_error); + DBUG_RETURN(1); + } tmp_mysql.free_me=mysql->free_me; mysql->free_me=0; bzero((char*) &mysql->options,sizeof(&mysql->options)); @@ -436,7 +430,7 @@ my_bool STDCALL mc_mysql_reconnect(MYSQL *mysql) -int STDCALL +int mc_simple_command(MYSQL *mysql,enum enum_server_command command, const char *arg, uint length, my_bool skipp_check) { @@ -446,11 +440,7 @@ mc_simple_command(MYSQL *mysql,enum enum_server_command command, if (mysql->net.vio == 0) { /* Do reconnect if possible */ if (mc_mysql_reconnect(mysql)) - { - net->last_errno=CR_SERVER_GONE_ERROR; - strmov(net->last_error,ER(net->last_errno)); goto end; - } } if (mysql->status != MYSQL_STATUS_READY) { @@ -471,8 +461,9 @@ mc_simple_command(MYSQL *mysql,enum enum_server_command command, { DBUG_PRINT("error",("Can't send command to server. Error: %d",socket_errno)); mc_end_server(mysql); - if (mc_mysql_reconnect(mysql) || - net_write_command(net,(uchar) command,arg, + if (mc_mysql_reconnect(mysql)) + goto end; + if (net_write_command(net,(uchar) command,arg, length ? length :(uint) strlen(arg))) { net->last_errno=CR_SERVER_GONE_ERROR; @@ -489,19 +480,21 @@ mc_simple_command(MYSQL *mysql,enum enum_server_command command, } -MYSQL * STDCALL +MYSQL * mc_mysql_connect(MYSQL *mysql,const char *host, const char *user, - const char *passwd, const char *db, - uint port, const char *unix_socket,uint client_flag) + const char *passwd, const char *db, + uint port, const char *unix_socket,uint client_flag, + uint net_read_timeout) { - char buff[100],*end,*host_info; + char buff[NAME_LEN+USERNAME_LENGTH+100],*end,*host_info; my_socket sock; ulong ip_addr; struct sockaddr_in sock_addr; - uint pkt_length; + ulong pkt_length; NET *net= &mysql->net; thr_alarm_t alarmed; - ALARM alarm_buff; + ALARM alarm_buff; + ulong max_allowed_packet; #ifdef __WIN__ HANDLE hPipe=INVALID_HANDLE_VALUE; @@ -510,15 +503,13 @@ mc_mysql_connect(MYSQL *mysql,const char *host, const char *user, struct sockaddr_un UNIXaddr; #endif DBUG_ENTER("mc_mysql_connect"); - - DBUG_PRINT("enter",("host: %s db: %s user: %s", + DBUG_PRINT("enter",("host: %s db: %s user: %s connect_time_out: %u read_timeout: %u", host ? host : "(Null)", db ? db : "(Null)", - user ? user : "(Null)")); - thr_alarm_init(&alarmed); - thr_alarm(&alarmed,(uint) net_read_timeout,&alarm_buff); + user ? user : "(Null)", + net_read_timeout, + (uint) slave_net_timeout)); - bzero((char*) &mysql->options,sizeof(mysql->options)); net->vio = 0; /* If something goes wrong */ mysql->charset=default_charset_info; /* Set character set */ if (!port) @@ -527,13 +518,16 @@ mc_mysql_connect(MYSQL *mysql,const char *host, const char *user, unix_socket=MYSQL_UNIX_ADDR; mysql->reconnect=1; /* Reconnect as default */ + mysql->server_status=SERVER_STATUS_AUTOCOMMIT; + if (!mysql->options.connect_timeout) + mysql->options.connect_timeout= net_read_timeout; /* ** Grab a socket and connect it to the server */ #if defined(HAVE_SYS_UN_H) - if (!host || !strcmp(host,LOCAL_HOST)) + if ((!host || !strcmp(host,LOCAL_HOST)) && unix_socket) { host=LOCAL_HOST; host_info=(char*) ER(CR_LOCALHOST_CONNECTION); @@ -548,10 +542,13 @@ mc_mysql_connect(MYSQL *mysql,const char *host, const char *user, bzero((char*) &UNIXaddr,sizeof(UNIXaddr)); UNIXaddr.sun_family = AF_UNIX; strmov(UNIXaddr.sun_path, unix_socket); - if (mc_sock_connect(sock,(struct sockaddr *) &UNIXaddr, sizeof(UNIXaddr), + if (mc_sock_connect(sock, + my_reinterpret_cast(struct sockaddr *) (&UNIXaddr), + sizeof(UNIXaddr), mysql->options.connect_timeout) <0) { - DBUG_PRINT("error",("Got error %d on connect to local server",socket_errno)); + DBUG_PRINT("error",("Got error %d on connect to local server", + socket_errno)); net->last_errno=CR_CONNECTION_ERROR; sprintf(net->last_error,ER(net->last_errno),unix_socket,socket_errno); goto error; @@ -598,7 +595,11 @@ mc_mysql_connect(MYSQL *mysql,const char *host, const char *user, host=LOCAL_HOST; sprintf(host_info=buff,ER(CR_TCP_CONNECTION),host); DBUG_PRINT("info",("Server name: '%s'. TCP sock: %d", host,port)); - if ((sock = socket(AF_INET,SOCK_STREAM,0)) == SOCKET_ERROR) + thr_alarm_init(&alarmed); + thr_alarm(&alarmed, net_read_timeout, &alarm_buff); + sock = (my_socket) socket(AF_INET,SOCK_STREAM,0); + thr_end_alarm(&alarmed); + if (sock == SOCKET_ERROR) { net->last_errno=CR_IPSOCK_ERROR; sprintf(net->last_error,ER(net->last_errno),socket_errno); @@ -634,37 +635,42 @@ mc_mysql_connect(MYSQL *mysql,const char *host, const char *user, my_gethostbyname_r_free(); } sock_addr.sin_port = (ushort) htons((ushort) port); - if (mc_sock_connect(sock,(struct sockaddr *) &sock_addr, sizeof(sock_addr), + if (mc_sock_connect(sock, + my_reinterpret_cast(struct sockaddr *) (&sock_addr), + sizeof(sock_addr), mysql->options.connect_timeout) <0) { DBUG_PRINT("error",("Got error %d on connect to '%s'", socket_errno,host)); net->last_errno= CR_CONN_HOST_ERROR; sprintf(net->last_error ,ER(CR_CONN_HOST_ERROR), host, socket_errno); - if (thr_alarm_in_use(&alarmed)) - thr_end_alarm(&alarmed); goto error; } - if (thr_alarm_in_use(&alarmed)) - thr_end_alarm(&alarmed); } if (!net->vio || my_net_init(net, net->vio)) { vio_delete(net->vio); - net->vio = 0; // safety + net->vio = 0; net->last_errno=CR_OUT_OF_MEMORY; strmov(net->last_error,ER(net->last_errno)); goto error; } vio_keepalive(net->vio,TRUE); - net->timeout=slave_net_timeout; + net->read_timeout=slave_net_timeout; /* Get version info */ mysql->protocol_version= PROTOCOL_VERSION; /* Assume this */ + if (mysql->options.connect_timeout && + vio_poll_read(net->vio, mysql->options.connect_timeout)) + { + net->last_errno= CR_SERVER_LOST; + strmov(net->last_error,ER(net->last_errno)); + goto error; + } if ((pkt_length=mc_net_safe_read(mysql)) == packet_error) goto error; - /* Check if version of protocoll matches current one */ + /* Check if version of protocol matches current one */ mysql->protocol_version= net->read_pos[0]; DBUG_DUMP("packet",(char*) net->read_pos,10); @@ -682,8 +688,15 @@ mc_mysql_connect(MYSQL *mysql,const char *host, const char *user, mysql->thread_id=uint4korr(end+1); end+=5; strmake(mysql->scramble_buff,end,8); - if (pkt_length > (uint) (end+9 - (char*) net->read_pos)) - mysql->server_capabilities=uint2korr(end+9); + end+=9; + if (pkt_length >= (uint) (end+1 - (char*) net->read_pos)) + mysql->server_capabilities=uint2korr(end); + if (pkt_length >= (uint) (end+18 - (char*) net->read_pos)) + { + /* New protocol with 16 bytes to describe server characteristics */ + mysql->server_language=end[2]; + mysql->server_status=uint2korr(end+3); + } /* Save connection information */ if (!user) user=""; @@ -710,7 +723,7 @@ mc_mysql_connect(MYSQL *mysql,const char *host, const char *user, mysql->unix_socket=0; strmov(mysql->server_version,(char*) net->read_pos+1); mysql->port=port; - mysql->client_flag=client_flag | mysql->options.client_flag; + client_flag|=mysql->options.client_flag; DBUG_PRINT("info",("Server version = '%s' capabilites: %ld", mysql->server_version,mysql->server_capabilities)); @@ -718,6 +731,10 @@ mc_mysql_connect(MYSQL *mysql,const char *host, const char *user, client_flag|=CLIENT_CAPABILITIES; #ifdef HAVE_OPENSSL + if (mysql->options.ssl_key || mysql->options.ssl_cert || + mysql->options.ssl_ca || mysql->options.ssl_capath || + mysql->options.ssl_cipher) + mysql->options.use_ssl= 1; if (mysql->options.use_ssl) client_flag|=CLIENT_SSL; #endif /* HAVE_OPENSSL */ @@ -725,8 +742,8 @@ mc_mysql_connect(MYSQL *mysql,const char *host, const char *user, if (db) client_flag|=CLIENT_CONNECT_WITH_DB; #ifdef HAVE_COMPRESS - if (mysql->server_capabilities & CLIENT_COMPRESS && - (mysql->options.compress || client_flag & CLIENT_COMPRESS)) + if ((mysql->server_capabilities & CLIENT_COMPRESS) && + (mysql->options.compress || (client_flag & CLIENT_COMPRESS))) client_flag|=CLIENT_COMPRESS; /* We will use compression */ else #endif @@ -753,42 +770,54 @@ mc_mysql_connect(MYSQL *mysql,const char *host, const char *user, mysql->client_flag=client_flag; #ifdef HAVE_OPENSSL - /* Oops.. are we careful enough to not send ANY information */ - /* without encryption? */ + /* + Oops.. are we careful enough to not send ANY information without + encryption? + */ if (client_flag & CLIENT_SSL) { if (my_net_write(net,buff,(uint) (2)) || net_flush(net)) + { + net->last_errno= CR_SERVER_LOST; + strmov(net->last_error,ER(net->last_errno)); goto error; + } /* Do the SSL layering. */ DBUG_PRINT("info", ("IO layer change in progress...")); - VioSSLConnectorFd* connector_fd = (VioSSLConnectorFd*) - (mysql->connector_fd); - VioSocket* vio_socket = (VioSocket*)(mysql->net.vio); - VioSSL* vio_ssl = connector_fd->connect(vio_socket); - mysql->net.vio = (NetVio*)(vio_ssl); + DBUG_PRINT("info", ("IO context %p",((struct st_VioSSLConnectorFd*)mysql->connector_fd)->ssl_context_)); + sslconnect((struct st_VioSSLConnectorFd*)(mysql->connector_fd),mysql->net.vio, (long)(mysql->options.connect_timeout)); + DBUG_PRINT("info", ("IO layer change done!")); } #endif /* HAVE_OPENSSL */ - + max_allowed_packet=mysql->net.max_packet_size; int3store(buff+2,max_allowed_packet); + + if (user && user[0]) strmake(buff+5,user,32); else - { - user = getenv("USER"); - if(!user) user = "mysql"; - strmov((char*) buff+5, user ); - } + { + user = getenv("USER"); + if (!user) user = "mysql"; + strmov((char*) buff+5, user ); + } DBUG_PRINT("info",("user: %s",buff+5)); end=scramble(strend(buff+5)+1, mysql->scramble_buff, passwd, (my_bool) (mysql->protocol_version == 9)); if (db) { - end=strmov(end+1,db); + end=strmake(end+1,db,NAME_LEN); mysql->db=my_strdup(db,MYF(MY_WME)); + db=0; + } + if (my_net_write(net,buff,(ulong) (end-buff)) || net_flush(net)) + { + net->last_errno= CR_SERVER_LOST; + strmov(net->last_error,ER(net->last_errno)); + goto error; } - if (my_net_write(net,buff,(uint) (end-buff)) || net_flush(net) || - mc_net_safe_read(mysql) == packet_error) + if (mc_net_safe_read(mysql) == packet_error) goto error; if (client_flag & CLIENT_COMPRESS) /* We will use compression */ net->compress=1; @@ -808,12 +837,40 @@ error: DBUG_RETURN(0); } + +#ifdef HAVE_OPENSSL +/* +************************************************************************** +** Free strings in the SSL structure and clear 'use_ssl' flag. +** NB! Errors are not reported until you do mysql_real_connect. +************************************************************************** +*/ +int +mysql_ssl_clear(MYSQL *mysql) +{ + my_free(mysql->options.ssl_key, MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->options.ssl_cert, MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->options.ssl_ca, MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->options.ssl_capath, MYF(MY_ALLOW_ZERO_PTR)); + my_free(mysql->options.ssl_cipher, MYF(MY_ALLOW_ZERO_PTR)); + mysql->options.ssl_key = 0; + mysql->options.ssl_cert = 0; + mysql->options.ssl_ca = 0; + mysql->options.ssl_capath = 0; + mysql->options.ssl_cipher= 0; + mysql->options.use_ssl = FALSE; + my_free(mysql->connector_fd,MYF(MY_ALLOW_ZERO_PTR)); + mysql->connector_fd = 0; + return 0; +} +#endif /* HAVE_OPENSSL */ + /************************************************************************* ** Send a QUIT to the server and close the connection ** If handle is alloced by mysql connect free it. *************************************************************************/ -void STDCALL +void mc_mysql_close(MYSQL *mysql) { DBUG_ENTER("mysql_close"); @@ -834,13 +891,503 @@ mc_mysql_close(MYSQL *mysql) /* Clear pointers for better safety */ mysql->host_info=mysql->user=mysql->passwd=mysql->db=0; bzero((char*) &mysql->options,sizeof(mysql->options)); - mysql->net.vio = 0; #ifdef HAVE_OPENSSL - ((VioConnectorFd*)(mysql->connector_fd))->delete(); - mysql->connector_fd = 0; + mysql_ssl_clear(mysql); #endif /* HAVE_OPENSSL */ if (mysql->free_me) my_free((gptr) mysql,MYF(0)); } DBUG_VOID_RETURN; } + +void mc_mysql_free_result(MYSQL_RES *result) +{ + DBUG_ENTER("mc_mysql_free_result"); + DBUG_PRINT("enter",("mysql_res: %lx",result)); + if (result) + { + if (result->handle && result->handle->status == MYSQL_STATUS_USE_RESULT) + { + DBUG_PRINT("warning",("Not all rows in set were read; Ignoring rows")); + for (;;) + { + ulong pkt_len; + if ((pkt_len=mc_net_safe_read(result->handle)) == packet_error) + break; + if (pkt_len == 1 && result->handle->net.read_pos[0] == 254) + break; /* End of data */ + } + result->handle->status=MYSQL_STATUS_READY; + } + mc_free_rows(result->data); + if (result->fields) + free_root(&result->field_alloc,MYF(0)); + if (result->row) + my_free((gptr) result->row,MYF(0)); + my_free((gptr) result,MYF(0)); + } + DBUG_VOID_RETURN; +} + +static void mc_free_rows(MYSQL_DATA *cur) +{ + if (cur) + { + free_root(&cur->alloc,MYF(0)); + my_free((gptr) cur,MYF(0)); + } +} + +static MYSQL_FIELD * +mc_unpack_fields(MYSQL_DATA *data,MEM_ROOT *alloc,uint fields, + my_bool default_value, my_bool long_flag_protocol) +{ + MYSQL_ROWS *row; + MYSQL_FIELD *field,*result; + DBUG_ENTER("unpack_fields"); + + field=result=(MYSQL_FIELD*) alloc_root(alloc,sizeof(MYSQL_FIELD)*fields); + if (!result) + DBUG_RETURN(0); + + for (row=data->data; row ; row = row->next,field++) + { + field->table= strdup_root(alloc,(char*) row->data[0]); + field->name= strdup_root(alloc,(char*) row->data[1]); + field->length= (uint) uint3korr(row->data[2]); + field->type= (enum enum_field_types) (uchar) row->data[3][0]; + if (long_flag_protocol) + { + field->flags= uint2korr(row->data[4]); + field->decimals=(uint) (uchar) row->data[4][2]; + } + else + { + field->flags= (uint) (uchar) row->data[4][0]; + field->decimals=(uint) (uchar) row->data[4][1]; + } + if (INTERNAL_NUM_FIELD(field)) + field->flags|= NUM_FLAG; + if (default_value && row->data[5]) + field->def=strdup_root(alloc,(char*) row->data[5]); + else + field->def=0; + field->max_length= 0; + } + mc_free_rows(data); /* Free old data */ + DBUG_RETURN(result); +} + +int mc_mysql_send_query(MYSQL* mysql, const char* query, uint length) +{ + return mc_simple_command(mysql, COM_QUERY, query, length, 1); +} + + +int mc_mysql_read_query_result(MYSQL *mysql) +{ + uchar *pos; + ulong field_count; + MYSQL_DATA *fields; + ulong length; + DBUG_ENTER("mc_mysql_read_query_result"); + + if ((length = mc_net_safe_read(mysql)) == packet_error) + DBUG_RETURN(-1); + mc_free_old_query(mysql); /* Free old result */ +get_info: + pos=(uchar*) mysql->net.read_pos; + if ((field_count= mc_net_field_length(&pos)) == 0) + { + mysql->affected_rows= mc_net_field_length_ll(&pos); + mysql->insert_id= mc_net_field_length_ll(&pos); + if (mysql->server_capabilities & CLIENT_TRANSACTIONS) + { + mysql->server_status=uint2korr(pos); pos+=2; + } + if (pos < mysql->net.read_pos+length && mc_net_field_length(&pos)) + mysql->info=(char*) pos; + DBUG_RETURN(0); + } + if (field_count == NULL_LENGTH) /* LOAD DATA LOCAL INFILE */ + { + int error=mc_send_file_to_server(mysql,(char*) pos); + if ((length=mc_net_safe_read(mysql)) == packet_error || error) + DBUG_RETURN(-1); + goto get_info; /* Get info packet */ + } + if (!(mysql->server_status & SERVER_STATUS_AUTOCOMMIT)) + mysql->server_status|= SERVER_STATUS_IN_TRANS; + + mysql->extra_info= mc_net_field_length_ll(&pos); /* Maybe number of rec */ + if (!(fields=mc_read_rows(mysql,(MYSQL_FIELD*) 0,5))) + DBUG_RETURN(-1); + if (!(mysql->fields=mc_unpack_fields(fields,&mysql->field_alloc, + (uint) field_count,0, + (my_bool) test(mysql->server_capabilities & + CLIENT_LONG_FLAG)))) + DBUG_RETURN(-1); + mysql->status=MYSQL_STATUS_GET_RESULT; + mysql->field_count=field_count; + DBUG_RETURN(0); +} + +int mc_mysql_query(MYSQL *mysql, const char *query, uint length) +{ + DBUG_ENTER("mysql_real_query"); + DBUG_PRINT("enter",("handle: %lx",mysql)); + DBUG_PRINT("query",("Query = \"%s\"",query)); + if (!length) + length = strlen(query); + if (mc_simple_command(mysql,COM_QUERY,query,length,1)) + DBUG_RETURN(-1); + DBUG_RETURN(mc_mysql_read_query_result(mysql)); +} + +static int mc_send_file_to_server(MYSQL *mysql, const char *filename) +{ + int fd, readcount, result= -1; + uint packet_length=MY_ALIGN(mysql->net.max_packet-16,IO_SIZE); + char *buf, tmp_name[FN_REFLEN]; + DBUG_ENTER("send_file_to_server"); + + if (!(buf=my_malloc(packet_length,MYF(0)))) + { + strmov(mysql->net.last_error, ER(mysql->net.last_errno=CR_OUT_OF_MEMORY)); + DBUG_RETURN(-1); + } + + fn_format(tmp_name,filename,"","",4); /* Convert to client format */ + if ((fd = my_open(tmp_name,O_RDONLY, MYF(0))) < 0) + { + my_net_write(&mysql->net,"",0); // Server needs one packet + net_flush(&mysql->net); + mysql->net.last_errno=EE_FILENOTFOUND; + my_snprintf(mysql->net.last_error,sizeof(mysql->net.last_error)-1, + EE(mysql->net.last_errno),tmp_name, errno); + goto err; + } + + while ((readcount = (int) my_read(fd,(byte*) buf,packet_length,MYF(0))) > 0) + { + if (my_net_write(&mysql->net,buf,readcount)) + { + DBUG_PRINT("error",("Lost connection to MySQL server during LOAD DATA of local file")); + mysql->net.last_errno=CR_SERVER_LOST; + strmov(mysql->net.last_error,ER(mysql->net.last_errno)); + goto err; + } + } + /* Send empty packet to mark end of file */ + if (my_net_write(&mysql->net,"",0) || net_flush(&mysql->net)) + { + mysql->net.last_errno=CR_SERVER_LOST; + sprintf(mysql->net.last_error,ER(mysql->net.last_errno),errno); + goto err; + } + if (readcount < 0) + { + mysql->net.last_errno=EE_READ; /* the errmsg for not entire file read */ + my_snprintf(mysql->net.last_error,sizeof(mysql->net.last_error)-1, + tmp_name,errno); + goto err; + } + result=0; // Ok + +err: + if (fd >= 0) + (void) my_close(fd,MYF(0)); + my_free(buf,MYF(0)); + DBUG_RETURN(result); +} + + +/* Get the length of next field. Change parameter to point at fieldstart */ +static ulong mc_net_field_length(uchar **packet) +{ + reg1 uchar *pos= *packet; + if (*pos < 251) + { + (*packet)++; + return (ulong) *pos; + } + if (*pos == 251) + { + (*packet)++; + return NULL_LENGTH; + } + if (*pos == 252) + { + (*packet)+=3; + return (ulong) uint2korr(pos+1); + } + if (*pos == 253) + { + (*packet)+=4; + return (ulong) uint3korr(pos+1); + } + (*packet)+=9; /* Must be 254 when here */ + return (ulong) uint4korr(pos+1); +} + +/* Same as above, but returns ulonglong values */ + +static my_ulonglong mc_net_field_length_ll(uchar **packet) +{ + reg1 uchar *pos= *packet; + if (*pos < 251) + { + (*packet)++; + return (my_ulonglong) *pos; + } + if (*pos == 251) + { + (*packet)++; + return (my_ulonglong) NULL_LENGTH; + } + if (*pos == 252) + { + (*packet)+=3; + return (my_ulonglong) uint2korr(pos+1); + } + if (*pos == 253) + { + (*packet)+=4; + return (my_ulonglong) uint3korr(pos+1); + } + (*packet)+=9; /* Must be 254 when here */ +#ifdef NO_CLIENT_LONGLONG + return (my_ulonglong) uint4korr(pos+1); +#else + return (my_ulonglong) uint8korr(pos+1); +#endif +} + +/* Read all rows (fields or data) from server */ + +static MYSQL_DATA *mc_read_rows(MYSQL *mysql,MYSQL_FIELD *mysql_fields, + uint fields) +{ + uint field; + ulong pkt_len; + ulong len; + uchar *cp; + char *to; + MYSQL_DATA *result; + MYSQL_ROWS **prev_ptr,*cur; + NET *net = &mysql->net; + DBUG_ENTER("mc_read_rows"); + + if ((pkt_len=mc_net_safe_read(mysql)) == packet_error) + DBUG_RETURN(0); + if (!(result=(MYSQL_DATA*) my_malloc(sizeof(MYSQL_DATA), + MYF(MY_ZEROFILL)))) + { + net->last_errno=CR_OUT_OF_MEMORY; + strmov(net->last_error,ER(net->last_errno)); + DBUG_RETURN(0); + } + init_alloc_root(&result->alloc,8192,0); /* Assume rowlength < 8192 */ + result->alloc.min_malloc=sizeof(MYSQL_ROWS); + prev_ptr= &result->data; + result->rows=0; + result->fields=fields; + + while (*(cp=net->read_pos) != 254 || pkt_len != 1) + { + result->rows++; + if (!(cur= (MYSQL_ROWS*) alloc_root(&result->alloc, + sizeof(MYSQL_ROWS))) || + !(cur->data= ((MYSQL_ROW) + alloc_root(&result->alloc, + (fields+1)*sizeof(char *)+pkt_len)))) + { + mc_free_rows(result); + net->last_errno=CR_OUT_OF_MEMORY; + strmov(net->last_error,ER(net->last_errno)); + DBUG_RETURN(0); + } + *prev_ptr=cur; + prev_ptr= &cur->next; + to= (char*) (cur->data+fields+1); + for (field=0 ; field < fields ; field++) + { + if ((len=(ulong) mc_net_field_length(&cp)) == NULL_LENGTH) + { /* null field */ + cur->data[field] = 0; + } + else + { + cur->data[field] = to; + memcpy(to,(char*) cp,len); to[len]=0; + to+=len+1; + cp+=len; + if (mysql_fields) + { + if (mysql_fields[field].max_length < len) + mysql_fields[field].max_length=len; + } + } + } + cur->data[field]=to; /* End of last field */ + if ((pkt_len=mc_net_safe_read(mysql)) == packet_error) + { + mc_free_rows(result); + DBUG_RETURN(0); + } + } + *prev_ptr=0; /* last pointer is null */ + DBUG_PRINT("exit",("Got %d rows",result->rows)); + DBUG_RETURN(result); +} + + +/* +** Read one row. Uses packet buffer as storage for fields. +** When next packet is read, the previous field values are destroyed +*/ + + +static int mc_read_one_row(MYSQL *mysql,uint fields,MYSQL_ROW row, + ulong *lengths) +{ + uint field; + ulong pkt_len,len; + uchar *pos,*prev_pos; + + if ((pkt_len=mc_net_safe_read(mysql)) == packet_error) + return -1; + if (pkt_len == 1 && mysql->net.read_pos[0] == 254) + return 1; /* End of data */ + prev_pos= 0; /* allowed to write at packet[-1] */ + pos=mysql->net.read_pos; + for (field=0 ; field < fields ; field++) + { + if ((len=(ulong) mc_net_field_length(&pos)) == NULL_LENGTH) + { /* null field */ + row[field] = 0; + *lengths++=0; + } + else + { + row[field] = (char*) pos; + pos+=len; + *lengths++=len; + } + if (prev_pos) + *prev_pos=0; /* Terminate prev field */ + prev_pos=pos; + } + row[field]=(char*) prev_pos+1; /* End of last field */ + *prev_pos=0; /* Terminate last field */ + return 0; +} + +my_ulonglong mc_mysql_num_rows(MYSQL_RES *res) +{ + return res->row_count; +} + +unsigned int mc_mysql_num_fields(MYSQL_RES *res) +{ + return res->field_count; +} + +void mc_mysql_data_seek(MYSQL_RES *result, my_ulonglong row) +{ + MYSQL_ROWS *tmp=0; + DBUG_PRINT("info",("mysql_data_seek(%ld)",(long) row)); + if (result->data) + for (tmp=result->data->data; row-- && tmp ; tmp = tmp->next) ; + result->current_row=0; + result->data_cursor = tmp; +} + +MYSQL_ROW STDCALL mc_mysql_fetch_row(MYSQL_RES *res) +{ + DBUG_ENTER("mc_mysql_fetch_row"); + if (!res->data) + { /* Unbufferred fetch */ + if (!res->eof) + { + if (!(mc_read_one_row(res->handle,res->field_count,res->row, + res->lengths))) + { + res->row_count++; + DBUG_RETURN(res->current_row=res->row); + } + else + { + DBUG_PRINT("info",("end of data")); + res->eof=1; + res->handle->status=MYSQL_STATUS_READY; + } + } + DBUG_RETURN((MYSQL_ROW) NULL); + } + { + MYSQL_ROW tmp; + if (!res->data_cursor) + { + DBUG_PRINT("info",("end of data")); + DBUG_RETURN(res->current_row=(MYSQL_ROW) NULL); + } + tmp = res->data_cursor->data; + res->data_cursor = res->data_cursor->next; + DBUG_RETURN(res->current_row=tmp); + } +} + +int mc_mysql_select_db(MYSQL *mysql, const char *db) +{ + int error; + DBUG_ENTER("mysql_select_db"); + DBUG_PRINT("enter",("db: '%s'",db)); + + if ((error=mc_simple_command(mysql,COM_INIT_DB,db,(uint) strlen(db),0))) + DBUG_RETURN(error); + my_free(mysql->db,MYF(MY_ALLOW_ZERO_PTR)); + mysql->db=my_strdup(db,MYF(MY_WME)); + DBUG_RETURN(0); +} + + +MYSQL_RES *mc_mysql_store_result(MYSQL *mysql) +{ + MYSQL_RES *result; + DBUG_ENTER("mysql_store_result"); + + if (!mysql->fields) + DBUG_RETURN(0); + if (mysql->status != MYSQL_STATUS_GET_RESULT) + { + strmov(mysql->net.last_error, + ER(mysql->net.last_errno=CR_COMMANDS_OUT_OF_SYNC)); + DBUG_RETURN(0); + } + mysql->status=MYSQL_STATUS_READY; /* server is ready */ + if (!(result=(MYSQL_RES*) my_malloc(sizeof(MYSQL_RES)+ + sizeof(ulong)*mysql->field_count, + MYF(MY_ZEROFILL)))) + { + mysql->net.last_errno=CR_OUT_OF_MEMORY; + strmov(mysql->net.last_error, ER(mysql->net.last_errno)); + DBUG_RETURN(0); + } + result->eof=1; /* Marker for buffered */ + result->lengths=(ulong*) (result+1); + if (!(result->data=mc_read_rows(mysql,mysql->fields,mysql->field_count))) + { + my_free((gptr) result,MYF(0)); + DBUG_RETURN(0); + } + mysql->affected_rows= result->row_count= result->data->rows; + result->data_cursor= result->data->data; + result->fields= mysql->fields; + result->field_alloc= mysql->field_alloc; + result->field_count= mysql->field_count; + result->current_field=0; + result->current_row=0; /* Must do a fetch first */ + mysql->fields=0; /* fields is now in result */ + DBUG_RETURN(result); /* Data fetched */ +} diff --git a/sql/mini_client.h b/sql/mini_client.h index f7d95a1b66e..24c13646170 100644 --- a/sql/mini_client.h +++ b/sql/mini_client.h @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -18,30 +18,29 @@ #define _MINI_CLIENT_H -MYSQL* STDCALL -mc_mysql_connect(MYSQL *mysql,const char *host, const char *user, - const char *passwd, const char *db, - uint port, const char *unix_socket,uint client_flag); - -int STDCALL -mc_simple_command(MYSQL *mysql,enum enum_server_command command, const char *arg, - uint length, my_bool skipp_check); -void STDCALL -mc_mysql_close(MYSQL *mysql); - -MYSQL * STDCALL -mc_mysql_init(MYSQL *mysql); - -void STDCALL -mc_mysql_debug(const char *debug); - -uint STDCALL -mc_net_safe_read(MYSQL *mysql); - -char * STDCALL mc_mysql_error(MYSQL *mysql); -int STDCALL mc_mysql_errno(MYSQL *mysql); -my_bool STDCALL mc_mysql_reconnect(MYSQL* mysql); - +MYSQL* mc_mysql_connect(MYSQL *mysql,const char *host, const char *user, + const char *passwd, const char *db, + uint port, const char *unix_socket,uint client_flag, + uint net_read_timeout); +int mc_simple_command(MYSQL *mysql,enum enum_server_command command, + const char *arg, uint length, my_bool skipp_check); +void mc_mysql_close(MYSQL *mysql); +MYSQL *mc_mysql_init(MYSQL *mysql); +void mc_mysql_debug(const char *debug); +ulong mc_net_safe_read(MYSQL *mysql); +char *mc_mysql_error(MYSQL *mysql); +int mc_mysql_errno(MYSQL *mysql); +my_bool mc_mysql_reconnect(MYSQL* mysql); +int mc_mysql_send_query(MYSQL* mysql, const char* query, uint length); +int mc_mysql_read_query_result(MYSQL *mysql); +int mc_mysql_query(MYSQL *mysql, const char *query, uint length); +MYSQL_RES * mc_mysql_store_result(MYSQL *mysql); +void mc_mysql_free_result(MYSQL_RES *result); +void mc_mysql_data_seek(MYSQL_RES *result, my_ulonglong row); +my_ulonglong mc_mysql_num_rows(MYSQL_RES *res); +unsigned int mc_mysql_num_fields(MYSQL_RES *res); +MYSQL_ROW STDCALL mc_mysql_fetch_row(MYSQL_RES *res); +int mc_mysql_select_db(MYSQL *mysql, const char *db); +void mc_end_server(MYSQL *mysql); #endif - diff --git a/sql/my_lock.c b/sql/my_lock.c index 647c07a03c3..7f47256703a 100644 --- a/sql/my_lock.c +++ b/sql/my_lock.c @@ -1,25 +1,25 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifdef __EMX__ +#if defined(__EMX__) || defined(__NETWARE__) #include "../mysys/my_lock.c" #else #undef MAP_TO_USE_RAID /* Avoid RAID mappings */ -#include <global.h> +#include <my_global.h> #include <my_sys.h> #include <mysys_err.h> #include <my_pthread.h> diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 833a816233e..c6e205f4729 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -14,22 +14,19 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef _MYSQL_PRIV_H -#define _MYSQL_PRIV_H - -#include <global.h> +#include <my_global.h> +#include <mysql_version.h> +#include <mysql_embed.h> #include <my_sys.h> #include <m_string.h> -#include "mysql_version.h" #include <hash.h> #include <signal.h> #include <thr_lock.h> #include <my_base.h> /* Needed by field.h */ #include <my_bitmap.h> -#include <violite.h> #ifdef __EMX__ -#undef write // remove pthread.h macro definition for EMX +#undef write /* remove pthread.h macro definition for EMX */ #endif typedef ulong table_map; /* Used for table bits in join */ @@ -37,6 +34,7 @@ typedef ulong key_map; /* Used for finding keys */ typedef ulong key_part_map; /* Used for finding key parts */ #include "mysql_com.h" +#include <violite.h> #include "unireg.h" void init_sql_alloc(MEM_ROOT *root, uint block_size, uint pre_alloc_size); @@ -47,20 +45,14 @@ char *sql_strmake(const char *str,uint len); gptr sql_memdup(const void * ptr,unsigned size); void sql_element_free(void *ptr); void kill_one_thread(THD *thd, ulong id); +bool net_request_file(NET* net, const char* fname); +char* query_table_status(THD *thd,const char *db,const char *table_name); #define x_free(A) { my_free((gptr) (A),MYF(MY_WME | MY_FAE | MY_ALLOW_ZERO_PTR)); } #define safeFree(x) { if(x) { my_free((gptr) x,MYF(0)); x = NULL; } } #define PREV_BITS(type,A) ((type) (((type) 1 << (A)) -1)) #define all_bits_set(A,B) ((A) & (B) != (B)) -#ifndef LL -#ifdef HAVE_LONG_LONG -#define LL(A) A ## LL -#else -#define LL(A) A ## L -#endif -#endif - /*************************************************************************** Configuration parameters ****************************************************************************/ @@ -69,21 +61,21 @@ void kill_one_thread(THD *thd, ulong id); #define HASH_PASSWORD_LENGTH 16 #define HOST_CACHE_SIZE 128 #define MAX_ACCEPT_RETRY 10 // Test accept this many times -#define MAX_BLOB_WIDTH 8192 // Default width for blob #define MAX_FIELDS_BEFORE_HASH 32 #define USER_VARS_HASH_SIZE 16 #define STACK_MIN_SIZE 8192 // Abort if less stack during eval. -#define STACK_BUFF_ALLOC 32 // For stack overrun checks +#define STACK_BUFF_ALLOC 64 // For stack overrun checks #ifndef MYSQLD_NET_RETRY_COUNT #define MYSQLD_NET_RETRY_COUNT 10 // Abort read after this many int. #endif #define TEMP_POOL_SIZE 128 /* The following parameters is to decide when to use an extra cache to - optimise seeks when reading a big table in sorted order + optimise seeks when reading a big table in sorted order */ #define MIN_FILE_LENGTH_TO_USE_ROW_CACHE (16L*1024*1024) #define MIN_ROWS_TO_USE_TABLE_CACHE 100 +#define MIN_ROWS_TO_USE_BULK_INSERT 100 /* The following is used to decide if MySQL should use table scanning @@ -155,52 +147,69 @@ void kill_one_thread(THD *thd, ulong id); #define SELECT_DESCRIBE 4 #define SELECT_SMALL_RESULT 8 #define SELECT_BIG_RESULT 16 -#define SELECT_HIGH_PRIORITY 64 /* Intern */ -#define SELECT_USE_CACHE 256 /* Intern */ -#define SELECT_COUNT_DISTINCT 512 /* Intern */ +#define OPTION_FOUND_ROWS 32 +#define OPTION_TO_QUERY_CACHE 64 +#define SELECT_NO_JOIN_CACHE 256 /* Intern */ #define OPTION_BIG_TABLES 512 /* for SQL OPTION */ #define OPTION_BIG_SELECTS 1024 /* for SQL OPTION */ #define OPTION_LOG_OFF 2048 #define OPTION_UPDATE_LOG 4096 /* update log flag */ -#define OPTION_LOW_PRIORITY_UPDATES 8192 +#define TMP_TABLE_ALL_COLUMNS 8192 #define OPTION_WARNINGS 16384 #define OPTION_AUTO_IS_NULL 32768 #define OPTION_FOUND_COMMENT 65536L #define OPTION_SAFE_UPDATES OPTION_FOUND_COMMENT*2 #define OPTION_BUFFER_RESULT OPTION_SAFE_UPDATES*2 #define OPTION_BIN_LOG OPTION_BUFFER_RESULT*2 -#define OPTION_NOT_AUTO_COMMIT OPTION_BIN_LOG*2 -#define OPTION_BEGIN OPTION_NOT_AUTO_COMMIT*2 +#define OPTION_NOT_AUTOCOMMIT OPTION_BIN_LOG*2 +#define OPTION_BEGIN OPTION_NOT_AUTOCOMMIT*2 #define OPTION_TABLE_LOCK OPTION_BEGIN*2 #define OPTION_QUICK OPTION_TABLE_LOCK*2 #define OPTION_QUOTE_SHOW_CREATE OPTION_QUICK*2 #define OPTION_INTERNAL_SUBTRANSACTIONS OPTION_QUOTE_SHOW_CREATE*2 /* Set if we are updating a non-transaction safe table */ -#define OPTION_STATUS_NO_TRANS_UPDATE OPTION_INTERNAL_SUBTRANSACTIONS*2 +#define OPTION_STATUS_NO_TRANS_UPDATE OPTION_INTERNAL_SUBTRANSACTIONS*2 /* The following is set when parsing the query */ #define QUERY_NO_INDEX_USED OPTION_STATUS_NO_TRANS_UPDATE*2 #define QUERY_NO_GOOD_INDEX_USED QUERY_NO_INDEX_USED*2 /* The following can be set when importing tables in a 'wrong order' to suppress foreign key checks */ -#define OPTION_NO_FOREIGN_KEY_CHECKS QUERY_NO_GOOD_INDEX_USED*2 +#define OPTION_NO_FOREIGN_KEY_CHECKS QUERY_NO_GOOD_INDEX_USED*2 /* The following speeds up inserts to InnoDB tables by suppressing unique key checks in some cases */ -#define OPTION_RELAXED_UNIQUE_CHECKS OPTION_NO_FOREIGN_KEY_CHECKS*2 -/* NOTE: we have now used 31 bits of the OPTION flag! */ +#define OPTION_RELAXED_UNIQUE_CHECKS OPTION_NO_FOREIGN_KEY_CHECKS*2 +#define SELECT_NO_UNLOCK ((ulong) OPTION_RELAXED_UNIQUE_CHECKS*2) +/* NOTE: we have now used up all 32 bits of the OPTION flag! */ /* Bits for different SQL modes modes (including ANSI mode) */ -#define MODE_REAL_AS_FLOAT 1 -#define MODE_PIPES_AS_CONCAT 2 -#define MODE_ANSI_QUOTES 4 -#define MODE_IGNORE_SPACE 8 -#define MODE_SERIALIZABLE 16 -#define MODE_ONLY_FULL_GROUP_BY 32 +#define MODE_REAL_AS_FLOAT 1 +#define MODE_PIPES_AS_CONCAT 2 +#define MODE_ANSI_QUOTES 4 +#define MODE_IGNORE_SPACE 8 +#define MODE_SERIALIZABLE 16 +#define MODE_ONLY_FULL_GROUP_BY 32 +#define MODE_NO_UNSIGNED_SUBTRACTION 64 #define RAID_BLOCK_SIZE 1024 +#ifdef EXTRA_DEBUG +/* + Sync points allow us to force the server to reach a certain line of code + and block there until the client tells the server it is ok to go on. + The client tells the server to block with SELECT GET_LOCK() + and unblocks it with SELECT RELEASE_LOCK(). Used for debugging difficult + concurrency problems +*/ +#define DBUG_SYNC_POINT(lock_name,lock_timeout) \ + debug_sync_point(lock_name,lock_timeout) +void debug_sync_point(const char* lock_name, uint lock_timeout); +#else +#define DBUG_SYNC_POINT(lock_name,lock_timeout) +#endif /* EXTRA_DEBUG */ + /* BINLOG_DUMP options */ #define BINLOG_DUMP_NON_BLOCK 1 @@ -209,6 +218,10 @@ void kill_one_thread(THD *thd, ulong id); #define SHOW_LOG_STATUS_FREE "FREE" #define SHOW_LOG_STATUS_INUSE "IN USE" +/* Options to add_table_to_list() */ +#define TL_OPTION_UPDATING 1 +#define TL_OPTION_FORCE_INDEX 2 + /* Some portable defines */ #define portable_sizeof_char_ptr 8 @@ -225,6 +238,20 @@ typedef struct st_sql_list { uint elements; byte *first; byte **next; + + inline void empty() + { + elements=0; + first=0; + next= &first; + } + inline void link_in_list(byte *element,byte **next_ptr) + { + elements++; + (*next)=element; + next= next_ptr; + *next=0; + } } SQL_LIST; @@ -248,41 +275,73 @@ inline THD *_current_thd(void) #include "sql_class.h" #include "opt_range.h" - -void mysql_create_db(THD *thd, char *db, uint create_info); -void mysql_binlog_send(THD* thd, char* log_ident, ulong pos, ushort flags); +#ifdef HAVE_QUERY_CACHE +#include "sql_cache.h" +#define query_cache_store_query(A, B) query_cache.store_query(A, B) +#define query_cache_destroy() query_cache.destroy() +#define query_cache_result_size_limit(A) query_cache.result_size_limit(A) +#define query_cache_resize(A) query_cache.resize(A) +#define query_cache_invalidate3(A, B, C) query_cache.invalidate(A, B, C) +#define query_cache_invalidate1(A) query_cache.invalidate(A) +#define query_cache_send_result_to_client(A, B, C) \ + query_cache.send_result_to_client(A, B, C) +#define query_cache_invalidate_by_MyISAM_filename_ref \ + &query_cache_invalidate_by_MyISAM_filename +#else +#define query_cache_store_query(A, B) +#define query_cache_destroy() +#define query_cache_result_size_limit(A) +#define query_cache_resize(A) +#define query_cache_invalidate3(A, B, C) +#define query_cache_invalidate1(A) +#define query_cache_send_result_to_client(A, B, C) 0 +#define query_cache_invalidate_by_MyISAM_filename_ref NULL + +#define query_cache_abort(A) +#define query_cache_end_of_result(A) +#define query_cache_invalidate_by_MyISAM_filename_ref NULL +#endif /*HAVE_QUERY_CACHE*/ + +int mysql_create_db(THD *thd, char *db, uint create_info, bool silent); +int mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent); +void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos, ushort flags); int mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists); +int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, + bool log_query); +int mysql_rm_table_part2_with_lock(THD *thd, TABLE_LIST *tables, + bool if_exists, + bool log_query); int quick_rm_table(enum db_type base,const char *db, const char *table_name); bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list); bool mysql_change_db(THD *thd,const char *name); void mysql_parse(THD *thd,char *inBuf,uint length); void mysql_init_select(LEX *lex); +bool mysql_new_select(LEX *lex); +void mysql_init_multi_delete(LEX *lex); void init_max_user_conn(void); +void init_update_queries(void); void free_max_user_conn(void); -pthread_handler_decl(handle_one_connection,arg); -pthread_handler_decl(handle_bootstrap,arg); -sig_handler end_thread_signal(int sig); +extern "C" pthread_handler_decl(handle_one_connection,arg); +extern "C" pthread_handler_decl(handle_bootstrap,arg); void end_thread(THD *thd,bool put_in_cache); void flush_thread_cache(); void mysql_execute_command(void); bool do_command(THD *thd); +bool dispatch_command(enum enum_server_command command, THD *thd, + char* packet, uint packet_length); bool check_stack_overrun(THD *thd,char *dummy); -bool reload_acl_and_cache(THD *thd, uint options, TABLE_LIST *tables); -void mysql_rm_db(THD *thd,char *db,bool if_exists); +bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables); void table_cache_init(void); void table_cache_free(void); uint cached_tables(void); void kill_mysql(void); void close_connection(NET *net,uint errcode=0,bool lock=1); -bool check_access(THD *thd,uint access,const char *db=0,uint *save_priv=0, - bool no_grant=0); -bool check_table_access(THD *thd,uint want_access,TABLE_LIST *tables); -bool check_process_priv(THD *thd=0); - -int generate_table(THD *thd, TABLE_LIST *table_list, - TABLE *locked_table); - +bool check_access(THD *thd, ulong access, const char *db=0, ulong *save_priv=0, + bool no_grant=0, bool no_errors=0); +bool check_table_access(THD *thd, ulong want_access, TABLE_LIST *tables, + bool no_errors=0); +bool check_global_access(THD *thd, ulong want_access); int mysql_backup_table(THD* thd, TABLE_LIST* table_list); int mysql_restore_table(THD* thd, TABLE_LIST* table_list); @@ -295,9 +354,9 @@ int mysql_analyze_table(THD* thd, TABLE_LIST* table_list, HA_CHECK_OPT* check_opt); int mysql_optimize_table(THD* thd, TABLE_LIST* table_list, HA_CHECK_OPT* check_opt); +bool check_simple_select(); /* net_pkg.c */ -void send_error(NET *net,uint sql_errno=0, const char *err=0); void send_warning(NET *net, uint sql_errno, const char *err=0); void net_printf(NET *net,uint sql_errno, ...); void send_ok(NET *net,ha_rows affected_rows=0L,ulonglong id=0L, @@ -324,19 +383,18 @@ SORT_FIELD * make_unireg_sortorder(ORDER *order, uint *length); int setup_order(THD *thd,TABLE_LIST *tables, List<Item> &fields, List <Item> &all_fields, ORDER *order); +int handle_select(THD *thd, LEX *lex, select_result *result); int mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &list,COND *conds, ORDER *order, ORDER *group,Item *having,ORDER *proc_param, - uint select_type,select_result *result); -Field *create_tmp_field(TABLE *table,Item *item, Item::Type type, - Item_result_field ***copy_func, Field **from_field, + ulong select_type,select_result *result); +int mysql_union(THD *thd,LEX *lex,select_result *result); +Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, + Item ***copy_func, Field **from_field, bool group,bool modify_item); int mysql_create_table(THD *thd,const char *db, const char *table_name, HA_CREATE_INFO *create_info, List<create_field> &fields, List<Key> &keys, bool tmp_table, bool no_log); -// no_log is needed for the case of CREATE TABLE ... SELECT , as the logging -// will be done later in sql_insert.cc - TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, const char *db, const char *name, List<create_field> *extra_fields, @@ -351,7 +409,9 @@ int mysql_alter_table(THD *thd, char *new_db, char *new_name, List<Alter_column> &alter_list, ORDER *order, bool drop_primary, - enum enum_duplicates handle_duplicates); + enum enum_duplicates handle_duplicates, + enum enum_enable_or_disable keys_onoff=LEAVE_AS_IS, + bool simple_alter=0); bool mysql_rename_table(enum db_type base, const char *old_db, const char * old_name, @@ -362,15 +422,19 @@ int mysql_create_index(THD *thd, TABLE_LIST *table_list, List<Key> &keys); int mysql_drop_index(THD *thd, TABLE_LIST *table_list, List<Alter_drop> &drop_list); int mysql_update(THD *thd,TABLE_LIST *tables,List<Item> &fields, - List<Item> &values,COND *conds, ha_rows limit, - enum enum_duplicates handle_duplicates, - thr_lock_type lock_type); + List<Item> &values,COND *conds, + ORDER *order, ha_rows limit, + enum enum_duplicates handle_duplicates); +int mysql_multi_update(THD *thd, TABLE_LIST *table_list, + List<Item> *fields, List<Item> *values, + COND *conds, ulong options, + enum enum_duplicates handle_duplicates); int mysql_insert(THD *thd,TABLE_LIST *table,List<Item> &fields, - List<List_item> &values, enum_duplicates flag, - thr_lock_type lock_type); + List<List_item> &values, enum_duplicates flag); void kill_delayed_threads(void); -int mysql_delete(THD *thd,TABLE_LIST *table,COND *conds,ha_rows rows, - thr_lock_type lock_type, ulong options); +int mysql_delete(THD *thd, TABLE_LIST *table, COND *conds, ORDER *order, + ha_rows rows, ulong options); +int mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok=0); TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update); TABLE *open_table(THD *thd,const char *db,const char *table,const char *alias, bool *refresh); @@ -388,13 +452,29 @@ void abort_locked_tables(THD *thd,const char *db, const char *table_name); Field *find_field_in_tables(THD *thd,Item_field *item,TABLE_LIST *tables); Field *find_field_in_table(THD *thd,TABLE *table,const char *name,uint length, bool check_grant,bool allow_rowid); +#ifdef HAVE_OPENSSL +struct st_des_keyblock +{ + des_cblock key1, key2, key3; +}; +struct st_des_keyschedule +{ + des_key_schedule ks1, ks2, ks3; +}; +extern char *des_key_file; +extern struct st_des_keyschedule des_keyschedule[10]; +extern uint des_default_key; +extern pthread_mutex_t LOCK_des_key_file; +bool load_des_key_file(const char *file_name); +void free_des_key_file(); +#endif /* HAVE_OPENSSL */ /* sql_do.cc */ int mysql_do(THD *thd, List<Item> &values); /* sql_list.c */ int mysqld_show_dbs(THD *thd,const char *wild); -int mysqld_show_open_tables(THD *thd,const char *db,const char *wild); +int mysqld_show_open_tables(THD *thd,const char *wild); int mysqld_show_tables(THD *thd,const char *db,const char *wild); int mysqld_extend_show_tables(THD *thd,const char *db,const char *wild); int mysqld_show_fields(THD *thd,TABLE_LIST *table, const char *wild, @@ -408,7 +488,15 @@ int mysqld_show_create(THD *thd, TABLE_LIST *table_list); void mysqld_list_processes(THD *thd,const char *user,bool verbose); int mysqld_show_status(THD *thd); int mysqld_show_variables(THD *thd,const char *wild); -int mysqld_show(THD *thd, const char *wild, show_var_st *variables); +int mysqld_show(THD *thd, const char *wild, show_var_st *variables, + enum enum_var_type value_type); + +/* sql_handler.cc */ +int mysql_ha_open(THD *thd, TABLE_LIST *tables); +int mysql_ha_close(THD *thd, TABLE_LIST *tables, bool dont_send_ok=0); +int mysql_ha_closeall(THD *thd, TABLE_LIST *tables); +int mysql_ha_read(THD *, TABLE_LIST *,enum enum_ha_read_modes,char *, + List<Item> *,enum ha_rkey_function,Item *,ha_rows,ha_rows); /* sql_base.cc */ void set_item_name(Item *item,char *pos,uint length); @@ -419,21 +507,26 @@ bool add_field_to_list(char *field_name, enum enum_field_types type, void store_position_for_column(const char *name); bool add_to_list(SQL_LIST &list,Item *group,bool asc=0); TABLE_LIST *add_table_to_list(Table_ident *table,LEX_STRING *alias, - bool updating, + ulong table_option, thr_lock_type flags=TL_UNLOCK, List<String> *use_index=0, List<String> *ignore_index=0); +void set_lock_for_tables(thr_lock_type lock_type); void add_join_on(TABLE_LIST *b,Item *expr); void add_join_natural(TABLE_LIST *a,TABLE_LIST *b); -bool add_proc_to_list(Item *item); +bool add_proc_to_list(THD *thd, Item *item); TABLE *unlink_open_table(THD *thd,TABLE *list,TABLE *find); SQL_SELECT *make_select(TABLE *head, table_map const_tables, table_map read_tables, COND *conds, int *error); Item ** find_item_in_list(Item *item,List<Item> &items); +bool insert_fields(THD *thd,TABLE_LIST *tables, + const char *db_name, const char *table_name, + List_iterator<Item> *it); bool setup_tables(TABLE_LIST *tables); int setup_fields(THD *thd,TABLE_LIST *tables,List<Item> &item, - bool set_query_id,List<Item> *sum_func_list); + bool set_query_id,List<Item> *sum_func_list, + bool allow_sum_func); int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds); int setup_ftfuncs(THD *thd); int init_ftfuncs(THD *thd, bool no_order); @@ -449,6 +542,7 @@ void free_io_cache(TABLE *entry); void intern_close_table(TABLE *entry); bool close_thread_table(THD *thd, TABLE **table_ptr); void close_thread_tables(THD *thd,bool locked=0); +bool close_thread_table(THD *thd, TABLE **table_ptr); void close_temporary_tables(THD *thd); TABLE **find_temporary_table(THD *thd, const char *db, const char *table_name); bool close_temporary_table(THD *thd, const char *db, const char *table_name); @@ -463,8 +557,7 @@ bool close_cached_tables(THD *thd, bool wait_for_refresh, TABLE_LIST *tables); void copy_field_from_tmp_record(Field *field,int offset); int fill_record(List<Item> &fields,List<Item> &values); int fill_record(Field **field,List<Item> &values); -int list_open_tables(THD *thd,List<char> *files, const char *db,const char *wild); -char* query_table_status(THD *thd,const char *db,const char *table_name); +OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *wild); /* sql_calc.cc */ bool eval_const_cond(COND *cond); @@ -479,18 +572,15 @@ int write_record(TABLE *table,COPY_INFO *info); /* bits set in manager_status */ #define MANAGER_BERKELEY_LOG_CLEANUP (1L << 0) extern ulong volatile manager_status; -extern bool volatile manager_thread_in_use; +extern bool volatile manager_thread_in_use, mqh_used; extern pthread_t manager_thread; -extern pthread_mutex_t LOCK_manager; -extern pthread_cond_t COND_manager; -pthread_handler_decl(handle_manager, arg); +extern "C" pthread_handler_decl(handle_manager, arg); /* sql_test.cc */ #ifndef DBUG_OFF void print_where(COND *cond,const char *info); void print_cached_tables(void); -void TEST_filesort(TABLE **form,SORT_FIELD *sortorder,uint s_length, - ha_rows special); +void TEST_filesort(SORT_FIELD *sortorder,uint s_length, ha_rows special); #endif void mysql_print_status(THD *thd); /* key.cc */ @@ -505,29 +595,80 @@ void init_errmessage(void); void sql_perror(const char *message); void sql_print_error(const char *format,...) __attribute__ ((format (printf, 1, 2))); +bool fn_format_relative_to_data_home(my_string to, const char *name, + const char *dir, const char *extension); +bool open_log(MYSQL_LOG *log, const char *hostname, + const char *opt_name, const char *extension, + const char *index_file_name, + enum_log_type type, bool read_append = 0, + bool no_auto_events = 0); +/* mysqld.cc */ +void clear_error_message(THD *thd); + +/* + External variables +*/ -extern ulong server_id; -extern char mysql_data_home[2],server_version[SERVER_VERSION_LENGTH], - max_sort_char, mysql_real_data_home[]; -extern my_string mysql_unix_port,mysql_tmpdir; +extern time_t start_time; +extern char *mysql_data_home,server_version[SERVER_VERSION_LENGTH], + max_sort_char, mysql_real_data_home[], *charsets_list; +extern my_string mysql_tmpdir; +extern const char *command_name[]; extern const char *first_keyword, *localhost, *delayed_user; -extern ulong refresh_version,flush_version, thread_id,query_id,opened_tables, - created_tmp_tables, created_tmp_disk_tables, - aborted_threads,aborted_connects, - delayed_insert_timeout, - delayed_insert_limit, delayed_queue_size, - delayed_insert_threads, delayed_insert_writes, - delayed_rows_in_use,delayed_insert_errors; +extern const char **errmesg; /* Error messages */ +extern const char *myisam_recover_options_str; +extern uchar *days_in_month; +extern char language[LIBLEN],reg_ext[FN_EXTLEN]; +extern char glob_hostname[FN_REFLEN], mysql_home[FN_REFLEN]; +extern char pidfile_name[FN_REFLEN], time_zone[30], *opt_init_file; +extern char log_error_file[FN_REFLEN]; +extern char blob_newline; +extern double log_10[32]; +extern ulonglong keybuff_size; +extern ulong refresh_version,flush_version, thread_id,query_id,opened_tables; +extern ulong created_tmp_tables, created_tmp_disk_tables; +extern ulong aborted_threads,aborted_connects; +extern ulong delayed_insert_timeout; +extern ulong delayed_insert_limit, delayed_queue_size; +extern ulong delayed_insert_threads, delayed_insert_writes; +extern ulong delayed_rows_in_use,delayed_insert_errors; extern ulong filesort_rows, filesort_range_count, filesort_scan_count; extern ulong filesort_merge_passes; extern ulong select_range_check_count, select_range_count, select_scan_count; -extern ulong select_full_range_join_count,select_full_join_count, - slave_open_temp_tables; -extern uint test_flags,select_errors,mysql_port,ha_open_options; +extern ulong select_full_range_join_count,select_full_join_count; +extern ulong slave_open_temp_tables, query_cache_size; extern ulong thd_startup_options, slow_launch_threads, slow_launch_time; -extern time_t start_time; -extern const char *command_name[]; -extern I_List<THD> threads; +extern ulong server_id, concurrency; +extern ulong ha_read_count, ha_write_count, ha_delete_count, ha_update_count; +extern ulong ha_read_key_count, ha_read_next_count, ha_read_prev_count; +extern ulong ha_read_first_count, ha_read_last_count; +extern ulong ha_read_rnd_count, ha_read_rnd_next_count; +extern ulong ha_commit_count, ha_rollback_count,table_cache_size; +extern ulong max_connections,max_connect_errors, connect_timeout; +extern ulong max_insert_delayed_threads, max_user_connections; +extern ulong long_query_count, what_to_log,flush_time,opt_sql_mode; +extern ulong query_buff_size, thread_stack,thread_stack_min; +extern ulong binlog_cache_size, max_binlog_cache_size, open_files_limit; +extern ulong max_binlog_size, rpl_recovery_rank, thread_cache_size; +extern ulong com_stat[(uint) SQLCOM_END], com_other, back_log; +extern ulong specialflag, current_pid; + +extern uint test_flags,select_errors,ha_open_options; +extern uint protocol_version,dropping_tables; +extern uint delay_key_write_options; +extern bool opt_endinfo, using_udf_functions, locked_in_memory; +extern bool opt_using_transactions, mysql_embedded; +extern bool using_update_log, opt_large_files; +extern bool opt_log, opt_update_log, opt_bin_log, opt_slow_log, opt_error_log; +extern bool opt_disable_networking, opt_skip_show_db; +extern bool volatile abort_loop, shutdown_in_progress, grant_option; +extern uint volatile thread_count, thread_running, global_read_lock; +extern my_bool opt_sql_bin_update, opt_safe_user_create, opt_no_mix_types; +extern my_bool opt_safe_show_db, opt_local_infile, lower_case_table_names; +extern my_bool opt_slave_compressed_protocol, use_temp_pool; +extern my_bool opt_enable_named_pipe; +extern char f_fyllchar; + extern MYSQL_LOG mysql_log,mysql_update_log,mysql_slow_log,mysql_bin_log; extern FILE *bootstrap_file; extern pthread_key(MEM_ROOT*,THR_MALLOC); @@ -536,51 +677,33 @@ extern pthread_mutex_t LOCK_mysql_create_db,LOCK_Acl,LOCK_open, LOCK_thread_count,LOCK_mapped_file,LOCK_user_locks, LOCK_status, LOCK_grant, LOCK_error_log, LOCK_delayed_insert, LOCK_delayed_status, LOCK_delayed_create, LOCK_crypt, LOCK_timezone, - LOCK_binlog_update, LOCK_slave, LOCK_server_id; -extern pthread_cond_t COND_refresh,COND_thread_count, COND_binlog_update, - COND_slave_stopped, COND_slave_start; + LOCK_slave_list, LOCK_active_mi, LOCK_manager, + LOCK_global_system_variables; +extern pthread_cond_t COND_refresh, COND_thread_count, COND_manager; extern pthread_attr_t connection_attrib; -extern bool opt_endinfo, using_udf_functions, locked_in_memory, - opt_using_transactions, use_temp_pool, opt_local_infile; -extern char f_fyllchar; -extern ulong ha_read_count, ha_write_count, ha_delete_count, ha_update_count, - ha_read_key_count, ha_read_next_count, ha_read_prev_count, - ha_read_first_count, ha_read_last_count, - ha_read_rnd_count, ha_read_rnd_next_count; +extern I_List<THD> threads; extern MY_BITMAP temp_pool; -extern uchar *days_in_month; extern DATE_FORMAT dayord; -extern double log_10[32]; -extern uint protocol_version,dropping_tables; -extern ulong keybuff_size,sortbuff_size,max_item_sort_length,table_cache_size, - max_join_size,join_buff_size,tmp_table_size, - max_connections,max_connect_errors,long_query_time, - max_insert_delayed_threads, max_user_connections, - long_query_count,net_wait_timeout,net_interactive_timeout, - net_read_timeout,net_write_timeout, - what_to_log,flush_time, opt_sql_mode, - max_tmp_tables,max_heap_table_size,query_buff_size, - lower_case_table_names,thread_stack,thread_stack_min, - binlog_cache_size, max_binlog_cache_size, record_rnd_cache_size; -extern ulong com_stat[(uint) SQLCOM_END], com_other; -extern ulong specialflag, current_pid; -extern bool low_priority_updates, using_update_log,opt_warnings; -extern bool opt_sql_bin_update, opt_safe_show_db, opt_safe_user_create; -extern char language[LIBLEN],reg_ext[FN_EXTLEN],blob_newline; -extern const char **errmesg; /* Error messages */ -extern const char *default_tx_isolation_name; extern String empty_string; -extern struct show_var_st init_vars[]; -extern struct show_var_st status_vars[]; -extern enum db_type default_table_type; -extern enum enum_tx_isolation default_tx_isolation; +extern SHOW_VAR init_vars[],status_vars[], internal_vars[]; +extern struct system_variables global_system_variables; +extern struct system_variables max_system_variables; +extern struct rand_struct sql_rand; + +/* optional things, have_* variables */ + +extern SHOW_COMP_OPTION have_isam, have_innodb, have_berkeley_db; +extern SHOW_COMP_OPTION have_raid, have_openssl, have_symlink; +extern SHOW_COMP_OPTION have_query_cache, have_berkeley_db, have_innodb; +extern SHOW_COMP_OPTION have_crypt; #ifndef __WIN__ extern pthread_t signal_thread; #endif -extern bool volatile abort_loop, shutdown_in_progress, grant_option; -extern uint volatile thread_count, thread_running, global_read_lock; +#ifdef HAVE_OPENSSL +extern struct st_VioSSLAcceptorFd * ssl_acceptor_fd; +#endif /* HAVE_OPENSSL */ MYSQL_LOCK *mysql_lock_tables(THD *thd,TABLE **table,uint count); void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock); @@ -588,9 +711,15 @@ void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock); void mysql_unlock_some_tables(THD *thd, TABLE **table,uint count); void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table); void mysql_lock_abort(THD *thd, TABLE *table); +void mysql_lock_abort_for_thread(THD *thd, TABLE *table); MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b); +bool lock_global_read_lock(THD *thd); +void unlock_global_read_lock(THD *thd); +bool wait_if_global_read_lock(THD *thd, bool abort_on_refresh); +void start_waiting_global_read_lock(THD *thd); /* Lock based on name */ +int lock_and_wait_for_table_name(THD *thd, TABLE_LIST *table_list); int lock_table_name(THD *thd, TABLE_LIST *table_list); void unlock_table_name(THD *thd, TABLE_LIST *table_list); bool wait_for_locked_table_names(THD *thd, TABLE_LIST *table_list); @@ -602,7 +731,7 @@ void unlock_table_names(THD *thd, TABLE_LIST *table_list, /* old unireg functions */ void unireg_init(ulong options); -void unireg_end(int signal); +void unireg_end(void); int rea_create_table(my_string file_name,HA_CREATE_INFO *create_info, List<create_field> &create_field, uint key_count,KEY *key_info); @@ -631,12 +760,12 @@ timestamp_type str_to_TIME(const char *str, uint length, TIME *l_time, int test_if_number(char *str,int *res,bool allow_wildcards); void change_byte(byte *,uint,char,char); -void unireg_abort(int exit_code); +extern "C" void unireg_abort(int exit_code); void init_read_record(READ_RECORD *info, THD *thd, TABLE *reg_form, SQL_SELECT *select, int use_record_cache, bool print_errors); void end_read_record(READ_RECORD *info); -ha_rows filesort(TABLE **form,struct st_sort_field *sortorder, uint s_length, +ha_rows filesort(TABLE *form,struct st_sort_field *sortorder, uint s_length, SQL_SELECT *select, ha_rows special,ha_rows max_rows, ha_rows *examined_rows); void change_double_for_sort(double nr,byte *to); @@ -647,7 +776,6 @@ uint calc_week(TIME *ltime, bool with_year, bool sunday_first_day_of_week, void find_date(char *pos,uint *vek,uint flag); TYPELIB *convert_strings_to_array_type(my_string *typelibs, my_string *end); TYPELIB *typelib(List<String> &strings); -void clean_up(bool print_message=1); ulong get_form_pos(File file, uchar *head, TYPELIB *save_names); ulong make_new_entry(File file,uchar *fileinfo,TYPELIB *formnames, const char *newname); @@ -677,17 +805,22 @@ void hostname_cache_free(); void hostname_cache_refresh(void); bool get_interval_info(const char *str,uint length,uint count, long *values); -/* sql_cache */ - +/* sql_cache.cc */ extern bool sql_cache_init(); extern void sql_cache_free(); extern int sql_cache_hit(THD *thd, char *inBuf, uint length); +/* item.cc */ +Item *get_system_var(enum_var_type var_type, LEX_STRING name); + +/* log.cc */ +bool flush_error_log(void); + /* Some inline functions for more speed */ inline bool add_item_to_list(Item *item) { - return current_lex->item_list.push_back(item); + return current_lex->select->item_list.push_back(item); } inline bool add_value_to_list(Item *value) { @@ -695,11 +828,11 @@ inline bool add_value_to_list(Item *value) } inline bool add_order_to_list(Item *item,bool asc) { - return add_to_list(current_lex->order_list,item,asc); + return add_to_list(current_lex->select->order_list,item,asc); } inline bool add_group_to_list(Item *item,bool asc) { - return add_to_list(current_lex->group_list,item,asc); + return add_to_list(current_lex->select->group_list,item,asc); } inline void mark_as_null_row(TABLE *table) { @@ -707,5 +840,3 @@ inline void mark_as_null_row(TABLE *table) table->status|=STATUS_NULL_ROW; bfill(table->null_flags,table->null_bytes,255); } - -#endif diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 0ca8659e7f6..a5241d33132 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2003 MySQL AB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -15,45 +15,57 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "mysql_priv.h" -#include <mysql.h> #include <m_ctype.h> #include <my_dir.h> #include "sql_acl.h" #include "slave.h" #include "sql_repl.h" +#include "repl_failsafe.h" #include "stacktrace.h" #ifdef HAVE_BERKELEY_DB #include "ha_berkeley.h" #endif #ifdef HAVE_INNOBASE_DB -#include "ha_innobase.h" -#endif -#ifdef HAVE_GEMINI_DB -#include "ha_gemini.h" +#include "ha_innodb.h" #endif #include "ha_myisam.h" #include <nisam.h> #include <thr_alarm.h> #include <ft_global.h> +#include <assert.h> #ifndef DBUG_OFF #define ONE_THREAD #endif -#if defined(HAVE_DEC_3_2_THREADS) || defined(SIGNALS_DONT_BREAK_READ) -#define HAVE_CLOSE_SERVER_SOCK 1 -void close_server_sock(); +#define SHUTDOWN_THD +#define MAIN_THD +#define SIGNAL_THD + +#ifdef PURIFY +#define IF_PURIFY(A,B) (A) #else -#define close_server_sock() +#define IF_PURIFY(A,B) (B) #endif +/* stack traces are only supported on linux intel */ +#if defined(__linux__) && defined(__i386__) && defined(USE_PSTACK) +#define HAVE_STACK_TRACE_ON_SEGV +#include "../pstack/pstack.h" +char pstack_file_name[80]; +#endif /* __linux__ */ + +#if defined(HAVE_DEC_3_2_THREADS) || defined(SIGNALS_DONT_BREAK_READ) +#define HAVE_CLOSE_SERVER_SOCK 1 +#endif + extern "C" { // Because of SCO 3.2V4.2 #include <errno.h> #include <sys/stat.h> #ifndef __GNU_LIBRARY__ #define __GNU_LIBRARY__ // Skip warnings in getopt.h #endif -#include <getopt.h> +#include <my_getopt.h> #ifdef HAVE_SYSENT_H #include <sysent.h> #endif @@ -67,7 +79,9 @@ extern "C" { // Because of SCO 3.2V4.2 #if defined(OS2) # include <sys/un.h> #elif !defined( __WIN__) +# ifndef __NETWARE__ #include <sys/resource.h> +# endif /* __NETWARE__ */ #ifdef HAVE_SYS_UN_H # include <sys/un.h> #endif @@ -79,9 +93,7 @@ extern "C" { // Because of SCO 3.2V4.2 #include <sys/select.h> #endif #include <sys/utsname.h> -#else -#include <windows.h> -#endif // __WIN__ +#endif /* __WIN__ */ #ifdef HAVE_LIBWRAP #include <tcpd.h> @@ -107,6 +119,15 @@ int deny_severity = LOG_WARNING; #include <sys/mman.h> #endif +#ifdef __NETWARE__ +#include <nks/vm.h> +#include <library.h> +#include <monitor.h> + +event_handle_t eh; +Report_t ref; +#endif /* __NETWARE__ */ + #ifdef _AIX41 int initgroups(const char *,unsigned int); #endif @@ -172,10 +193,9 @@ static SECURITY_DESCRIPTOR sdPipeDescriptor; static HANDLE hPipe = INVALID_HANDLE_VALUE; static pthread_cond_t COND_handler_count; static uint handler_count; -static bool opt_enable_named_pipe = 0; #endif #ifdef __WIN__ -static bool opt_console=0,start_mode=0, use_opt_args; +static bool start_mode=0, use_opt_args; static int opt_argc; static char **opt_argv; #endif @@ -201,17 +221,12 @@ SHOW_COMP_OPTION have_berkeley_db=SHOW_OPTION_YES; #else SHOW_COMP_OPTION have_berkeley_db=SHOW_OPTION_NO; #endif -#ifdef HAVE_GEMINI_DB -SHOW_COMP_OPTION have_gemini=SHOW_OPTION_YES; -#else -SHOW_COMP_OPTION have_gemini=SHOW_OPTION_NO; -#endif #ifdef HAVE_INNOBASE_DB SHOW_COMP_OPTION have_innodb=SHOW_OPTION_YES; #else SHOW_COMP_OPTION have_innodb=SHOW_OPTION_NO; #endif -#ifndef NO_ISAM +#ifdef HAVE_ISAM SHOW_COMP_OPTION have_isam=SHOW_OPTION_YES; #else SHOW_COMP_OPTION have_isam=SHOW_OPTION_NO; @@ -222,96 +237,156 @@ SHOW_COMP_OPTION have_raid=SHOW_OPTION_YES; SHOW_COMP_OPTION have_raid=SHOW_OPTION_NO; #endif #ifdef HAVE_OPENSSL -SHOW_COMP_OPTION have_ssl=SHOW_OPTION_YES; +SHOW_COMP_OPTION have_openssl=SHOW_OPTION_YES; +#else +SHOW_COMP_OPTION have_openssl=SHOW_OPTION_NO; +#endif +#ifdef HAVE_BROKEN_REALPATH +SHOW_COMP_OPTION have_symlink=SHOW_OPTION_NO; +#else +SHOW_COMP_OPTION have_symlink=SHOW_OPTION_YES; +#endif +#ifdef HAVE_QUERY_CACHE +SHOW_COMP_OPTION have_query_cache=SHOW_OPTION_YES; #else -SHOW_COMP_OPTION have_ssl=SHOW_OPTION_NO; +SHOW_COMP_OPTION have_query_cache=SHOW_OPTION_NO; +#endif +#ifdef HAVE_CRYPT +SHOW_COMP_OPTION have_crypt=SHOW_OPTION_YES; +#else +SHOW_COMP_OPTION have_crypt=SHOW_OPTION_NO; +#endif + +bool opt_large_files= sizeof(my_off_t) > 4; +#if SIZEOF_OFF_T > 4 && defined(BIG_TABLES) +#define GET_HA_ROWS GET_ULL +#else +#define GET_HA_ROWS GET_ULONG +#endif + +#ifdef HAVE_LIBWRAP +char *libwrapName= NULL; #endif +/* + Variables to store startup options +*/ -static bool opt_skip_slave_start = 0; // if set, slave is not autostarted +my_bool opt_skip_slave_start = 0; // If set, slave is not autostarted +/* + If set, some standard measures to enforce slave data integrity will not + be performed +*/ +my_bool opt_reckless_slave = 0; + +ulong back_log, connect_timeout, concurrency; +char mysql_home[FN_REFLEN], pidfile_name[FN_REFLEN], time_zone[30]; +char log_error_file[FN_REFLEN]; +bool opt_log, opt_update_log, opt_bin_log, opt_slow_log; +bool opt_error_log= IF_WIN(1,0); +bool opt_disable_networking=0, opt_skip_show_db=0; +my_bool opt_enable_named_pipe= 0; +my_bool opt_local_infile, opt_external_locking, opt_slave_compressed_protocol; +uint delay_key_write_options= (uint) DELAY_KEY_WRITE_ON; + +static bool opt_do_pstack = 0; static ulong opt_specialflag=SPECIAL_ENGLISH; + +static ulong opt_myisam_block_size; static my_socket unix_sock= INVALID_SOCKET,ip_sock= INVALID_SOCKET; -static ulong back_log,connect_timeout,concurrency; static my_string opt_logname=0,opt_update_logname=0, opt_binlog_index_name = 0,opt_slow_logname=0; -static char mysql_home[FN_REFLEN],pidfile_name[FN_REFLEN]; + +static char* mysql_home_ptr= mysql_home; +static char* pidfile_name_ptr= pidfile_name; +char* log_error_file_ptr= log_error_file; static pthread_t select_thread; -static bool opt_log,opt_update_log,opt_bin_log,opt_slow_log,opt_noacl, - opt_disable_networking=0, opt_bootstrap=0,opt_skip_show_db=0, - opt_myisam_log=0, - opt_large_files=sizeof(my_off_t) > 4; -bool opt_sql_bin_update = 0, opt_log_slave_updates = 0, opt_safe_show_db=0, - opt_safe_user_create=0; +static my_bool opt_noacl=0, opt_bootstrap=0, opt_myisam_log=0; +my_bool opt_safe_user_create = 0, opt_no_mix_types = 0; +my_bool lower_case_table_names, opt_old_rpl_compat; +my_bool opt_show_slave_auth_info, opt_sql_bin_update = 0; +my_bool opt_log_slave_updates= 0, opt_console= 0; + +volatile bool mqh_used = 0; FILE *bootstrap_file=0; int segfaulted = 0; // ensure we do not enter SIGSEGV handler twice -extern MASTER_INFO glob_mi; -extern int init_master_info(MASTER_INFO* mi); -// if sql_bin_update is true, SQL_LOG_UPDATE and SQL_LOG_BIN are kept in sync, -// and are treated as aliases for each other +/* + If sql_bin_update is true, SQL_LOG_UPDATE and SQL_LOG_BIN are kept in sync, + and are treated as aliases for each other +*/ static bool kill_in_progress=FALSE; struct rand_struct sql_rand; // used by sql_class.cc:THD::THD() static int cleanup_done; -static char **defaults_argv,time_zone[30]; -static const char *default_table_type_name; -static char glob_hostname[FN_REFLEN]; +static char **defaults_argv; +char glob_hostname[FN_REFLEN]; +#include "sslopt-vars.h" #ifdef HAVE_OPENSSL -static bool opt_use_ssl = FALSE; -static char *opt_ssl_key = 0; -static char *opt_ssl_cert = 0; -static char *opt_ssl_ca = 0; -static char *opt_ssl_capath = 0; -static VioSSLAcceptorFd* ssl_acceptor_fd = 0; +char *des_key_file = 0; +struct st_VioSSLAcceptorFd *ssl_acceptor_fd= 0; #endif /* HAVE_OPENSSL */ - I_List <i_string_pair> replicate_rewrite_db; I_List<i_string> replicate_do_db, replicate_ignore_db; // allow the user to tell us which db to replicate and which to ignore I_List<i_string> binlog_do_db, binlog_ignore_db; /* if we guessed server_id , we need to know about it */ -ulong server_id = 0; +ulong server_id= 0; // Must be long becasue of set_var.cc bool server_id_supplied = 0; uint mysql_port; -uint test_flags, select_errors=0, dropping_tables=0,ha_open_options=0; +uint test_flags = 0, select_errors=0, dropping_tables=0,ha_open_options=0; uint volatile thread_count=0, thread_running=0, kill_cached_threads=0, - wake_thread=0, global_read_lock=0; + wake_thread=0; ulong thd_startup_options=(OPTION_UPDATE_LOG | OPTION_AUTO_IS_NULL | OPTION_BIN_LOG | OPTION_QUOTE_SHOW_CREATE ); uint protocol_version=PROTOCOL_VERSION; -ulong keybuff_size,sortbuff_size,max_item_sort_length,table_cache_size, - max_join_size,join_buff_size,tmp_table_size,thread_stack, - thread_stack_min,net_wait_timeout,what_to_log= ~ (1L << (uint) COM_TIME), - query_buff_size, lower_case_table_names, mysqld_net_retry_count, - net_interactive_timeout, slow_launch_time = 2L, - net_read_timeout,net_write_timeout,slave_open_temp_tables=0, - open_files_limit=0, max_binlog_size, record_rnd_cache_size; +struct system_variables global_system_variables; +struct system_variables max_system_variables; +ulonglong keybuff_size; +ulong table_cache_size, + thread_stack, + thread_stack_min,what_to_log= ~ (1L << (uint) COM_TIME), + query_buff_size, + slow_launch_time = 2L, + slave_open_temp_tables=0, + open_files_limit=0, max_binlog_size; ulong com_stat[(uint) SQLCOM_END], com_other; ulong slave_net_timeout; ulong thread_cache_size=0, binlog_cache_size=0, max_binlog_cache_size=0; +ulong query_cache_size=0; +#ifdef HAVE_QUERY_CACHE +ulong query_cache_limit=0; +Query_cache query_cache; +#endif + volatile ulong cached_thread_count=0; // replication parameters, if master_host is not NULL, we are a slave my_string master_user = (char*) "test", master_password = 0, master_host=0, - master_info_file = (char*) "master.info"; + master_info_file = (char*) "master.info", + relay_log_info_file = (char*) "relay-log.info", + master_ssl_key=0, master_ssl_cert=0, master_ssl_capath=0, master_ssl_cipher=0; +my_string report_user = 0, report_password = 0, report_host=0; + const char *localhost=LOCAL_HOST; const char *delayed_user="DELAYED"; uint master_port = MYSQL_PORT, master_connect_retry = 60; +uint report_port = MYSQL_PORT; +bool master_ssl = 0; -ulong max_tmp_tables,max_heap_table_size,master_retry_count=0; +ulong master_retry_count=0; ulong bytes_sent = 0L, bytes_received = 0L; -bool opt_endinfo,using_udf_functions,low_priority_updates, locked_in_memory; -bool opt_using_transactions, using_update_log, opt_warnings=0; -bool opt_local_infile=1; -bool volatile abort_loop,select_thread_in_use,grant_option; -bool volatile ready_to_exit,shutdown_in_progress; +bool opt_endinfo,using_udf_functions, locked_in_memory; +bool opt_using_transactions, using_update_log; +bool volatile abort_loop, select_thread_in_use, signal_thread_in_use; +bool volatile ready_to_exit, shutdown_in_progress, grant_option; ulong refresh_version=1L,flush_version=1L; /* Increments on each reload */ -ulong query_id=1L,long_query_count,long_query_time,aborted_threads, +ulong query_id=1L,long_query_count,aborted_threads, aborted_connects,delayed_insert_timeout,delayed_insert_limit, delayed_queue_size,delayed_insert_threads,delayed_insert_writes, delayed_rows_in_use,delayed_insert_errors,flush_time, thread_created; @@ -325,27 +400,32 @@ ulong max_connections,max_insert_delayed_threads,max_used_connections, max_connect_errors, max_user_connections = 0; ulong thread_id=1L,current_pid; ulong slow_launch_threads = 0; -ulong myisam_max_sort_file_size, myisam_max_extra_sort_file_size; char mysql_real_data_home[FN_REFLEN], - mysql_data_home[2],language[LIBLEN],reg_ext[FN_EXTLEN], - default_charset[LIBLEN],mysql_charsets_dir[FN_REFLEN], *charsets_list, + language[LIBLEN],reg_ext[FN_EXTLEN], + mysql_charsets_dir[FN_REFLEN], *charsets_list, blob_newline,f_fyllchar,max_sort_char,*mysqld_user,*mysqld_chroot, *opt_init_file; -char *opt_bin_logname = 0; // this one needs to be seen in sql_parse.cc +char *language_ptr= language; +char mysql_data_home_buff[2], *mysql_data_home=mysql_real_data_home; +#ifndef EMBEDDED_LIBRARY +bool mysql_embedded=0; +#else +bool mysql_embedded=1; +#endif + +static char *opt_bin_logname = 0; +char *opt_relay_logname = 0, *opt_relaylog_index_name=0; char server_version[SERVER_VERSION_LENGTH]=MYSQL_SERVER_VERSION; const char *first_keyword="first"; const char **errmesg; /* Error messages */ const char *myisam_recover_options_str="OFF"; const char *sql_mode_str="OFF"; -const char *default_tx_isolation_name; -enum_tx_isolation default_tx_isolation=ISO_READ_COMMITTED; +ulong rpl_recovery_rank=0; -#ifdef HAVE_GEMINI_DB -const char *gemini_recovery_options_str="FULL"; -#endif -my_string mysql_unix_port=NULL,mysql_tmpdir=NULL; +my_string mysql_unix_port=NULL, opt_mysql_tmpdir=NULL, mysql_tmpdir=NULL; ulong my_bind_addr; /* the address we bind to */ +char *my_bind_addr_str; DATE_FORMAT dayord; double log_10[32]; /* 10 potences */ I_List<THD> threads,thread_cache; @@ -354,12 +434,12 @@ time_t start_time; ulong opt_sql_mode = 0L; const char *sql_mode_names[] = { "REAL_AS_FLOAT", "PIPES_AS_CONCAT", "ANSI_QUOTES", "IGNORE_SPACE", - "SERIALIZE","ONLY_FULL_GROUP_BY", NullS }; -TYPELIB sql_mode_typelib= {array_elements(sql_mode_names),"", + "SERIALIZE","ONLY_FULL_GROUP_BY", "NO_UNSIGNED_SUBTRACTION",NullS }; +TYPELIB sql_mode_typelib= {array_elements(sql_mode_names)-1,"", sql_mode_names}; MY_BITMAP temp_pool; -bool use_temp_pool=0; +my_bool use_temp_pool=0; pthread_key(MEM_ROOT*,THR_MALLOC); pthread_key(THD*, THR_THD); @@ -369,47 +449,51 @@ pthread_mutex_t LOCK_mysql_create_db, LOCK_Acl, LOCK_open, LOCK_thread_count, LOCK_error_log, LOCK_delayed_insert, LOCK_delayed_status, LOCK_delayed_create, LOCK_crypt, LOCK_bytes_sent, LOCK_bytes_received, - LOCK_binlog_update, LOCK_slave, LOCK_server_id, - LOCK_user_conn; + LOCK_global_system_variables, + LOCK_user_conn, LOCK_slave_list, LOCK_active_mi; -pthread_cond_t COND_refresh,COND_thread_count,COND_binlog_update, - COND_slave_stopped, COND_slave_start; +pthread_cond_t COND_refresh,COND_thread_count, COND_slave_stopped, + COND_slave_start; pthread_cond_t COND_thread_cache,COND_flush_thread_cache; pthread_t signal_thread; pthread_attr_t connection_attrib; -enum db_type default_table_type=DB_TYPE_MYISAM; #ifdef __WIN__ #undef getpid #include <process.h> +#if !defined(EMBEDDED_LIBRARY) HANDLE hEventShutdown; static char shutdown_event_name[40]; #include "nt_servc.h" static NTService Service; // Service object for WinNT #endif +#endif #ifdef OS2 pthread_cond_t eventShutdown; #endif static void start_signal_handler(void); -static void *signal_hand(void *arg); +extern "C" pthread_handler_decl(signal_hand, arg); static void set_options(void); static void get_options(int argc,char **argv); static char *get_relative_path(const char *path); static void fix_paths(void); -static pthread_handler_decl(handle_connections_sockets,arg); -static pthread_handler_decl(kill_server_thread,arg); +extern "C" pthread_handler_decl(handle_connections_sockets,arg); +extern "C" pthread_handler_decl(kill_server_thread,arg); static int bootstrap(FILE *file); +static void close_server_sock(); static bool read_init_file(char *file_name); #ifdef __NT__ -static pthread_handler_decl(handle_connections_namedpipes,arg); +extern "C" pthread_handler_decl(handle_connections_namedpipes,arg); #endif -extern pthread_handler_decl(handle_slave,arg); +extern "C" pthread_handler_decl(handle_slave,arg); #ifdef SET_RLIMIT_NOFILE static uint set_maximum_open_files(uint max_file_limit); #endif static ulong find_bit_type(const char *x, TYPELIB *bit_lib); +static void clean_up(bool print_message); +static void clean_up_mutexes(void); /**************************************************************************** ** Code to end mysqld @@ -437,7 +521,7 @@ static void close_connections(void) (void) pthread_mutex_unlock(&LOCK_manager); /* kill connection thread */ -#if !defined(__WIN__) && !defined(__EMX__) && !defined(OS2) +#if !defined(__WIN__) && !defined(__EMX__) && !defined(OS2) && !defined(__NETWARE__) DBUG_PRINT("quit",("waiting for select thread: %lx",select_thread)); (void) pthread_mutex_lock(&LOCK_thread_count); @@ -450,15 +534,7 @@ static void close_connections(void) if (pthread_kill(select_thread,THR_CLIENT_ALARM)) break; // allready dead #endif -#ifdef HAVE_TIMESPEC_TS_SEC - abstime.ts_sec=time(NULL)+2; // Bsd 2.1 - abstime.ts_nsec=0; -#else - struct timeval tv; - gettimeofday(&tv,0); - abstime.tv_sec=tv.tv_sec+2; - abstime.tv_nsec=tv.tv_usec*1000; -#endif + set_timespec(abstime, 2); for (uint tmp=0 ; tmp < 10 ; tmp++) { error=pthread_cond_timedwait(&COND_thread_count,&LOCK_thread_count, @@ -488,28 +564,28 @@ static void close_connections(void) } } #ifdef __NT__ -if (hPipe != INVALID_HANDLE_VALUE && opt_enable_named_pipe) -{ - HANDLE temp; - DBUG_PRINT( "quit", ("Closing named pipes") ); - - /* Create connection to the handle named pipe handler to break the loop */ - if ((temp = CreateFile(szPipeName, - GENERIC_READ | GENERIC_WRITE, - 0, - NULL, - OPEN_EXISTING, - 0, - NULL )) != INVALID_HANDLE_VALUE) + if (hPipe != INVALID_HANDLE_VALUE && opt_enable_named_pipe) { - WaitNamedPipe(szPipeName, 1000); - DWORD dwMode = PIPE_READMODE_BYTE | PIPE_WAIT; - SetNamedPipeHandleState(temp, &dwMode, NULL, NULL); - CancelIo(temp); - DisconnectNamedPipe(temp); - CloseHandle(temp); + HANDLE temp; + DBUG_PRINT( "quit", ("Closing named pipes") ); + + /* Create connection to the handle named pipe handler to break the loop */ + if ((temp = CreateFile(szPipeName, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + 0, + NULL )) != INVALID_HANDLE_VALUE) + { + WaitNamedPipe(szPipeName, 1000); + DWORD dwMode = PIPE_READMODE_BYTE | PIPE_WAIT; + SetNamedPipeHandleState(temp, &dwMode, NULL, NULL); + CancelIo(temp); + DisconnectNamedPipe(temp); + CloseHandle(temp); + } } - } #endif #ifdef HAVE_SYS_UN_H if (unix_sock != INVALID_SOCKET) @@ -550,13 +626,11 @@ if (hPipe != INVALID_HANDLE_VALUE && opt_enable_named_pipe) (void) pthread_mutex_unlock(&LOCK_thread_count); // For unlink from list if (thread_count) - { sleep(1); // Give threads time to die - } /* Force remaining threads to die by closing the connection to the client */ - (void) my_net_init(&net, (Vio*) 0); + (void) my_net_init(&net, (st_vio*) 0); for (;;) { DBUG_PRINT("quit",("Locking LOCK_thread_count")); @@ -589,74 +663,93 @@ if (hPipe != INVALID_HANDLE_VALUE && opt_enable_named_pipe) } (void) pthread_mutex_unlock(&LOCK_thread_count); - mysql_log.close(1); - mysql_slow_log.close(1); - mysql_update_log.close(1); - mysql_bin_log.close(1); DBUG_PRINT("quit",("close_connections thread")); DBUG_VOID_RETURN; } -#ifdef HAVE_CLOSE_SERVER_SOCK -void close_server_sock() + +static void close_server_sock() { +#ifdef HAVE_CLOSE_SERVER_SOCK DBUG_ENTER("close_server_sock"); - - if (ip_sock != INVALID_SOCKET) + my_socket tmp_sock; + tmp_sock=ip_sock; + if (tmp_sock != INVALID_SOCKET) { + ip_sock=INVALID_SOCKET; DBUG_PRINT("info",("calling shutdown on TCP/IP socket")); - VOID(shutdown(ip_sock,2)); -#ifdef NOT_USED + VOID(shutdown(tmp_sock,2)); +#if defined(__NETWARE__) /* - The following code is disabled as it cases MySQL to hang on - AIX 4.3 during shutdown + The following code is disabled for normal systems as it causes MySQL + to hang on AIX 4.3 during shutdown */ DBUG_PRINT("info",("calling closesocket on TCP/IP socket")); - VOID(closesocket(ip_sock)); + VOID(closesocket(tmp_sock)); #endif - ip_sock=INVALID_SOCKET; } - if (unix_sock != INVALID_SOCKET) + tmp_sock=unix_sock; + if (tmp_sock != INVALID_SOCKET) { + unix_sock=INVALID_SOCKET; DBUG_PRINT("info",("calling shutdown on unix socket")); - VOID(shutdown(unix_sock,2)); + VOID(shutdown(tmp_sock,2)); +#if defined(__NETWARE__) + /* + The following code is disabled for normal systems as it causes MySQL + AIX 4.3 during shutdown (not tested, but likely) + */ + DBUG_PRINT("info",("calling closesocket on unix/IP socket")); + VOID(closesocket(tmp_sock)); +#endif VOID(unlink(mysql_unix_port)); - unix_sock=INVALID_SOCKET; } DBUG_VOID_RETURN; -} #endif +} + void kill_mysql(void) { DBUG_ENTER("kill_mysql"); + #ifdef SIGNALS_DONT_BREAK_READ abort_loop=1; // Break connection loops close_server_sock(); // Force accept to wake up -#endif +#endif + #if defined(__WIN__) +#if !defined(EMBEDDED_LIBRARY) { if (!SetEvent(hEventShutdown)) { DBUG_PRINT("error",("Got error: %ld from SetEvent",GetLastError())); } + /* + or: + HANDLE hEvent=OpenEvent(0, FALSE, "MySqlShutdown"); + SetEvent(hEventShutdown); + CloseHandle(hEvent); + */ } +#endif #elif defined(OS2) pthread_cond_signal( &eventShutdown); // post semaphore #elif defined(HAVE_PTHREAD_KILL) - if (pthread_kill(signal_thread,MYSQL_KILL_SIGNAL))// End everything nicely + if (pthread_kill(signal_thread, MYSQL_KILL_SIGNAL)) { DBUG_PRINT("error",("Got error %d from pthread_kill",errno)); /* purecov: inspected */ } #elif !defined(SIGNALS_DONT_BREAK_READ) - kill(current_pid,MYSQL_KILL_SIGNAL); + kill(current_pid, MYSQL_KILL_SIGNAL); #endif DBUG_PRINT("quit",("After pthread_kill")); shutdown_in_progress=1; // Safety if kill didn't work -#ifdef SIGNALS_DONT_BREAK_READ +#ifdef SIGNALS_DONT_BREAK_READ if (!kill_in_progress) { pthread_t tmp; + abort_loop=1; if (pthread_create(&tmp,&connection_attrib, kill_server_thread, (void*) 0)) sql_print_error("Error: Can't create thread to kill server"); @@ -668,22 +761,21 @@ void kill_mysql(void) /* Force server down. kill all connections and threads and exit */ -#if defined(OS2) +#if defined(OS2) || defined(__NETWARE__) extern "C" void kill_server(int sig_ptr) -#define RETURN_FROM_KILL_SERVER return +#define RETURN_FROM_KILL_SERVER DBUG_VOID_RETURN #elif !defined(__WIN__) static void *kill_server(void *sig_ptr) -#define RETURN_FROM_KILL_SERVER return 0 +#define RETURN_FROM_KILL_SERVER DBUG_RETURN(0) #else static void __cdecl kill_server(int sig_ptr) -#define RETURN_FROM_KILL_SERVER return +#define RETURN_FROM_KILL_SERVER DBUG_VOID_RETURN #endif { int sig=(int) (long) sig_ptr; // This is passed a int DBUG_ENTER("kill_server"); - // if there is a signal during the kill in progress, we do not need - // another one + // if there is a signal during the kill in progress, ignore the other if (kill_in_progress) // Safety RETURN_FROM_KILL_SERVER; kill_in_progress=TRUE; @@ -694,22 +786,29 @@ static void __cdecl kill_server(int sig_ptr) else sql_print_error(ER(ER_GOT_SIGNAL),my_progname,sig); /* purecov: inspected */ -#if defined(USE_ONE_SIGNAL_HAND) && !defined(__WIN__) && !defined(OS2) +#if defined(__NETWARE__) || (defined(USE_ONE_SIGNAL_HAND) && !defined(__WIN__) && !defined(OS2)) my_thread_init(); // If this is a new thread #endif close_connections(); if (sig != MYSQL_KILL_SIGNAL && sig != 0) unireg_abort(1); /* purecov: inspected */ else - unireg_end(0); + unireg_end(); + +#ifdef __NETWARE__ + pthread_join(select_thread, NULL); // wait for main thread +#else pthread_exit(0); /* purecov: deadcode */ +#endif /* __NETWARE__ */ + RETURN_FROM_KILL_SERVER; } -#ifdef USE_ONE_SIGNAL_HAND -pthread_handler_decl(kill_server_thread,arg __attribute__((unused))) +#if defined(USE_ONE_SIGNAL_HAND) || (defined(__NETWARE__) && defined(SIGNALS_DONT_BREAK_READ)) +extern "C" pthread_handler_decl(kill_server_thread,arg __attribute__((unused))) { + SHUTDOWN_THD; my_thread_init(); // Initialize new thread kill_server(0); my_thread_end(); // Normally never reached @@ -717,37 +816,66 @@ pthread_handler_decl(kill_server_thread,arg __attribute__((unused))) } #endif -static sig_handler print_signal_warning(int sig) +#if defined(__amiga__) +#undef sigset +#define sigset signal +#endif + +extern "C" sig_handler print_signal_warning(int sig) { - if (opt_warnings) - sql_print_error("Warning: Got signal %d from thread %d", - sig,my_thread_id()); + if (!DBUG_IN_USE) + { + if (global_system_variables.log_warnings) + sql_print_error("Warning: Got signal %d from thread %d", + sig,my_thread_id()); + } #ifdef DONT_REMEMBER_SIGNAL sigset(sig,print_signal_warning); /* int. thread system calls */ #endif -#if !defined(__WIN__) && !defined(OS2) +#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__) if (sig == SIGALRM) alarm(2); /* reschedule alarm */ #endif } +/* + cleanup all memory and end program nicely + + SYNOPSIS + unireg_end() + + NOTES + This function never returns. + + If SIGNALS_DONT_BREAK_READ is defined, this function is called + by the main thread. To get MySQL to shut down nicely in this case + (Mac OS X) we have to call exit() instead if pthread_exit(). +*/ -void unireg_end(int signal_number __attribute__((unused))) +void unireg_end(void) { - clean_up(); + clean_up(1); + my_thread_end(); +#ifndef __NETWARE__ #ifdef SIGNALS_DONT_BREAK_READ exit(0); #else pthread_exit(0); // Exit is in main thread #endif +#endif /* __NETWARE__ */ } -void unireg_abort(int exit_code) +extern "C" void unireg_abort(int exit_code) { + DBUG_ENTER("unireg_abort"); if (exit_code) sql_print_error("Aborting\n"); - clean_up(); /* purecov: inspected */ + clean_up(1); /* purecov: inspected */ + DBUG_PRINT("quit",("done with cleanup in unireg_abort")); + my_thread_end(); + clean_up_mutexes(); + my_end(opt_endinfo ? MY_CHECK_ERROR | MY_GIVE_INFO : 0); exit(exit_code); /* purecov: inspected */ } @@ -757,15 +885,22 @@ void clean_up(bool print_message) DBUG_PRINT("exit",("clean_up")); if (cleanup_done++) return; /* purecov: inspected */ + + mysql_log.cleanup(); + mysql_slow_log.cleanup(); + mysql_update_log.cleanup(); + mysql_bin_log.cleanup(); + if (use_slave_mask) bitmap_free(&slave_error_mask); acl_free(1); grant_free(); - sql_cache_free(); + query_cache_destroy(); table_cache_free(); hostname_cache_free(); item_user_lock_free(); lex_free(); /* Free some memory */ + set_var_free(); #ifdef HAVE_DLOPEN if (!opt_noacl) udf_free(); @@ -775,30 +910,75 @@ void clean_up(bool print_message) #ifdef USE_RAID end_raid(); #endif - free_defaults(defaults_argv); + if (defaults_argv) + free_defaults(defaults_argv); my_free(charsets_list, MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql_tmpdir,MYF(0)); + my_free(mysql_tmpdir,MYF(MY_ALLOW_ZERO_PTR)); + my_free(slave_load_tmpdir,MYF(MY_ALLOW_ZERO_PTR)); x_free(opt_bin_logname); + x_free(opt_relay_logname); bitmap_free(&temp_pool); free_max_user_conn(); -#ifndef __WIN__ + end_slave_list(); +#ifdef HAVE_OPENSSL + free_des_key_file(); +#endif /* HAVE_OPENSSL */ +#ifdef USE_REGEX + regex_end(); +#endif + + if (print_message && errmesg) + sql_print_error(ER(ER_SHUTDOWN_COMPLETE),my_progname); +#if !defined(__WIN__) && !defined(EMBEDDED_LIBRARY) if (!opt_bootstrap) (void) my_delete(pidfile_name,MYF(0)); // This may not always exist #endif - if (print_message) - sql_print_error(ER(ER_SHUTDOWN_COMPLETE),my_progname); x_free((gptr) my_errmsg[ERRMAPP]); /* Free messages */ - my_thread_end(); - + DBUG_PRINT("quit", ("Error messages freed")); /* Tell main we are ready */ (void) pthread_mutex_lock(&LOCK_thread_count); + DBUG_PRINT("quit", ("got thread count lock")); ready_to_exit=1; /* do the broadcast inside the lock to ensure that my_end() is not called */ (void) pthread_cond_broadcast(&COND_thread_count); (void) pthread_mutex_unlock(&LOCK_thread_count); + /* + The following lines may never be executed as the main thread may have + killed us + */ + DBUG_PRINT("quit", ("done with cleanup")); } /* clean_up */ +static void clean_up_mutexes() +{ + (void) pthread_mutex_destroy(&LOCK_mysql_create_db); + (void) pthread_mutex_destroy(&LOCK_Acl); + (void) pthread_mutex_destroy(&LOCK_grant); + (void) pthread_mutex_destroy(&LOCK_open); + (void) pthread_mutex_destroy(&LOCK_thread_count); + (void) pthread_mutex_destroy(&LOCK_mapped_file); + (void) pthread_mutex_destroy(&LOCK_status); + (void) pthread_mutex_destroy(&LOCK_error_log); + (void) pthread_mutex_destroy(&LOCK_delayed_insert); + (void) pthread_mutex_destroy(&LOCK_delayed_status); + (void) pthread_mutex_destroy(&LOCK_delayed_create); + (void) pthread_mutex_destroy(&LOCK_manager); + (void) pthread_mutex_destroy(&LOCK_crypt); + (void) pthread_mutex_destroy(&LOCK_bytes_sent); + (void) pthread_mutex_destroy(&LOCK_bytes_received); + (void) pthread_mutex_destroy(&LOCK_timezone); + (void) pthread_mutex_destroy(&LOCK_user_conn); + (void) pthread_mutex_destroy(&LOCK_rpl_status); + (void) pthread_mutex_destroy(&LOCK_active_mi); + (void) pthread_mutex_destroy(&LOCK_global_system_variables); + (void) pthread_cond_destroy(&COND_thread_count); + (void) pthread_cond_destroy(&COND_refresh); + (void) pthread_cond_destroy(&COND_thread_cache); + (void) pthread_cond_destroy(&COND_flush_thread_cache); + (void) pthread_cond_destroy(&COND_manager); + (void) pthread_cond_destroy(&COND_rpl_status); +} /**************************************************************************** ** Init IP and UNIX socket @@ -832,7 +1012,7 @@ static void set_ports() static void set_user(const char *user) { -#if !defined(__WIN__) && !defined(OS2) +#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__) struct passwd *ent; // don't bother if we aren't superuser @@ -855,20 +1035,33 @@ static void set_user(const char *user) if (!strcmp(user,"root")) return; // Avoid problem with dynamic libraries + uid_t uid; if (!(ent = getpwnam(user))) { - fprintf(stderr,"Fatal error: Can't change to run as user '%s' ; Please check that the user exists!\n",user); - unireg_abort(1); + // allow a numeric uid to be used + const char *pos; + for (pos=user; isdigit(*pos); pos++) ; + if (*pos) // Not numeric id + { + fprintf(stderr,"Fatal error: Can't change to run as user '%s' ; Please check that the user exists!\n",user); + unireg_abort(1); + } + uid=atoi(user); // Use numberic uid } + else + { #ifdef HAVE_INITGROUPS - initgroups((char*) user,ent->pw_gid); + initgroups((char*) user,ent->pw_gid); #endif - if (setgid(ent->pw_gid) == -1) - { - sql_perror("setgid"); - unireg_abort(1); + if (setgid(ent->pw_gid) == -1) + { + sql_perror("setgid"); + unireg_abort(1); + } + uid=ent->pw_uid; } - if (setuid(ent->pw_uid) == -1) + + if (setuid(uid) == -1) { sql_perror("setuid"); unireg_abort(1); @@ -880,14 +1073,13 @@ static void set_user(const char *user) static void set_root(const char *path) { -#if !defined(__WIN__) && !defined(__EMX__) && !defined(OS2) +#if !defined(__WIN__) && !defined(__EMX__) && !defined(OS2) && !defined(__NETWARE__) if (chroot(path) == -1) { sql_perror("chroot"); unireg_abort(1); } -// my_setwd("/", MYF(0)); - sql_print_error("Warning: --chroot option doesn't provide 100%% closed chroot jail in MySQL 3.23. Upgrade to 4.0"); + my_setwd("/", MYF(0)); #endif } @@ -940,15 +1132,12 @@ static void server_init(void) if (listen(ip_sock,(int) back_log) < 0) { sql_perror("Can't start server: listen() on TCP/IP port"); - sql_print_error("Warning: listen() on TCP/IP failed with error %d", + sql_print_error("Error: listen() on TCP/IP failed with error %d", socket_errno); unireg_abort(1); } } - if (mysqld_chroot) - set_root(mysqld_chroot); - - set_user(mysqld_user); // set_user now takes care of mysqld_user==NULL + set_user(mysqld_user); // Works also with mysqld_user==NULL #ifdef __NT__ /* create named pipe */ @@ -978,8 +1167,8 @@ static void server_init(void) PIPE_READMODE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, - (int) net_buffer_length, - (int) net_buffer_length, + (int) global_system_variables.net_buffer_length, + (int) global_system_variables.net_buffer_length, NMPWAIT_USE_DEFAULT_WAIT, &saPipeSecurity )) == INVALID_HANDLE_VALUE) { @@ -1051,7 +1240,7 @@ void yyerror(const char *s) void close_connection(NET *net,uint errcode,bool lock) { - Vio* vio; + st_vio* vio; DBUG_ENTER("close_connection"); DBUG_PRINT("enter",("fd: %s error: '%s'", net->vio? vio_description(net->vio):"(not connected)", @@ -1072,11 +1261,11 @@ void close_connection(NET *net,uint errcode,bool lock) /* Called when a thread is aborted */ /* ARGSUSED */ -sig_handler end_thread_signal(int sig __attribute__((unused))) +extern "C" sig_handler end_thread_signal(int sig __attribute__((unused))) { THD *thd=current_thd; DBUG_ENTER("end_thread_signal"); - if (thd) + if (thd && ! thd->bootstrap) end_thread(thd,0); DBUG_VOID_RETURN; /* purecov: deadcode */ } @@ -1117,6 +1306,7 @@ void end_thread(THD *thd, bool put_in_cache) /* Tell main we are ready */ (void) pthread_mutex_unlock(&LOCK_thread_count); + /* It's safe to broadcast outside a lock (COND... is not deleted here) */ (void) pthread_cond_broadcast(&COND_thread_count); DBUG_PRINT("info", ("unlocked thread_count mutex")) #ifdef ONE_THREAD @@ -1129,21 +1319,6 @@ void end_thread(THD *thd, bool put_in_cache) DBUG_VOID_RETURN; } -#ifdef SIGNALS_DONT_BREAK_READ -inline void kill_broken_server() -{ - /* hack to get around signals ignored in syscalls for problem OS's */ - if (unix_sock == INVALID_SOCKET || - (!opt_disable_networking && ip_sock == INVALID_SOCKET)) - { - select_thread_in_use = 0; - kill_server((void*)MYSQL_KILL_SIGNAL); /* never returns */ - } -} -#define MAYBE_BROKEN_SYSCALL kill_broken_server(); -#else -#define MAYBE_BROKEN_SYSCALL -#endif /* Start a cached thread. LOCK_thread_count is locked on entry */ @@ -1170,14 +1345,14 @@ void flush_thread_cache() } - /* - ** Aborts a thread nicely. Commes here on SIGPIPE - ** TODO: One should have to fix that thr_alarm know about this - ** thread too - */ +/* + Aborts a thread nicely. Commes here on SIGPIPE + TODO: One should have to fix that thr_alarm know about this + thread too. +*/ #ifdef THREAD_SPECIFIC_SIGPIPE -static sig_handler abort_thread(int sig __attribute__((unused))) +extern "C" sig_handler abort_thread(int sig __attribute__((unused))) { THD *thd=current_thd; DBUG_ENTER("abort_thread"); @@ -1188,9 +1363,9 @@ static sig_handler abort_thread(int sig __attribute__((unused))) #endif /****************************************************************************** -** Setup a signal thread with handles all signals -** Because linux doesn't support scemas use a mutex to check that -** the signal thread is ready before continuing + Setup a signal thread with handles all signals. + Because Linux doesn't support schemas use a mutex to check that + the signal thread is ready before continuing ******************************************************************************/ #if defined(__WIN__) || defined(OS2) @@ -1207,26 +1382,78 @@ static void init_signals(void) } static void start_signal_handler(void) +{} + +static void check_data_home(const char *path) +{} + +#elif defined(__NETWARE__) + +// down server event callback +void mysql_down_server_cb(void *, void *) { + kill_server(0); +} + +// destroy callback resources +void mysql_cb_destroy(void *) +{ + UnRegisterEventNotification(eh); // cleanup down event notification + NX_UNWRAP_INTERFACE(ref); +} + +// initialize callbacks +void mysql_cb_init() +{ + // register for down server event + void *handle = getnlmhandle(); + rtag_t rt = AllocateResourceTag(handle, "MySQL Down Server Callback", + EventSignature); + NX_WRAP_INTERFACE((void *)mysql_down_server_cb, 2, (void **)&ref); + eh = RegisterForEventNotification(rt, EVENT_DOWN_SERVER, + EVENT_PRIORITY_APPLICATION, + NULL, ref, NULL); + NXVmRegisterExitHandler(mysql_cb_destroy, NULL); // clean-up } -#elif defined(__EMX__) static void init_signals(void) { - signal(SIGQUIT, sig_kill); - signal(SIGKILL, sig_kill); - signal(SIGTERM, sig_kill); - signal(SIGINT, sig_kill); - signal(SIGHUP, sig_reload); // Flush everything - signal(SIGALRM, SIG_IGN); - signal(SIGBREAK,SIG_IGN); - signal_thread = pthread_self(); + int signals[] = {SIGINT,SIGILL,SIGFPE,SIGSEGV,SIGTERM,SIGABRT}; + + for (uint i=0 ; i < sizeof(signals)/sizeof(int) ; i++) + signal(signals[i], kill_server); + mysql_cb_init(); // initialize callbacks } +static void start_signal_handler(void) +{ + // Save vm id of this process + if (!opt_bootstrap) + { + File pidFile; + if ((pidFile = my_create(pidfile_name,0664, O_WRONLY, MYF(MY_WME))) >= 0) + { + char buff[21]; + sprintf(buff,"%lu",(ulong) getpid()); + (void) my_write(pidFile, buff,strlen(buff),MYF(MY_WME)); + (void) my_close(pidFile,MYF(0)); + } + } + // no signal handler +} + +/* Warn if the data is on a Traditional volume */ + +static void check_data_home(const char *path) +{ +} + +#elif defined(__EMX__) static void sig_reload(int signo) { - reload_acl_and_cache((THD*) 0,REFRESH_LOG, (TABLE_LIST*) 0); // Flush everything + // Flush everything + reload_acl_and_cache((THD*) 0,REFRESH_LOG, (TABLE_LIST*) 0); signal(signo, SIG_ACK); } @@ -1240,24 +1467,39 @@ static void sig_kill(int signo) signal(signo, SIG_ACK); } - -static void start_signal_handler(void) +static void init_signals(void) { + signal(SIGQUIT, sig_kill); + signal(SIGKILL, sig_kill); + signal(SIGTERM, sig_kill); + signal(SIGINT, sig_kill); + signal(SIGHUP, sig_reload); // Flush everything + signal(SIGALRM, SIG_IGN); + signal(SIGBREAK,SIG_IGN); + signal_thread = pthread_self(); + SIGNAL_THD; } + +static void start_signal_handler(void) +{} + +static void check_data_home(const char *path) +{} + #else /* if ! __WIN__ && ! __EMX__ */ #ifdef HAVE_LINUXTHREADS #define UNSAFE_DEFAULT_LINUX_THREADS 200 #endif -static sig_handler handle_segfault(int sig) +extern "C" sig_handler handle_segfault(int sig) { THD *thd=current_thd; /* - Strictly speaking, we should need a mutex here + Strictly speaking, one needs a mutex here but since we have got SIGSEGV already, things are a mess so not having the mutex is not as bad as possibly using a buggy - mutex - so we keep things simple. + mutex - so we keep things simple */ if (segfaulted) { @@ -1275,37 +1517,37 @@ or misconfigured. This error can also be caused by malfunctioning hardware.\n", fprintf(stderr, "\ We will try our best to scrape up some info that will hopefully help diagnose\n\ the problem, but since we have already crashed, something is definitely wrong\n\ -and this may fail\n\n"); - fprintf(stderr, "key_buffer_size=%ld\n", keybuff_size); - fprintf(stderr, "record_buffer=%ld\n", my_default_record_cache_size); - fprintf(stderr, "sort_buffer=%ld\n", sortbuff_size); +and this may fail.\n\n"); + fprintf(stderr, "key_buffer_size=%lu\n", (ulong) keybuff_size); + fprintf(stderr, "read_buffer_size=%ld\n", global_system_variables.read_buff_size); + fprintf(stderr, "sort_buffer_size=%ld\n", thd->variables.sortbuff_size); fprintf(stderr, "max_used_connections=%ld\n", max_used_connections); fprintf(stderr, "max_connections=%ld\n", max_connections); fprintf(stderr, "threads_connected=%d\n", thread_count); fprintf(stderr, "It is possible that mysqld could use up to \n\ -key_buffer_size + (record_buffer + sort_buffer)*max_connections = %ld K\n\ -bytes of memory\n", (keybuff_size + (my_default_record_cache_size + - sortbuff_size) * max_connections)/ 1024); - fprintf(stderr, "Hope that's ok, if not, decrease some variables in the equation\n\n"); +key_buffer_size + (read_buffer_size + sort_buffer_size)*max_connections = %ld K\n\ +bytes of memory\n", ((ulong) keybuff_size + + (global_system_variables.read_buff_size + + thd->variables.sortbuff_size) * + max_connections)/ 1024); + fprintf(stderr, "Hope that's ok; if not, decrease some variables in the equation.\n\n"); #if defined(HAVE_LINUXTHREADS) if (sizeof(char*) == 4 && thread_count > UNSAFE_DEFAULT_LINUX_THREADS) { fprintf(stderr, "\ You seem to be running 32-bit Linux and have %d concurrent connections.\n\ -If you have not changed STACK_SIZE in LinuxThreads and build the binary \n\ -yourself, LinuxThreads is quite likely to steal a part of global heap for\n\ +If you have not changed STACK_SIZE in LinuxThreads and built the binary \n\ +yourself, LinuxThreads is quite likely to steal a part of the global heap for\n\ the thread stack. Please read http://www.mysql.com/doc/L/i/Linux.html\n\n", thread_count); } #endif /* HAVE_LINUXTHREADS */ #ifdef HAVE_STACKTRACE - if(!(test_flags & TEST_NO_STACKTRACE)) + if (!(test_flags & TEST_NO_STACKTRACE)) { -#ifdef HAVE_GEMINI_DB - utrace(); -#endif + fprintf(stderr,"thd=%p\n",thd); print_stacktrace(thd ? (gptr) thd->thread_stack : (gptr) 0, thread_stack); } @@ -1318,12 +1560,12 @@ Some pointers may be invalid and cause the dump to abort...\n"); fprintf(stderr, "\n\ Successfully dumped variables, if you ran with --log, take a look at the\n\ details of what thread %ld did to cause the crash. In some cases of really\n\ -bad corruption, the values shown above may be invalid\n\n", +bad corruption, the values shown above may be invalid.\n\n", thd->thread_id); } fprintf(stderr, "\ The manual page at http://www.mysql.com/doc/C/r/Crashing.html contains\n\ -information that should help you find out what is causing the crash\n"); +information that should help you find out what is causing the crash.\n"); fflush(stderr); #endif /* HAVE_STACKTRACE */ @@ -1359,13 +1601,29 @@ static void init_signals(void) sigprocmask(SIG_SETMASK,&sa.sa_mask,NULL); init_stacktrace(); +#if defined(__amiga__) + sa.sa_handler=(void(*)())handle_segfault; +#else sa.sa_handler=handle_segfault; +#endif sigaction(SIGSEGV, &sa, NULL); #ifdef SIGBUS sigaction(SIGBUS, &sa, NULL); #endif sigaction(SIGILL, &sa, NULL); + sigaction(SIGFPE, &sa, NULL); + } + +#ifdef HAVE_GETRLIMIT + if (test_flags & TEST_CORE_ON_SIGNAL) + { + /* Change limits so that we will get a core file */ + struct rlimit rl; + rl.rlim_cur = rl.rlim_max = RLIM_INFINITY; + if (setrlimit(RLIMIT_CORE, &rl) && global_system_variables.log_warnings) + sql_print_error("Warning: setrlimit could not change the size of core files to 'infinity'; We may not be able to generate a core file on signals"); } +#endif (void) sigemptyset(&set); #ifdef THREAD_SPECIFIC_SIGPIPE sigset(SIGPIPE,abort_thread); @@ -1378,8 +1636,8 @@ static void init_signals(void) sigaddset(&set,SIGQUIT); sigaddset(&set,SIGTERM); sigaddset(&set,SIGHUP); - sigset(SIGTERM,print_signal_warning); // If it's blocked by parent - signal(SIGHUP,print_signal_warning); // If it's blocked by parent + sigset(SIGTERM, print_signal_warning); // If it's blocked by parent + sigset(SIGHUP, print_signal_warning); // If it's blocked by parent #ifdef SIGTSTP sigaddset(&set,SIGTSTP); #endif @@ -1421,20 +1679,22 @@ static void start_signal_handler(void) } -/* -** This threads handles all signals and alarms -*/ +/* This threads handles all signals and alarms */ /* ARGSUSED */ -static void *signal_hand(void *arg __attribute__((unused))) +extern "C" void *signal_hand(void *arg __attribute__((unused))) { sigset_t set; int sig; my_thread_init(); // Init new thread DBUG_ENTER("signal_hand"); + signal_thread_in_use= 1; - /* Setup alarm handler */ - init_thr_alarm(max_connections+max_insert_delayed_threads); + /* + Setup alarm handler + The two extra handlers are for slave threads + */ + init_thr_alarm(max_connections+max_insert_delayed_threads+2); #if SIGINT != THR_KILL_SIGNAL (void) sigemptyset(&set); // Setup up SIGINT for debug (void) sigaddset(&set,SIGINT); // For debugging @@ -1463,24 +1723,42 @@ static void *signal_hand(void *arg __attribute__((unused))) (void) my_close(pidFile,MYF(0)); } } +#ifdef HAVE_STACK_TRACE_ON_SEGV + if (opt_do_pstack) + { + sprintf(pstack_file_name,"mysqld-%lu-%%d-%%d.backtrace", (ulong)getpid()); + pstack_install_segv_action(pstack_file_name); + } +#endif /* HAVE_STACK_TRACE_ON_SEGV */ - // signal to start_signal_handler that we are ready + /* + signal to start_signal_handler that we are ready + This works by waiting for start_signal_handler to free mutex, + after which we signal it that we are ready. + At this pointer there is no other threads running, so there + should not be any other pthread_cond_signal() calls. + */ (void) pthread_mutex_lock(&LOCK_thread_count); - (void) pthread_cond_signal(&COND_thread_count); (void) pthread_mutex_unlock(&LOCK_thread_count); + (void) pthread_cond_broadcast(&COND_thread_count); + (void) pthread_sigmask(SIG_BLOCK,&set,NULL); for (;;) { int error; // Used when debugging if (shutdown_in_progress && !abort_loop) { - sig= MYSQL_KILL_SIGNAL; + sig= SIGTERM; error=0; } else while ((error=my_sigwait(&set,&sig)) == EINTR) ; if (cleanup_done) + { + my_thread_end(); + signal_thread_in_use= 0; pthread_exit(0); // Safety + } switch (sig) { case SIGTERM: case SIGQUIT: @@ -1497,17 +1775,23 @@ static void *signal_hand(void *arg __attribute__((unused))) if (!(opt_specialflag & SPECIAL_NO_PRIOR)) my_pthread_attr_setprio(&connection_attrib,INTERRUPT_PRIOR); if (pthread_create(&tmp,&connection_attrib, kill_server_thread, - (void*) 0)) + (void*) sig)) sql_print_error("Error: Can't create thread to kill server"); #else - kill_server((void*) sig); // MIT THREAD has a alarm thread + kill_server((void*) sig); // MIT THREAD has a alarm thread #endif } break; case SIGHUP: - reload_acl_and_cache((THD*) 0,REFRESH_LOG, - (TABLE_LIST*) 0); // Flush logs - mysql_print_status((THD*) 0); // Send debug some info + if (!abort_loop) + { + reload_acl_and_cache((THD*) 0, + (REFRESH_LOG | REFRESH_TABLES | REFRESH_FAST | + REFRESH_STATUS | REFRESH_GRANT | + REFRESH_THREADS | REFRESH_HOSTS), + (TABLE_LIST*) 0); // Flush logs + mysql_print_status((THD*) 0); // Send debug some info + } break; #ifdef USE_ONE_SIGNAL_HAND case THR_SERVER_ALARM: @@ -1516,7 +1800,7 @@ static void *signal_hand(void *arg __attribute__((unused))) #endif default: #ifdef EXTRA_DEBUG - sql_print_error("Warning: Got signal: %d, error: %d",sig,error); /* purecov: tested */ + sql_print_error("Warning: Got signal: %d error: %d",sig,error); /* purecov: tested */ #endif break; /* purecov: tested */ } @@ -1524,18 +1808,21 @@ static void *signal_hand(void *arg __attribute__((unused))) return(0); /* purecov: deadcode */ } +static void check_data_home(const char *path) +{} + #endif /* __WIN__*/ /* -** All global error messages are sent here where the first one is stored for -** the client + All global error messages are sent here where the first one is stored for + the client */ /* ARGSUSED */ -static int my_message_sql(uint error, const char *str, - myf MyFlags __attribute__((unused))) +extern "C" int my_message_sql(uint error, const char *str, + myf MyFlags __attribute__((unused))) { NET *net; DBUG_ENTER("my_message_sql"); @@ -1553,6 +1840,17 @@ static int my_message_sql(uint error, const char *str, DBUG_RETURN(0); } + +/* + Forget last error message (if we got one) +*/ + +void clear_error_message(THD *thd) +{ + thd->net.last_error[0]= 0; +} + + #ifdef __WIN__ struct utsname @@ -1560,28 +1858,29 @@ struct utsname char nodename[FN_REFLEN]; }; + int uname(struct utsname *a) { return -1; } -#endif -#ifdef __WIN__ -pthread_handler_decl(handle_shutdown,arg) +extern "C" pthread_handler_decl(handle_shutdown,arg) { MSG msg; + SHUTDOWN_THD; my_thread_init(); /* this call should create the message queue for this thread */ PeekMessage(&msg, NULL, 1, 65534,PM_NOREMOVE); - +#if !defined(EMBEDDED_LIBRARY) if (WaitForSingleObject(hEventShutdown,INFINITE)==WAIT_OBJECT_0) +#endif kill_server(MYSQL_KILL_SIGNAL); return 0; } -int __stdcall handle_kill(ulong ctrl_type) +int STDCALL handle_kill(ulong ctrl_type) { if (ctrl_type == CTRL_CLOSE_EVENT || ctrl_type == CTRL_SHUTDOWN_EVENT) @@ -1594,8 +1893,9 @@ int __stdcall handle_kill(ulong ctrl_type) #endif #ifdef OS2 -pthread_handler_decl(handle_shutdown,arg) +extern "C" pthread_handler_decl(handle_shutdown,arg) { + SHUTDOWN_THD; my_thread_init(); // wait semaphore @@ -1604,8 +1904,10 @@ pthread_handler_decl(handle_shutdown,arg) // close semaphore and kill server pthread_cond_destroy( &eventShutdown); - // exit main loop on main thread, so kill will be done from - // main thread (this is thread 2) + /* + Exit main loop on main thread, so kill will be done from + main thread (this is thread 2) + */ abort_loop = 1; // unblock select() @@ -1616,27 +1918,27 @@ pthread_handler_decl(handle_shutdown,arg) } #endif -const char *load_default_groups[]= { "mysqld","server",0 }; -#ifdef HAVE_LIBWRAP -char *libwrapName=NULL; -#endif +const char *load_default_groups[]= { "mysqld","server",0 }; -static void open_log(MYSQL_LOG *log, const char *hostname, - const char *opt_name, const char *extension, - enum_log_type type) +bool open_log(MYSQL_LOG *log, const char *hostname, + const char *opt_name, const char *extension, + const char *index_file_name, + enum_log_type type, bool read_append, + bool no_auto_events) { char tmp[FN_REFLEN]; if (!opt_name || !opt_name[0]) { - /* TODO: The following should be using fn_format(); We just need to - first change fn_format() to cut the file name if it's too long. + /* + TODO: The following should be using fn_format(); We just need to + first change fn_format() to cut the file name if it's too long. */ strmake(tmp,hostname,FN_REFLEN-5); - strmov(strcend(tmp,'.'),extension); + strmov(fn_ext(tmp),extension); opt_name=tmp; } - // get rid of extention if the log is binary to avoid problems + // get rid of extension if the log is binary to avoid problems if (type == LOG_BIN) { char *p = fn_ext(opt_name); @@ -1647,7 +1949,9 @@ static void open_log(MYSQL_LOG *log, const char *hostname, opt_name=tmp; } } - log->open(opt_name,type); + return log->open(opt_name, type, 0, index_file_name, + (read_append) ? SEQ_READ_APPEND : WRITE_CACHE, + no_auto_events); } @@ -1662,10 +1966,19 @@ int main(int argc, char **argv) my_umask=0660; // Default umask for new files my_umask_dir=0700; // Default umask for new directories + MAIN_THD; + /* + Initialize signal_th and shutdown_th to main_th for default value + as we need to initialize them to something safe. They are used + when compiled with safemalloc. + */ + SIGNAL_THD; + SHUTDOWN_THD; MY_INIT(argv[0]); // init my_sys library & pthreads tzset(); // Set tzname start_time=time((time_t*) 0); + #ifdef OS2 { // fix timezone for daylight saving @@ -1693,7 +2006,7 @@ int main(int argc, char **argv) if (gethostname(glob_hostname,sizeof(glob_hostname)-4) < 0) strmov(glob_hostname,"mysql"); strmake(pidfile_name, glob_hostname, sizeof(pidfile_name)-5); - strmov(strcend(pidfile_name,'.'),".pid"); // Add extension + strmov(fn_ext(pidfile_name),".pid"); // Add proper extension #ifndef DBUG_OFF strxmov(strend(server_version),MYSQL_SERVER_SUFFIX,"-debug",NullS); #else @@ -1708,15 +2021,17 @@ int main(int argc, char **argv) #endif load_defaults(MYSQL_CONFIG_NAME,load_default_groups,&argc,&argv); defaults_argv=argv; - mysql_tmpdir=getenv("TMPDIR"); /* Use this if possible */ + + /* Get default temporary directory */ + opt_mysql_tmpdir=getenv("TMPDIR"); /* Use this if possible */ #if defined( __WIN__) || defined(OS2) - if (!mysql_tmpdir) - mysql_tmpdir=getenv("TEMP"); - if (!mysql_tmpdir) - mysql_tmpdir=getenv("TMP"); + if (!opt_mysql_tmpdir) + opt_mysql_tmpdir=getenv("TEMP"); + if (!opt_mysql_tmpdir) + opt_mysql_tmpdir=getenv("TMP"); #endif - if (!mysql_tmpdir || !mysql_tmpdir[0]) - mysql_tmpdir=(char*) P_tmpdir; /* purecov: inspected */ + if (!opt_mysql_tmpdir || !opt_mysql_tmpdir[0]) + opt_mysql_tmpdir=(char*) P_tmpdir; /* purecov: inspected */ set_options(); get_options(argc,argv); @@ -1743,32 +2058,32 @@ int main(int argc, char **argv) (void) pthread_mutex_init(&LOCK_bytes_sent,MY_MUTEX_INIT_FAST); (void) pthread_mutex_init(&LOCK_bytes_received,MY_MUTEX_INIT_FAST); (void) pthread_mutex_init(&LOCK_timezone,MY_MUTEX_INIT_FAST); - (void) pthread_mutex_init(&LOCK_binlog_update, MY_MUTEX_INIT_FAST); // QQ NOT USED - (void) pthread_mutex_init(&LOCK_slave, MY_MUTEX_INIT_FAST); - (void) pthread_mutex_init(&LOCK_server_id, MY_MUTEX_INIT_FAST); (void) pthread_mutex_init(&LOCK_user_conn, MY_MUTEX_INIT_FAST); + (void) pthread_mutex_init(&LOCK_rpl_status, MY_MUTEX_INIT_FAST); + (void) pthread_mutex_init(&LOCK_active_mi, MY_MUTEX_INIT_FAST); + (void) pthread_mutex_init(&LOCK_global_system_variables, MY_MUTEX_INIT_FAST); (void) pthread_cond_init(&COND_thread_count,NULL); (void) pthread_cond_init(&COND_refresh,NULL); (void) pthread_cond_init(&COND_thread_cache,NULL); (void) pthread_cond_init(&COND_flush_thread_cache,NULL); (void) pthread_cond_init(&COND_manager,NULL); - (void) pthread_cond_init(&COND_binlog_update, NULL); - (void) pthread_cond_init(&COND_slave_stopped, NULL); - (void) pthread_cond_init(&COND_slave_start, NULL); + (void) pthread_cond_init(&COND_rpl_status, NULL); init_signals(); - if (set_default_charset_by_name(default_charset, MYF(MY_WME))) - unireg_abort(1); + if (set_default_charset_by_name(sys_charset.value, MYF(MY_WME))) + exit(1); charsets_list = list_charsets(MYF(MY_COMPILED_SETS|MY_CONFIG_SETS)); #ifdef HAVE_OPENSSL if (opt_use_ssl) { - ssl_acceptor_fd = VioSSLAcceptorFd_new(opt_ssl_key, opt_ssl_cert, - opt_ssl_ca, opt_ssl_capath); + /* having ssl_acceptor_fd != 0 signals the use of SSL */ + ssl_acceptor_fd= new_VioSSLAcceptorFd(opt_ssl_key, opt_ssl_cert, + opt_ssl_ca, opt_ssl_capath, + opt_ssl_cipher); + DBUG_PRINT("info",("ssl_acceptor_fd: %lx", (long) ssl_acceptor_fd)); if (!ssl_acceptor_fd) - opt_use_ssl=0; - /* having ssl_acceptor_fd!=0 signals the use of SSL */ + opt_use_ssl = 0; } #endif /* HAVE_OPENSSL */ @@ -1784,13 +2099,25 @@ int main(int argc, char **argv) (void) pthread_attr_setdetachstate(&connection_attrib, PTHREAD_CREATE_DETACHED); pthread_attr_setstacksize(&connection_attrib,thread_stack); - +#ifdef HAVE_PTHREAD_ATTR_GETSTACKSIZE + { + /* Retrieve used stack size; Needed for checking stack overflows */ + size_t stack_size; + pthread_attr_getstacksize(&connection_attrib, &stack_size); + if (global_system_variables.log_warnings && stack_size != thread_stack) + { + sql_print_error("Warning: Asked for %ld thread stack, but got %ld", + thread_stack, stack_size); + thread_stack= stack_size; + } + } +#endif if (!(opt_specialflag & SPECIAL_NO_PRIOR)) my_pthread_attr_setprio(&connection_attrib,WAIT_PRIOR); pthread_attr_setscope(&connection_attrib, PTHREAD_SCOPE_SYSTEM); #if defined( SET_RLIMIT_NOFILE) || defined( OS2) - /* connections and databases neads lots of files */ + /* connections and databases needs lots of files */ { uint wanted_files=10+(uint) max(max_connections*5, max_connections+table_cache_size*2); @@ -1812,6 +2139,7 @@ int main(int argc, char **argv) init_errmessage(); /* Read error messages from file */ lex_init(); item_init(); + set_var_init(); mysys_uses_curses=0; #ifdef USE_REGEX regex_init(); @@ -1822,101 +2150,67 @@ int main(int argc, char **argv) unireg_abort(1); /* - ** We have enough space for fiddling with the argv, continue + We have enough space for fiddling with the argv, continue */ umask(((~my_umask) & 0666)); + check_data_home(mysql_real_data_home); if (my_setwd(mysql_real_data_home,MYF(MY_WME))) { unireg_abort(1); /* purecov: inspected */ } + mysql_data_home= mysql_data_home_buff; mysql_data_home[0]=FN_CURLIB; // all paths are relative from here mysql_data_home[1]=0; server_init(); table_cache_init(); hostname_cache_init(); - sql_cache_init(); + query_cache_result_size_limit(query_cache_limit); + query_cache_resize(query_cache_size); randominit(&sql_rand,(ulong) start_time,(ulong) start_time/2); reset_floating_point_exceptions(); init_thr_lock(); - - /* Fix varibles that are base 1024*1024 */ - myisam_max_temp_length= (my_off_t) min(((ulonglong) myisam_max_sort_file_size)*1024*1024, (ulonglong) MAX_FILE_SIZE); - myisam_max_extra_temp_length= (my_off_t) min(((ulonglong) myisam_max_extra_sort_file_size)*1024*1024, (ulonglong) MAX_FILE_SIZE); + init_slave_list(); +#ifdef HAVE_OPENSSL + if (des_key_file) + load_des_key_file(des_key_file); +#endif /* HAVE_OPENSSL */ /* Setup log files */ if (opt_log) - open_log(&mysql_log, glob_hostname, opt_logname, ".log", LOG_NORMAL); + open_log(&mysql_log, glob_hostname, opt_logname, ".log", NullS, + LOG_NORMAL); if (opt_update_log) { open_log(&mysql_update_log, glob_hostname, opt_update_logname, "", - LOG_NEW); + NullS, LOG_NEW); using_update_log=1; } + + if (opt_slow_log) + open_log(&mysql_slow_log, glob_hostname, opt_slow_logname, "-slow.log", + NullS, LOG_NORMAL); - //make sure slave thread gets started - // if server_id is set, valid master.info is present, and master_host has - // not been specified - if(server_id && !master_host) - { - char fname[FN_REFLEN+128]; - MY_STAT stat_area; - fn_format(fname, master_info_file, mysql_data_home, "", 4+16+32); - if(my_stat(fname, &stat_area, MYF(0)) && !init_master_info(&glob_mi)) - master_host = glob_mi.host; - } - - if (opt_bin_log && !server_id) + if (opt_error_log) { - server_id= !master_host ? 1 : 2; - switch (server_id) { -#ifdef EXTRA_DEBUG - case 1: - sql_print_error("\ -Warning: You have enabled the binary log, but you haven't set server-id:\n\ -Updates will be logged to the binary log, but connections to slaves will\n\ -not be accepted."); - break; -#endif - case 2: - sql_print_error("\ -Warning: You should set server-id to a non-0 value if master_host is set.\n\ -The server will not act as a slave."); - break; - } - } - if (opt_bin_log) - { - if (!opt_bin_logname) + if (!log_error_file_ptr[0]) + fn_format(log_error_file, glob_hostname, mysql_data_home, ".err", 0); + else + fn_format(log_error_file, log_error_file_ptr, mysql_data_home, ".err", + MY_UNPACK_FILENAME | MY_SAFE_PATH); + if (!log_error_file[0]) + opt_error_log= 1; // Too long file name + else { - char tmp[FN_REFLEN]; - /* TODO: The following should be using fn_format(); We just need to - first change fn_format() to cut the file name if it's too long. - */ - strmake(tmp,glob_hostname,FN_REFLEN-5); - strmov(strcend(tmp,'.'),"-bin"); - opt_bin_logname=my_strdup(tmp,MYF(MY_WME)); + if (freopen(log_error_file, "a+", stdout)) + freopen(log_error_file, "a+", stderr); } - mysql_bin_log.set_index_file_name(opt_binlog_index_name); - open_log(&mysql_bin_log, glob_hostname, opt_bin_logname, "-bin", - LOG_BIN); - using_update_log=1; - } - - if (opt_slow_log) - open_log(&mysql_slow_log, glob_hostname, opt_slow_logname, "-slow.log", - LOG_NORMAL); -#ifdef __WIN__ -#define MYSQL_ERR_FILE "mysql.err" - if (!opt_console) - { - freopen(MYSQL_ERR_FILE,"a+",stdout); - freopen(MYSQL_ERR_FILE,"a+",stderr); } -#endif if (ha_init()) { sql_print_error("Can't init databases"); - exit(1); + if (unix_sock != INVALID_SOCKET) + unlink(mysql_unix_port); + unireg_abort(1); } ha_key_cache(); #if defined(HAVE_MLOCKALL) && defined(MCL_CURRENT) @@ -1931,15 +2225,15 @@ The server will not act as a slave."); } #else locked_in_memory=0; -#endif +#endif if (opt_myisam_log) - (void) mi_log( 1 ); - ft_init_stopwords(ft_precompiled_stopwords); /* SerG */ + (void) mi_log(1); + ft_init_stopwords(); #ifdef __WIN__ if (!opt_console) - FreeConsole(); // Remove window + FreeConsole(); // Remove window #endif /* @@ -1951,28 +2245,66 @@ The server will not act as a slave."); pthread_key_create(&THR_MALLOC,NULL)) { sql_print_error("Can't create thread-keys"); - exit(1); + if (unix_sock != INVALID_SOCKET) + unlink(mysql_unix_port); + unireg_abort(1); } start_signal_handler(); // Creates pidfile - if (acl_init(opt_noacl)) + if (acl_init((THD*) 0, opt_noacl)) { + abort_loop=1; select_thread_in_use=0; - (void) pthread_kill(signal_thread,MYSQL_KILL_SIGNAL); +#ifndef __NETWARE__ + (void) pthread_kill(signal_thread, MYSQL_KILL_SIGNAL); +#endif /* __NETWARE__ */ #ifndef __WIN__ if (!opt_bootstrap) - (void) my_delete(pidfile_name,MYF(MY_WME)); // Not neaded anymore + (void) my_delete(pidfile_name,MYF(MY_WME)); // Not needed anymore #endif - exit(1); + if (unix_sock != INVALID_SOCKET) + unlink(mysql_unix_port); + unireg_abort(1); } if (!opt_noacl) - (void) grant_init(); - if (max_user_connections) - init_max_user_conn(); + (void) grant_init((THD*) 0); + init_max_user_conn(); + init_update_queries(); + DBUG_ASSERT(current_thd == 0); #ifdef HAVE_DLOPEN if (!opt_noacl) udf_init(); #endif + /* init_slave() must be called after the thread keys are created */ + init_slave(); + + DBUG_ASSERT(current_thd == 0); + if (opt_bin_log && !server_id) + { + server_id= !master_host ? 1 : 2; + switch (server_id) { +#ifdef EXTRA_DEBUG + case 1: + sql_print_error("\ +Warning: You have enabled the binary log, but you haven't set server-id:\n\ +Updates will be logged to the binary log, but connections to slaves will\n\ +not be accepted."); + break; +#endif + case 2: + sql_print_error("\ +Warning: You should set server-id to a non-0 value if master_host is set.\n\ +The server will not act as a slave."); + break; + } + } + if (opt_bin_log) + { + open_log(&mysql_bin_log, glob_hostname, opt_bin_logname, "-bin", + opt_binlog_index_name,LOG_BIN); + using_update_log=1; + } + if (opt_bootstrap) { @@ -1989,7 +2321,7 @@ The server will not act as a slave."); } } (void) thr_setconcurrency(concurrency); // 10 by default -#ifdef __WIN__ //IRENA +#if defined(__WIN__) && !defined(EMBEDDED_LIBRARY) //IRENA { hEventShutdown=CreateEvent(0, FALSE, FALSE, shutdown_event_name); pthread_t hThread; @@ -2020,18 +2352,9 @@ The server will not act as a slave."); sql_print_error("Warning: Can't create thread to manage maintenance"); } - // slave thread - if (master_host) - { - pthread_t hThread; - if (!opt_skip_slave_start && - pthread_create(&hThread, &connection_attrib, handle_slave, 0)) - sql_print_error("Warning: Can't create thread to handle slave"); - else if(opt_skip_slave_start) - init_master_info(&glob_mi); - } - - printf(ER(ER_READY),my_progname,server_version, mysql_unix_port, mysql_port); + printf(ER(ER_READY),my_progname,server_version, + ((unix_sock == INVALID_SOCKET) ? (char*) "" : mysql_unix_port), + mysql_port); fflush(stdout); #ifdef __NT__ @@ -2069,15 +2392,13 @@ The server will not act as a slave."); } } while (handler_count > 0) - { pthread_cond_wait(&COND_handler_count,&LOCK_thread_count); - } } pthread_mutex_unlock(&LOCK_thread_count); } #else handle_connections_sockets(0); -#ifdef EXTRA_DEBUG +#ifdef EXTRA_DEBUG2 sql_print_error("Exiting main thread"); #endif #endif /* __NT__ */ @@ -2087,49 +2408,63 @@ The server will not act as a slave."); DBUG_PRINT("quit",("Exiting main thread")); #ifndef __WIN__ -#ifdef EXTRA_DEBUG +#ifdef EXTRA_DEBUG2 sql_print_error("Before Lock_thread_count"); #endif (void) pthread_mutex_lock(&LOCK_thread_count); + DBUG_PRINT("quit", ("Got thread_count mutex")); select_thread_in_use=0; // For close_connections - (void) pthread_cond_broadcast(&COND_thread_count); (void) pthread_mutex_unlock(&LOCK_thread_count); -#ifdef EXTRA_DEBUG + (void) pthread_cond_broadcast(&COND_thread_count); +#ifdef EXTRA_DEBUG2 sql_print_error("After lock_thread_count"); #endif -#endif +#endif /* __WIN__ */ /* Wait until cleanup is done */ (void) pthread_mutex_lock(&LOCK_thread_count); while (!ready_to_exit) - { pthread_cond_wait(&COND_thread_count,&LOCK_thread_count); - } (void) pthread_mutex_unlock(&LOCK_thread_count); -#ifdef __WIN__ - if (Service.IsNT()) - { - if(start_mode) - Service.Stop(); - else + +#if defined(__WIN__) && !defined(EMBEDDED_LIBRARY) + if (Service.IsNT() && start_mode) + Service.Stop(); + else + { + Service.SetShutdownEvent(0); + if (hEventShutdown) + CloseHandle(hEventShutdown); + } +#endif +#ifndef __NETWARE__ + { + uint i; + /* + Wait up to 10 seconds for signal thread to die. We use this mainly to + avoid getting warnings that my_thread_end has not been called + */ + for (i= 0 ; i < 100 && signal_thread_in_use; i++) { - Service.SetShutdownEvent(0); - if(hEventShutdown) CloseHandle(hEventShutdown); + if (pthread_kill(signal_thread, MYSQL_KILL_SIGNAL)) + break; + my_sleep(100); // Give it time to die } - } - else - { - Service.SetShutdownEvent(0); - if(hEventShutdown) CloseHandle(hEventShutdown); - } + } #endif + clean_up_mutexes(); my_end(opt_endinfo ? MY_CHECK_ERROR | MY_GIVE_INFO : 0); exit(0); return(0); /* purecov: deadcode */ } -#if defined(__WIN__) +/**************************************************************************** + Main and thread entry function for Win32 + (all this is needed only to run mysqld as a service on WinNT) +****************************************************************************/ + +#if defined(__WIN__) && !defined(EMBEDDED_LIBRARY) int mysql_service(void *p) { if (use_opt_args) @@ -2257,14 +2592,21 @@ int main(int argc, char **argv) #endif +/* + Execute all commands from a file. Used by the mysql_install_db script to + create MySQL privilege tables without having to start a full MySQL server. +*/ + static int bootstrap(FILE *file) { THD *thd= new THD; int error; + DBUG_ENTER("bootstrap"); + thd->bootstrap=1; thd->client_capabilities=0; - my_net_init(&thd->net,(Vio*) 0); - thd->max_packet_length=thd->net.max_packet; + my_net_init(&thd->net,(st_vio*) 0); + thd->max_client_packet_length= thd->net.max_packet; thd->master_access= ~0; thd->thread_id=thread_id++; thread_count++; @@ -2274,7 +2616,7 @@ static int bootstrap(FILE *file) (void*) thd)) { sql_print_error("Warning: Can't create thread to handle bootstrap"); - return -1; + DBUG_RETURN(-1); } /* Wait for thread to die */ (void) pthread_mutex_lock(&LOCK_thread_count); @@ -2288,7 +2630,7 @@ static int bootstrap(FILE *file) net_end(&thd->net); thd->cleanup(); delete thd; - return error; + DBUG_RETURN(error); } static bool read_init_file(char *file_name) @@ -2309,7 +2651,7 @@ static void create_new_thread(THD *thd) DBUG_ENTER("create_new_thread"); NET *net=&thd->net; // For easy ref - net->timeout = (uint) connect_timeout; // Timeout for read + net->read_timeout = (uint) connect_timeout; if (protocol_version > 9) net->return_errno=1; @@ -2321,13 +2663,7 @@ static void create_new_thread(THD *thd) delete thd; DBUG_VOID_RETURN; } - if (pthread_mutex_lock(&LOCK_thread_count)) - { - DBUG_PRINT("error",("Can't lock LOCK_thread_count")); - close_connection(net,ER_OUT_OF_RESOURCES); - delete thd; - DBUG_VOID_RETURN; - } + pthread_mutex_lock(&LOCK_thread_count); if (thread_count-delayed_insert_threads > max_used_connections) max_used_connections=thread_count-delayed_insert_threads; thd->thread_id=thread_id++; @@ -2388,10 +2724,33 @@ static void create_new_thread(THD *thd) DBUG_VOID_RETURN; } +#ifdef SIGNALS_DONT_BREAK_READ +inline void kill_broken_server() +{ + /* hack to get around signals ignored in syscalls for problem OS's */ + if ( +#if !defined(__NETWARE__) + unix_sock == INVALID_SOCKET || +#endif + (!opt_disable_networking && ip_sock == INVALID_SOCKET)) + { + select_thread_in_use = 0; +#ifdef __NETWARE__ + kill_server(MYSQL_KILL_SIGNAL); /* never returns */ +#else + kill_server((void*)MYSQL_KILL_SIGNAL); /* never returns */ +#endif /* __NETWARE__ */ + } +} +#define MAYBE_BROKEN_SYSCALL kill_broken_server(); +#else +#define MAYBE_BROKEN_SYSCALL +#endif /* Handle new connections and spawn new process to handle them */ -pthread_handler_decl(handle_connections_sockets,arg __attribute__((unused))) +extern "C" pthread_handler_decl(handle_connections_sockets, + arg __attribute__((unused))) { my_socket sock,new_sock; uint error_count=0; @@ -2400,7 +2759,7 @@ pthread_handler_decl(handle_connections_sockets,arg __attribute__((unused))) THD *thd; struct sockaddr_in cAddr; int ip_flags=0,socket_flags=0,flags; - Vio *vio_tmp; + st_vio *vio_tmp; DBUG_ENTER("handle_connections_sockets"); LINT_INIT(new_sock); @@ -2427,7 +2786,7 @@ pthread_handler_decl(handle_connections_sockets,arg __attribute__((unused))) while (!abort_loop) { readFDs=clientFDs; -#ifdef HPUX +#ifdef HPUX10 if (select(max_used_connection,(int*) &readFDs,0,0,0) < 0) continue; #else @@ -2438,17 +2797,17 @@ pthread_handler_decl(handle_connections_sockets,arg __attribute__((unused))) if (!select_errors++ && !abort_loop) /* purecov: inspected */ sql_print_error("mysqld: Got error %d from select",socket_errno); /* purecov: inspected */ } - MAYBE_BROKEN_SYSCALL; + MAYBE_BROKEN_SYSCALL continue; } -#endif /* HPUX */ +#endif /* HPUX10 */ if (abort_loop) + { + MAYBE_BROKEN_SYSCALL; break; + } - /* - ** Is this a new connection request - */ - + /* Is this a new connection request ? */ #ifdef HAVE_SYS_UN_H if (FD_ISSET(unix_sock,&readFDs)) { @@ -2477,6 +2836,13 @@ pthread_handler_decl(handle_connections_sockets,arg __attribute__((unused))) size_socket length=sizeof(struct sockaddr_in); new_sock = accept(sock, my_reinterpret_cast(struct sockaddr *) (&cAddr), &length); +#ifdef __NETWARE__ + // TODO: temporary fix, waiting for TCP/IP fix - DEFECT000303149 + if ((new_sock == INVALID_SOCKET) && (socket_errno == EINVAL)) + { + kill_server(SIGTERM); + } +#endif if (new_sock != INVALID_SOCKET || (socket_errno != SOCKET_EINTR && socket_errno != SOCKET_EAGAIN)) break; @@ -2557,7 +2923,8 @@ pthread_handler_decl(handle_connections_sockets,arg __attribute__((unused))) if (!(thd= new THD)) { - (void) shutdown(new_sock,2); VOID(closesocket(new_sock)); + (void) shutdown(new_sock,2); + VOID(closesocket(new_sock)); continue; } if (!(vio_tmp=vio_new(new_sock, @@ -2597,7 +2964,7 @@ pthread_handler_decl(handle_connections_sockets,arg __attribute__((unused))) #ifdef __NT__ -pthread_handler_decl(handle_connections_namedpipes,arg) +extern "C" pthread_handler_decl(handle_connections_namedpipes,arg) { HANDLE hConnectedPipe; BOOL fConnected; @@ -2624,8 +2991,8 @@ pthread_handler_decl(handle_connections_namedpipes,arg) PIPE_READMODE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, - (int) net_buffer_length, - (int) net_buffer_length, + (int) global_system_variables.net_buffer_length, + (int) global_system_variables.net_buffer_length, NMPWAIT_USE_DEFAULT_WAIT, &saPipeSecurity )) == INVALID_HANDLE_VALUE ) @@ -2642,8 +3009,8 @@ pthread_handler_decl(handle_connections_namedpipes,arg) PIPE_READMODE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, - (int) net_buffer_length, - (int) net_buffer_length, + (int) global_system_variables.net_buffer_length, + (int) global_system_variables.net_buffer_length, NMPWAIT_USE_DEFAULT_WAIT, &saPipeSecurity)) == INVALID_HANDLE_VALUE) @@ -2673,8 +3040,8 @@ pthread_handler_decl(handle_connections_namedpipes,arg) pthread_mutex_lock(&LOCK_thread_count); handler_count--; - pthread_cond_signal(&COND_handler_count); pthread_mutex_unlock(&LOCK_thread_count); + pthread_cond_signal(&COND_handler_count); DBUG_RETURN(0); } #endif /* __NT__ */ @@ -2685,510 +3052,891 @@ pthread_handler_decl(handle_connections_namedpipes,arg) ******************************************************************************/ enum options { - OPT_ISAM_LOG=256, OPT_SKIP_NEW, - OPT_SKIP_GRANT, OPT_SKIP_LOCK, - OPT_ENABLE_LOCK, OPT_USE_LOCKING, - OPT_SOCKET, OPT_UPDATE_LOG, - OPT_BIN_LOG, OPT_SKIP_RESOLVE, - OPT_SKIP_NETWORKING, OPT_BIN_LOG_INDEX, - OPT_BIND_ADDRESS, OPT_PID_FILE, - OPT_SKIP_PRIOR, OPT_BIG_TABLES, - OPT_STANDALONE, OPT_ONE_THREAD, - OPT_CONSOLE, OPT_LOW_PRIORITY_UPDATES, - OPT_SKIP_HOST_CACHE, OPT_LONG_FORMAT, - OPT_FLUSH, OPT_SAFE, - OPT_BOOTSTRAP, OPT_SKIP_SHOW_DB, - OPT_TABLE_TYPE, OPT_INIT_FILE, - OPT_DELAY_KEY_WRITE, OPT_SLOW_QUERY_LOG, - OPT_SKIP_DELAY_KEY_WRITE, OPT_CHARSETS_DIR, - OPT_BDB_HOME, OPT_BDB_LOG, - OPT_BDB_TMP, OPT_BDB_NOSYNC, - OPT_BDB_LOCK, OPT_BDB_SKIP, - OPT_BDB_NO_RECOVER, OPT_BDB_SHARED, - OPT_MASTER_HOST, OPT_MASTER_USER, - OPT_MASTER_PASSWORD, OPT_MASTER_PORT, - OPT_MASTER_INFO_FILE, OPT_MASTER_CONNECT_RETRY, - OPT_MASTER_RETRY_COUNT, - OPT_SQL_BIN_UPDATE_SAME, OPT_REPLICATE_DO_DB, - OPT_REPLICATE_IGNORE_DB, OPT_LOG_SLAVE_UPDATES, - OPT_BINLOG_DO_DB, OPT_BINLOG_IGNORE_DB, - OPT_WANT_CORE, OPT_SKIP_CONCURRENT_INSERT, - OPT_MEMLOCK, OPT_MYISAM_RECOVER, - OPT_REPLICATE_REWRITE_DB, OPT_SERVER_ID, - OPT_SKIP_SLAVE_START, OPT_SKIP_INNOBASE, - OPT_SAFEMALLOC_MEM_LIMIT, OPT_REPLICATE_DO_TABLE, - OPT_REPLICATE_IGNORE_TABLE, OPT_REPLICATE_WILD_DO_TABLE, - OPT_REPLICATE_WILD_IGNORE_TABLE, - OPT_DISCONNECT_SLAVE_EVENT_COUNT, - OPT_ABORT_SLAVE_EVENT_COUNT, - OPT_INNODB_DATA_HOME_DIR, - OPT_INNODB_DATA_FILE_PATH, - OPT_INNODB_LOG_GROUP_HOME_DIR, - OPT_INNODB_LOG_ARCH_DIR, - OPT_INNODB_LOG_ARCHIVE, - OPT_INNODB_FLUSH_LOG_AT_TRX_COMMIT, - OPT_INNODB_FAST_SHUTDOWN, - OPT_INNODB_UNIX_FILE_FLUSH_METHOD, - OPT_SAFE_SHOW_DB, - OPT_GEMINI_SKIP, OPT_INNODB_SKIP, - OPT_TEMP_POOL, OPT_TX_ISOLATION, - OPT_GEMINI_FLUSH_LOG, OPT_GEMINI_RECOVER, - OPT_GEMINI_UNBUFFERED_IO, OPT_SKIP_SAFEMALLOC, - OPT_SKIP_STACK_TRACE, OPT_SKIP_SYMLINKS, - OPT_MAX_BINLOG_DUMP_EVENTS, OPT_SPORADIC_BINLOG_DUMP_FAIL, - OPT_SAFE_USER_CREATE, OPT_SQL_MODE, - OPT_HAVE_NAMED_PIPE, - OPT_SLAVE_SKIP_ERRORS, OPT_LOCAL_INFILE + OPT_ISAM_LOG=256, OPT_SKIP_NEW, + OPT_SKIP_GRANT, OPT_SKIP_LOCK, + OPT_ENABLE_LOCK, OPT_USE_LOCKING, + OPT_SOCKET, OPT_UPDATE_LOG, + OPT_BIN_LOG, OPT_SKIP_RESOLVE, + OPT_SKIP_NETWORKING, OPT_BIN_LOG_INDEX, + OPT_BIND_ADDRESS, OPT_PID_FILE, + OPT_SKIP_PRIOR, OPT_BIG_TABLES, + OPT_STANDALONE, OPT_ONE_THREAD, + OPT_CONSOLE, OPT_LOW_PRIORITY_UPDATES, + OPT_SKIP_HOST_CACHE, OPT_LONG_FORMAT, + OPT_FLUSH, OPT_SAFE, + OPT_BOOTSTRAP, OPT_SKIP_SHOW_DB, + OPT_TABLE_TYPE, OPT_INIT_FILE, + OPT_DELAY_KEY_WRITE_ALL, OPT_SLOW_QUERY_LOG, + OPT_DELAY_KEY_WRITE, OPT_CHARSETS_DIR, + OPT_BDB_HOME, OPT_BDB_LOG, + OPT_BDB_TMP, OPT_BDB_NOSYNC, + OPT_BDB_LOCK, OPT_BDB_SKIP, + OPT_BDB_NO_RECOVER, OPT_BDB_SHARED, + OPT_MASTER_HOST, OPT_MASTER_USER, + OPT_MASTER_PASSWORD, OPT_MASTER_PORT, + OPT_MASTER_INFO_FILE, OPT_MASTER_CONNECT_RETRY, + OPT_MASTER_RETRY_COUNT, + OPT_MASTER_SSL, OPT_MASTER_SSL_KEY, + OPT_MASTER_SSL_CERT, OPT_MASTER_SSL_CAPATH, + OPT_MASTER_SSL_CIPHER, + OPT_SQL_BIN_UPDATE_SAME, OPT_REPLICATE_DO_DB, + OPT_REPLICATE_IGNORE_DB, OPT_LOG_SLAVE_UPDATES, + OPT_BINLOG_DO_DB, OPT_BINLOG_IGNORE_DB, + OPT_WANT_CORE, OPT_CONCURRENT_INSERT, + OPT_MEMLOCK, OPT_MYISAM_RECOVER, + OPT_REPLICATE_REWRITE_DB, OPT_SERVER_ID, + OPT_SKIP_SLAVE_START, OPT_SKIP_INNOBASE, + OPT_SAFEMALLOC_MEM_LIMIT, OPT_REPLICATE_DO_TABLE, + OPT_REPLICATE_IGNORE_TABLE, OPT_REPLICATE_WILD_DO_TABLE, + OPT_REPLICATE_WILD_IGNORE_TABLE, + OPT_DISCONNECT_SLAVE_EVENT_COUNT, + OPT_ABORT_SLAVE_EVENT_COUNT, + OPT_INNODB_DATA_HOME_DIR, + OPT_INNODB_DATA_FILE_PATH, + OPT_INNODB_LOG_GROUP_HOME_DIR, + OPT_INNODB_LOG_ARCH_DIR, + OPT_INNODB_LOG_ARCHIVE, + OPT_INNODB_FLUSH_LOG_AT_TRX_COMMIT, + OPT_INNODB_FLUSH_METHOD, + OPT_INNODB_FAST_SHUTDOWN, + OPT_SAFE_SHOW_DB, + OPT_INNODB_SKIP, OPT_SKIP_SAFEMALLOC, + OPT_TEMP_POOL, OPT_TX_ISOLATION, + OPT_SKIP_STACK_TRACE, OPT_SKIP_SYMLINKS, + OPT_MAX_BINLOG_DUMP_EVENTS, OPT_SPORADIC_BINLOG_DUMP_FAIL, + OPT_SAFE_USER_CREATE, OPT_SQL_MODE, + OPT_HAVE_NAMED_PIPE, + OPT_DO_PSTACK, OPT_REPORT_HOST, + OPT_REPORT_USER, OPT_REPORT_PASSWORD, OPT_REPORT_PORT, + OPT_SHOW_SLAVE_AUTH_INFO, OPT_OLD_RPL_COMPAT, + OPT_SLAVE_LOAD_TMPDIR, OPT_NO_MIX_TYPE, + OPT_RPL_RECOVERY_RANK,OPT_INIT_RPL_ROLE, + OPT_RELAY_LOG, OPT_RELAY_LOG_INDEX, OPT_RELAY_LOG_INFO_FILE, + OPT_SLAVE_SKIP_ERRORS, OPT_DES_KEY_FILE, OPT_LOCAL_INFILE, + OPT_RECKLESS_SLAVE, + OPT_SSL_SSL, OPT_SSL_KEY, OPT_SSL_CERT, OPT_SSL_CA, + OPT_SSL_CAPATH, OPT_SSL_CIPHER, + OPT_BACK_LOG, OPT_BINLOG_CACHE_SIZE, + OPT_CONNECT_TIMEOUT, OPT_DELAYED_INSERT_TIMEOUT, + OPT_DELAYED_INSERT_LIMIT, OPT_DELAYED_QUEUE_SIZE, + OPT_FLUSH_TIME, OPT_FT_MIN_WORD_LEN, + OPT_FT_MAX_WORD_LEN, OPT_FT_MAX_WORD_LEN_FOR_SORT, OPT_FT_STOPWORD_FILE, + OPT_INTERACTIVE_TIMEOUT, OPT_JOIN_BUFF_SIZE, + OPT_KEY_BUFFER_SIZE, OPT_LONG_QUERY_TIME, + OPT_LOWER_CASE_TABLE_NAMES, OPT_MAX_ALLOWED_PACKET, + OPT_MAX_BINLOG_CACHE_SIZE, OPT_MAX_BINLOG_SIZE, + OPT_MAX_CONNECTIONS, OPT_MAX_CONNECT_ERRORS, + OPT_MAX_DELAYED_THREADS, OPT_MAX_HEP_TABLE_SIZE, + OPT_MAX_JOIN_SIZE, OPT_MAX_SORT_LENGTH, + OPT_MAX_TMP_TABLES, OPT_MAX_USER_CONNECTIONS, + OPT_MAX_WRITE_LOCK_COUNT, OPT_BULK_INSERT_BUFFER_SIZE, + OPT_MYISAM_BLOCK_SIZE, OPT_MYISAM_MAX_EXTRA_SORT_FILE_SIZE, + OPT_MYISAM_MAX_SORT_FILE_SIZE, OPT_MYISAM_SORT_BUFFER_SIZE, + OPT_NET_BUFFER_LENGTH, OPT_NET_RETRY_COUNT, + OPT_NET_READ_TIMEOUT, OPT_NET_WRITE_TIMEOUT, + OPT_OPEN_FILES_LIMIT, + OPT_QUERY_CACHE_LIMIT, OPT_QUERY_CACHE_SIZE, + OPT_QUERY_CACHE_TYPE, OPT_RECORD_BUFFER, + OPT_RECORD_RND_BUFFER, OPT_RELAY_LOG_SPACE_LIMIT, + OPT_SLAVE_NET_TIMEOUT, OPT_SLAVE_COMPRESSED_PROTOCOL, OPT_SLOW_LAUNCH_TIME, + OPT_SORT_BUFFER, OPT_TABLE_CACHE, + OPT_THREAD_CONCURRENCY, OPT_THREAD_CACHE_SIZE, + OPT_TMP_TABLE_SIZE, OPT_THREAD_STACK, + OPT_WAIT_TIMEOUT, + OPT_INNODB_MIRRORED_LOG_GROUPS, + OPT_INNODB_LOG_FILES_IN_GROUP, + OPT_INNODB_LOG_FILE_SIZE, + OPT_INNODB_LOG_BUFFER_SIZE, + OPT_INNODB_BUFFER_POOL_SIZE, + OPT_INNODB_ADDITIONAL_MEM_POOL_SIZE, + OPT_INNODB_FILE_IO_THREADS, + OPT_INNODB_LOCK_WAIT_TIMEOUT, + OPT_INNODB_THREAD_CONCURRENCY, + OPT_INNODB_FORCE_RECOVERY, + OPT_BDB_CACHE_SIZE, + OPT_BDB_LOG_BUFFER_SIZE, + OPT_BDB_MAX_LOCK, + OPT_ERROR_LOG_FILE }; -static struct option long_options[] = { - {"ansi", no_argument, 0, 'a'}, - {"basedir", required_argument, 0, 'b'}, + +#define LONG_TIMEOUT ((ulong) 3600L*24L*365L) + +struct my_option my_long_options[] = +{ + {"ansi", 'a', "Use ANSI SQL syntax instead of MySQL syntax", 0, 0, 0, + GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"basedir", 'b', + "Path to installation directory. All paths are usually resolved relative to this.", + (gptr*) &mysql_home_ptr, (gptr*) &mysql_home_ptr, 0, GET_STR, REQUIRED_ARG, + 0, 0, 0, 0, 0, 0}, #ifdef HAVE_BERKELEY_DB - {"bdb-home", required_argument, 0, (int) OPT_BDB_HOME}, - {"bdb-lock-detect", required_argument, 0, (int) OPT_BDB_LOCK}, - {"bdb-logdir", required_argument, 0, (int) OPT_BDB_LOG}, - {"bdb-no-recover", no_argument, 0, (int) OPT_BDB_NO_RECOVER}, - {"bdb-no-sync", no_argument, 0, (int) OPT_BDB_NOSYNC}, - {"bdb-shared-data", no_argument, 0, (int) OPT_BDB_SHARED}, - {"bdb-tmpdir", required_argument, 0, (int) OPT_BDB_TMP}, -#endif - {"big-tables", no_argument, 0, (int) OPT_BIG_TABLES}, - {"binlog-do-db", required_argument, 0, (int) OPT_BINLOG_DO_DB}, - {"binlog-ignore-db", required_argument, 0, (int) OPT_BINLOG_IGNORE_DB}, - {"bind-address", required_argument, 0, (int) OPT_BIND_ADDRESS}, - {"bootstrap", no_argument, 0, (int) OPT_BOOTSTRAP}, + {"bdb-home", OPT_BDB_HOME, "Berkeley home directory", (gptr*) &berkeley_home, + (gptr*) &berkeley_home, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"bdb-lock-detect", OPT_BDB_LOCK, + "Berkeley lock detect (DEFAULT, OLDEST, RANDOM or YOUNGEST, # sec)", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"bdb-logdir", OPT_BDB_LOG, "Berkeley DB log file directory", + (gptr*) &berkeley_logdir, (gptr*) &berkeley_logdir, 0, GET_STR, + REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"bdb-no-recover", OPT_BDB_NO_RECOVER, + "Don't try to recover Berkeley DB tables on start", 0, 0, 0, GET_NO_ARG, + NO_ARG, 0, 0, 0, 0, 0, 0}, + {"bdb-no-sync", OPT_BDB_NOSYNC, "Don't synchronously flush logs", 0, 0, 0, + GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"bdb-shared-data", OPT_BDB_SHARED, + "Start Berkeley DB in multi-process mode", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, + 0, 0, 0, 0, 0}, + {"bdb-tmpdir", OPT_BDB_TMP, "Berkeley DB tempfile name", + (gptr*) &berkeley_tmpdir, (gptr*) &berkeley_tmpdir, 0, GET_STR, + REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, +#endif /* HAVE_BERKELEY_DB */ + {"skip-bdb", OPT_BDB_SKIP, "Don't use berkeley db (will save memory)", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"big-tables", OPT_BIG_TABLES, + "Allow big result sets by saving all temporary sets on file (Solves most 'table full' errors)", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"binlog-do-db", OPT_BINLOG_DO_DB, + "Tells the master it should log updates for the specified database, and exclude all others not explicitly mentioned.", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"binlog-ignore-db", OPT_BINLOG_IGNORE_DB, + "Tells the master that updates to the given database should not be logged tothe binary log", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"bind-address", OPT_BIND_ADDRESS, "IP address to bind to", + (gptr*) &my_bind_addr_str, (gptr*) &my_bind_addr_str, 0, GET_STR, + REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"bootstrap", OPT_BOOTSTRAP, "Used by mysql installation scripts", 0, 0, 0, + GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"console", OPT_CONSOLE, "Write error output on screen; Don't remove the console window on windows", + (gptr*) &opt_console, (gptr*) &opt_console, 0, GET_BOOL, NO_ARG, 0, 0, 0, + 0, 0, 0}, #ifdef __WIN__ - {"console", no_argument, 0, (int) OPT_CONSOLE}, -#endif - {"core-file", no_argument, 0, (int) OPT_WANT_CORE}, - {"chroot", required_argument, 0, 'r'}, - {"character-sets-dir", required_argument, 0, (int) OPT_CHARSETS_DIR}, - {"datadir", required_argument, 0, 'h'}, - {"debug", optional_argument, 0, '#'}, - {"default-character-set", required_argument, 0, 'C'}, - {"default-table-type", required_argument, 0, (int) OPT_TABLE_TYPE}, - {"delay-key-write-for-all-tables", - no_argument, 0, (int) OPT_DELAY_KEY_WRITE}, - {"enable-locking", no_argument, 0, (int) OPT_ENABLE_LOCK}, - {"enable-named-pipe", no_argument, 0, (int) OPT_HAVE_NAMED_PIPE}, - {"exit-info", optional_argument, 0, 'T'}, - {"flush", no_argument, 0, (int) OPT_FLUSH}, -#ifdef HAVE_GEMINI_DB - {"gemini-flush-log-at-commit",no_argument, 0, (int) OPT_GEMINI_FLUSH_LOG}, - {"gemini-recovery", required_argument, 0, (int) OPT_GEMINI_RECOVER}, - {"gemini-unbuffered-io", no_argument, 0, (int) OPT_GEMINI_UNBUFFERED_IO}, -#endif - /* We must always support this option to make scripts like mysqltest easier - to do */ - {"innodb_data_file_path", required_argument, 0, - OPT_INNODB_DATA_FILE_PATH}, + {"standalone", OPT_STANDALONE, + "Dummy option to start as a standalone program (NT)", 0, 0, 0, GET_NO_ARG, + NO_ARG, 0, 0, 0, 0, 0, 0}, +#endif + {"core-file", OPT_WANT_CORE, "Write core on errors", 0, 0, 0, GET_NO_ARG, + NO_ARG, 0, 0, 0, 0, 0, 0}, + {"chroot", 'r', "Chroot mysqld daemon during startup.", + (gptr*) &mysqld_chroot, (gptr*) &mysqld_chroot, 0, GET_STR, REQUIRED_ARG, + 0, 0, 0, 0, 0, 0}, + {"character-sets-dir", OPT_CHARSETS_DIR, + "Directory where character sets are", (gptr*) &charsets_dir, + (gptr*) &charsets_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"datadir", 'h', "Path to the database root", (gptr*) &mysql_data_home, + (gptr*) &mysql_data_home, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, +#ifndef DBUG_OFF + {"debug", '#', "Debug log.", (gptr*) &default_dbug_option, + (gptr*) &default_dbug_option, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, +#ifdef SAFEMALLOC + {"skip-safemalloc", OPT_SKIP_SAFEMALLOC, + "Don't use the memory allocation checking", 0, 0, 0, GET_NO_ARG, NO_ARG, + 0, 0, 0, 0, 0, 0}, +#endif +#endif +#ifdef HAVE_OPENSSL + {"des-key-file", OPT_DES_KEY_FILE, + "Load keys for des_encrypt() and des_encrypt from given file", + (gptr*) &des_key_file, (gptr*) &des_key_file, 0, GET_STR, REQUIRED_ARG, + 0, 0, 0, 0, 0, 0}, +#endif /* HAVE_OPENSSL */ + {"default-character-set", 'C', "Set the default character set", + (gptr*) &sys_charset.value, (gptr*) &sys_charset.value, 0, GET_STR, + REQUIRED_ARG, 0, 0, 0, 0, 0, 0 }, + {"default-table-type", OPT_TABLE_TYPE, + "Set the default table type for tables", 0, 0, + 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"delay-key-write", OPT_DELAY_KEY_WRITE, "Type of DELAY_KEY_WRITE", + 0,0,0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, + {"delay-key-write-for-all-tables", OPT_DELAY_KEY_WRITE_ALL, + "Don't flush key buffers between writes for any MyISAM table (Deprecated option, use --delay-key-write=all instead)", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"enable-locking", OPT_ENABLE_LOCK, + "Deprecated option, use --external-locking instead", + (gptr*) &opt_external_locking, (gptr*) &opt_external_locking, + 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, +#ifdef __NT__ + {"enable-named-pipe", OPT_HAVE_NAMED_PIPE, "Enable the named pipe (NT)", + (gptr*) &opt_enable_named_pipe, (gptr*) &opt_enable_named_pipe, 0, GET_BOOL, + NO_ARG, 0, 0, 0, 0, 0, 0}, +#endif + {"enable-pstack", OPT_DO_PSTACK, "Print a symbolic stack trace on failure", + (gptr*) &opt_do_pstack, (gptr*) &opt_do_pstack, 0, GET_BOOL, NO_ARG, 0, 0, + 0, 0, 0, 0}, + {"exit-info", 'T', "Used for debugging; Use at your own risk!", 0, 0, 0, + GET_LONG, OPT_ARG, 0, 0, 0, 0, 0, 0}, + {"flush", OPT_FLUSH, "Flush tables to disk between SQL commands", 0, 0, 0, + GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + /* We must always support the next option to make scripts like mysqltest + easier to do */ + {"init-rpl-role", OPT_INIT_RPL_ROLE, "Set the replication role", 0, 0, 0, + GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"innodb_data_file_path", OPT_INNODB_DATA_FILE_PATH, + "Path to individual files and their sizes", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, #ifdef HAVE_INNOBASE_DB - {"innodb_data_home_dir", required_argument, 0, - OPT_INNODB_DATA_HOME_DIR}, - {"innodb_log_group_home_dir", required_argument, 0, - OPT_INNODB_LOG_GROUP_HOME_DIR}, - {"innodb_log_arch_dir", required_argument, 0, - OPT_INNODB_LOG_ARCH_DIR}, - {"innodb_log_archive", optional_argument, 0, - OPT_INNODB_LOG_ARCHIVE}, - {"innodb_flush_log_at_trx_commit", optional_argument, 0, - OPT_INNODB_FLUSH_LOG_AT_TRX_COMMIT}, - {"innodb_fast_shutdown", optional_argument, 0, - OPT_INNODB_FAST_SHUTDOWN}, - {"innodb_flush_method", required_argument, 0, - OPT_INNODB_UNIX_FILE_FLUSH_METHOD}, -#endif - {"help", no_argument, 0, '?'}, - {"init-file", required_argument, 0, (int) OPT_INIT_FILE}, - {"log", optional_argument, 0, 'l'}, - {"language", required_argument, 0, 'L'}, - {"local-infile", optional_argument, 0, (int) OPT_LOCAL_INFILE}, - {"log-bin", optional_argument, 0, (int) OPT_BIN_LOG}, - {"log-bin-index", required_argument, 0, (int) OPT_BIN_LOG_INDEX}, - {"log-isam", optional_argument, 0, (int) OPT_ISAM_LOG}, - {"log-update", optional_argument, 0, (int) OPT_UPDATE_LOG}, - {"log-slow-queries", optional_argument, 0, (int) OPT_SLOW_QUERY_LOG}, - {"log-long-format", no_argument, 0, (int) OPT_LONG_FORMAT}, - {"log-slave-updates", no_argument, 0, (int) OPT_LOG_SLAVE_UPDATES}, - {"low-priority-updates", no_argument, 0, (int) OPT_LOW_PRIORITY_UPDATES}, - {"master-host", required_argument, 0, (int) OPT_MASTER_HOST}, - {"master-user", required_argument, 0, (int) OPT_MASTER_USER}, - {"master-password", required_argument, 0, (int) OPT_MASTER_PASSWORD}, - {"master-port", required_argument, 0, (int) OPT_MASTER_PORT}, - {"master-connect-retry", required_argument, 0, (int) OPT_MASTER_CONNECT_RETRY}, - {"master-retry-count", required_argument, 0, (int) OPT_MASTER_RETRY_COUNT}, - {"master-info-file", required_argument, 0, (int) OPT_MASTER_INFO_FILE}, - {"myisam-recover", optional_argument, 0, (int) OPT_MYISAM_RECOVER}, - {"memlock", no_argument, 0, (int) OPT_MEMLOCK}, - // needs to be available for the test case to pass in non-debugging mode - // is a no-op - {"disconnect-slave-event-count", required_argument, 0, - (int) OPT_DISCONNECT_SLAVE_EVENT_COUNT}, - {"abort-slave-event-count", required_argument, 0, - (int) OPT_ABORT_SLAVE_EVENT_COUNT}, - {"max-binlog-dump-events", required_argument, 0, - (int) OPT_MAX_BINLOG_DUMP_EVENTS}, - {"sporadic-binlog-dump-fail", no_argument, 0, - (int) OPT_SPORADIC_BINLOG_DUMP_FAIL}, - {"safemalloc-mem-limit", required_argument, 0, (int) - OPT_SAFEMALLOC_MEM_LIMIT}, - {"new", no_argument, 0, 'n'}, - {"old-protocol", no_argument, 0, 'o'}, + {"innodb_data_home_dir", OPT_INNODB_DATA_HOME_DIR, + "The common part for Innodb table spaces", (gptr*) &innobase_data_home_dir, + (gptr*) &innobase_data_home_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, + 0}, + {"innodb_log_group_home_dir", OPT_INNODB_LOG_GROUP_HOME_DIR, + "Path to innodb log files.", (gptr*) &innobase_log_group_home_dir, + (gptr*) &innobase_log_group_home_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, + 0, 0}, + {"innodb_log_arch_dir", OPT_INNODB_LOG_ARCH_DIR, + "Where full logs should be archived", (gptr*) &innobase_log_arch_dir, + (gptr*) &innobase_log_arch_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"innodb_log_archive", OPT_INNODB_LOG_ARCHIVE, + "Set to 1 if you want to have logs archived", 0, 0, 0, GET_LONG, OPT_ARG, + 0, 0, 0, 0, 0, 0}, + {"innodb_flush_log_at_trx_commit", OPT_INNODB_FLUSH_LOG_AT_TRX_COMMIT, + "Set to 0 (write and flush once per second), 1 (write and flush at each commit) or 2 (write at commit, flush once per second)", + (gptr*) &innobase_flush_log_at_trx_commit, + (gptr*) &innobase_flush_log_at_trx_commit, + 0, GET_UINT, OPT_ARG, 0, 0, 2, 0, 0, 0}, + {"innodb_flush_method", OPT_INNODB_FLUSH_METHOD, + "With which method to flush data", (gptr*) &innobase_unix_file_flush_method, + (gptr*) &innobase_unix_file_flush_method, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, + 0, 0, 0}, + {"innodb_fast_shutdown", OPT_INNODB_FAST_SHUTDOWN, + "Speeds up server shutdown process", (gptr*) &innobase_fast_shutdown, + (gptr*) &innobase_fast_shutdown, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, +#endif /* End HAVE_INNOBASE_DB */ + {"help", '?', "Display this help and exit", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, + 0, 0, 0, 0, 0}, + {"init-file", OPT_INIT_FILE, "Read SQL commands from this file at startup", + (gptr*) &opt_init_file, (gptr*) &opt_init_file, 0, GET_STR, REQUIRED_ARG, + 0, 0, 0, 0, 0, 0}, + {"log", 'l', "Log connections and queries to file", (gptr*) &opt_logname, + (gptr*) &opt_logname, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, + {"language", 'L', + "Client error messages in given language. May be given as a full path", + (gptr*) &language_ptr, (gptr*) &language_ptr, 0, GET_STR, REQUIRED_ARG, + 0, 0, 0, 0, 0, 0}, + {"local-infile", OPT_LOCAL_INFILE, + "Enable/disable LOAD DATA LOCAL INFILE (takes values 1|0)", + (gptr*) &opt_local_infile, + (gptr*) &opt_local_infile, 0, GET_BOOL, OPT_ARG, + 1, 0, 0, 0, 0, 0}, + {"log-bin", OPT_BIN_LOG, + "Log queries in new binary format (for replication)", + (gptr*) &opt_bin_logname, (gptr*) &opt_bin_logname, 0, GET_STR_ALLOC, + OPT_ARG, 0, 0, 0, 0, 0, 0}, + {"log-bin-index", OPT_BIN_LOG_INDEX, + "File that holds the names for last binary log files", + (gptr*) &opt_binlog_index_name, (gptr*) &opt_binlog_index_name, 0, GET_STR, + REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"log-isam", OPT_ISAM_LOG, "Log all MyISAM changes to file", + (gptr*) &myisam_log_filename, (gptr*) &myisam_log_filename, 0, GET_STR, + OPT_ARG, 0, 0, 0, 0, 0, 0}, + {"log-update", OPT_UPDATE_LOG, + "Log updates to file.# where # is a unique number if not given.", + (gptr*) &opt_update_logname, (gptr*) &opt_update_logname, 0, GET_STR, + OPT_ARG, 0, 0, 0, 0, 0, 0}, + {"log-slow-queries", OPT_SLOW_QUERY_LOG, + "Log slow queries to this log file. Defaults logging to hostname-slow.log", + (gptr*) &opt_slow_logname, (gptr*) &opt_slow_logname, 0, GET_STR, OPT_ARG, + 0, 0, 0, 0, 0, 0}, + {"log-long-format", OPT_LONG_FORMAT, + "Log some extra information to update log", 0, 0, 0, GET_NO_ARG, NO_ARG, + 0, 0, 0, 0, 0, 0}, + {"log-slave-updates", OPT_LOG_SLAVE_UPDATES, + "Tells the slave to log the updates from the slave thread to the binary log. You will need to turn it on if you plan to daisy-chain the slaves.", + (gptr*) &opt_log_slave_updates, (gptr*) &opt_log_slave_updates, 0, GET_BOOL, + NO_ARG, 0, 0, 0, 0, 0, 0}, + {"low-priority-updates", OPT_LOW_PRIORITY_UPDATES, + "INSERT/DELETE/UPDATE has lower priority than selects", + (gptr*) &global_system_variables.low_priority_updates, + (gptr*) &max_system_variables.low_priority_updates, + 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"master-host", OPT_MASTER_HOST, + "Master hostname or IP address for replication. If not set, the slave thread will not be started. Note that the setting of master-host will be ignored if there exists a valid master.info file.", + (gptr*) &master_host, (gptr*) &master_host, 0, GET_STR, REQUIRED_ARG, 0, 0, + 0, 0, 0, 0}, + {"master-user", OPT_MASTER_USER, + "The username the slave thread will use for authentication when connecting to the master. The user must have FILE privilege. If the master user is not set, user test is assumed. The value in master.info will take precedence if it can be read.", + (gptr*) &master_user, (gptr*) &master_user, 0, GET_STR, REQUIRED_ARG, 0, 0, + 0, 0, 0, 0}, + {"master-password", OPT_MASTER_PASSWORD, + "The password the slave thread will authenticate with when connecting to the master. If not set, an empty password is assumed.The value in master.info will take precedence if it can be read.", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"master-port", OPT_MASTER_PORT, + "The port the master is listening on. If not set, the compiled setting of MYSQL_PORT is assumed. If you have not tinkered with configure options, this should be 3306. The value in master.info will take precedence if it can be read", + (gptr*) &master_port, (gptr*) &master_port, 0, GET_UINT, REQUIRED_ARG, + MYSQL_PORT, 0, 0, 0, 0, 0}, + {"master-connect-retry", OPT_MASTER_CONNECT_RETRY, + "The number of seconds the slave thread will sleep before retrying to connect to the master in case the master goes down or the connection is lost.", + (gptr*) &master_connect_retry, (gptr*) &master_connect_retry, 0, GET_UINT, + REQUIRED_ARG, 60, 0, 0, 0, 0, 0}, + {"master-retry-count", OPT_MASTER_RETRY_COUNT, + "The number of tries the slave will make to connect to the master before giving up.", + (gptr*) &master_retry_count, (gptr*) &master_retry_count, 0, GET_ULONG, + REQUIRED_ARG, 3600*24, 0, 0, 0, 0, 0}, + {"master-info-file", OPT_MASTER_INFO_FILE, + "The location of the file that remembers where we left off on the master during the replication process. The default is `master.info' in the data directory. You should not need to change this.", + (gptr*) &master_info_file, (gptr*) &master_info_file, 0, GET_STR, + REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"master-ssl", OPT_MASTER_SSL, + "Turn SSL on for replication. Be warned that is this is a relatively new feature.", + (gptr*) &master_ssl, (gptr*) &master_ssl, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, + 0, 0}, + {"master-ssl-key", OPT_MASTER_SSL_KEY, + "Master SSL keyfile name. Only applies if you have enabled master-ssl.", + (gptr*) &master_ssl_key, (gptr*) &master_ssl_key, 0, GET_STR, OPT_ARG, + 0, 0, 0, 0, 0, 0}, + {"master-ssl-cert", OPT_MASTER_SSL_CERT, + "Master SSL certificate file name. Only applies if you have enabled master-ssl.", + (gptr*) &master_ssl_cert, (gptr*) &master_ssl_cert, 0, GET_STR, OPT_ARG, + 0, 0, 0, 0, 0, 0}, + {"master-ssl-capath", OPT_MASTER_SSL_CAPATH, + "Master SSL CA path. Only applies if you have enabled master-ssl.", + (gptr*) &master_ssl_capath, (gptr*) &master_ssl_capath, 0, GET_STR, OPT_ARG, + 0, 0, 0, 0, 0, 0}, + {"master-ssl-cipher", OPT_MASTER_SSL_CIPHER, + "Master SSL cipher. Only applies if you have enabled master-ssl.", + (gptr*) &master_ssl_cipher, (gptr*) &master_ssl_capath, 0, GET_STR, OPT_ARG, + 0, 0, 0, 0, 0, 0}, + {"myisam-recover", OPT_MYISAM_RECOVER, + "Syntax: myisam-recover[=option[,option...]], where option can be DEFAULT, BACKUP or FORCE.", + (gptr*) &myisam_recover_options_str, (gptr*) &myisam_recover_options_str, 0, + GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0}, + {"memlock", OPT_MEMLOCK, "Lock mysqld in memory", (gptr*) &locked_in_memory, + (gptr*) &locked_in_memory, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"disconnect-slave-event-count", OPT_DISCONNECT_SLAVE_EVENT_COUNT, + "Option used by mysql-test for debugging and testing of replication", + (gptr*) &disconnect_slave_event_count, + (gptr*) &disconnect_slave_event_count, 0, GET_INT, REQUIRED_ARG, 0, 0, 0, + 0, 0, 0}, + {"abort-slave-event-count", OPT_ABORT_SLAVE_EVENT_COUNT, + "Option used by mysql-test for debugging and testing of replication", + (gptr*) &abort_slave_event_count, (gptr*) &abort_slave_event_count, + 0, GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"max-binlog-dump-events", OPT_MAX_BINLOG_DUMP_EVENTS, + "Option used by mysql-test for debugging and testing of replication", + (gptr*) &max_binlog_dump_events, (gptr*) &max_binlog_dump_events, 0, + GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"sporadic-binlog-dump-fail", OPT_SPORADIC_BINLOG_DUMP_FAIL, + "Option used by mysql-test for debugging and testing of replication", + (gptr*) &opt_sporadic_binlog_dump_fail, + (gptr*) &opt_sporadic_binlog_dump_fail, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, + 0}, + {"safemalloc-mem-limit", OPT_SAFEMALLOC_MEM_LIMIT, + "Simulate memory shortage when compiled with the --with-debug=full option", + 0, 0, 0, GET_ULL, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"new", 'n', "Use very new possible 'unsafe' functions", 0, 0, 0, GET_NO_ARG, + NO_ARG, 0, 0, 0, 0, 0, 0}, +#ifdef NOT_YET + {"no-mix-table-types", OPT_NO_MIX_TYPE, "Don't allow commands with uses two different table types", + (gptr*) &opt_no_mix_types, (gptr*) &opt_no_mix_types, 0, GET_BOOL, NO_ARG, + 0, 0, 0, 0, 0, 0}, +#endif + {"old-protocol", 'o', "Use the old (3.20) protocol client/server protocol", + (gptr*) &protocol_version, (gptr*) &protocol_version, 0, GET_UINT, NO_ARG, + PROTOCOL_VERSION, 0, 0, 0, 0, 0}, + {"old-rpl-compat", OPT_OLD_RPL_COMPAT, + "Use old LOAD DATA format in the binary log (don't save data in file)", + (gptr*) &opt_old_rpl_compat, (gptr*) &opt_old_rpl_compat, 0, GET_BOOL, + NO_ARG, 0, 0, 0, 0, 0, 0}, #ifdef ONE_THREAD - {"one-thread", no_argument, 0, (int) OPT_ONE_THREAD}, -#endif - {"pid-file", required_argument, 0, (int) OPT_PID_FILE}, - {"port", required_argument, 0, 'P'}, - {"replicate-do-db", required_argument, 0, (int) OPT_REPLICATE_DO_DB}, - {"replicate-do-table", required_argument, 0, - (int) OPT_REPLICATE_DO_TABLE}, - {"replicate-wild-do-table", required_argument, 0, - (int) OPT_REPLICATE_WILD_DO_TABLE}, - {"replicate-ignore-db", required_argument, 0, - (int) OPT_REPLICATE_IGNORE_DB}, - {"replicate-ignore-table", required_argument, 0, - (int) OPT_REPLICATE_IGNORE_TABLE}, - {"replicate-wild-ignore-table", required_argument, 0, - (int) OPT_REPLICATE_WILD_IGNORE_TABLE}, - {"replicate-rewrite-db", required_argument, 0, - (int) OPT_REPLICATE_REWRITE_DB}, - {"safe-mode", no_argument, 0, (int) OPT_SAFE}, - {"safe-show-database", no_argument, 0, (int) OPT_SAFE_SHOW_DB}, - {"safe-user-create", no_argument, 0, (int) OPT_SAFE_USER_CREATE}, - {"server-id", required_argument, 0, (int) OPT_SERVER_ID}, - {"set-variable", required_argument, 0, 'O'}, - {"skip-bdb", no_argument, 0, (int) OPT_BDB_SKIP}, - {"skip-innodb", no_argument, 0, (int) OPT_INNODB_SKIP}, - {"skip-gemini", no_argument, 0, (int) OPT_GEMINI_SKIP}, - {"skip-concurrent-insert", no_argument, 0, (int) OPT_SKIP_CONCURRENT_INSERT}, - {"skip-delay-key-write", no_argument, 0, (int) OPT_SKIP_DELAY_KEY_WRITE}, - {"skip-grant-tables", no_argument, 0, (int) OPT_SKIP_GRANT}, - {"skip-locking", no_argument, 0, (int) OPT_SKIP_LOCK}, - {"skip-host-cache", no_argument, 0, (int) OPT_SKIP_HOST_CACHE}, - {"skip-name-resolve", no_argument, 0, (int) OPT_SKIP_RESOLVE}, - {"skip-networking", no_argument, 0, (int) OPT_SKIP_NETWORKING}, - {"skip-new", no_argument, 0, (int) OPT_SKIP_NEW}, - {"skip-safemalloc", no_argument, 0, (int) OPT_SKIP_SAFEMALLOC}, - {"skip-show-database", no_argument, 0, (int) OPT_SKIP_SHOW_DB}, - {"skip-slave-start", no_argument, 0, (int) OPT_SKIP_SLAVE_START}, - {"skip-stack-trace", no_argument, 0, (int) OPT_SKIP_STACK_TRACE}, - {"skip-symlink", no_argument, 0, (int) OPT_SKIP_SYMLINKS}, - {"skip-thread-priority", no_argument, 0, (int) OPT_SKIP_PRIOR}, - {"slave-skip-errors", required_argument,0, - (int) OPT_SLAVE_SKIP_ERRORS}, - {"socket", required_argument, 0, (int) OPT_SOCKET}, - {"sql-bin-update-same", no_argument, 0, (int) OPT_SQL_BIN_UPDATE_SAME}, - {"sql-mode", required_argument, 0, (int) OPT_SQL_MODE}, + {"one-thread", OPT_ONE_THREAD, + "Only use one thread (for debugging under Linux)", 0, 0, 0, GET_NO_ARG, + NO_ARG, 0, 0, 0, 0, 0, 0}, +#endif + {"pid-file", OPT_PID_FILE, "Pid file used by safe_mysqld", + (gptr*) &pidfile_name_ptr, (gptr*) &pidfile_name_ptr, 0, GET_STR, + REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"log-error", OPT_ERROR_LOG_FILE, "Log error file", + (gptr*) &log_error_file_ptr, (gptr*) &log_error_file_ptr, 0, GET_STR, + OPT_ARG, 0, 0, 0, 0, 0, 0}, + {"port", 'P', "Port number to use for connection.", (gptr*) &mysql_port, + (gptr*) &mysql_port, 0, GET_UINT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"reckless-slave", OPT_RECKLESS_SLAVE, "For debugging", 0, 0, 0, GET_NO_ARG, + NO_ARG, 0, 0, 0, 0, 0, 0}, + {"replicate-do-db", OPT_REPLICATE_DO_DB, + "Tells the slave thread to restrict replication to the specified database. To specify more than one database, use the directive multiple times, once for each database. Note that this will only work if you do not use cross-database queries such as UPDATE some_db.some_table SET foo='bar' while having selected a different or no database. If you need cross database updates to work, make sure you have 3.23.28 or later, and use replicate-wild-do-table=db_name.%.", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"replicate-do-table", OPT_REPLICATE_DO_TABLE, + "Tells the slave thread to restrict replication to the specified table. To specify more than one table, use the directive multiple times, once for each table. This will work for cross-database updates, in contrast to replicate-do-db.", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"replicate-wild-do-table", OPT_REPLICATE_WILD_DO_TABLE, + "Tells the slave thread to restrict replication to the tables that match the specified wildcard pattern. To specify more than one table, use the directive multiple times, once for each table. This will work for cross-database updates. Example: replicate-wild-do-table=foo%.bar% will replicate only updates to tables in all databases that start with foo and whose table names start with bar", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"replicate-ignore-db", OPT_REPLICATE_IGNORE_DB, + "Tells the slave thread to not replicate to the specified database. To specify more than one database to ignore, use the directive multiple times, once for each database. This option will not work if you use cross database updates. If you need cross database updates to work, make sure you have 3.23.28 or later, and use replicate-wild-ignore-table=db_name.%. ", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"replicate-ignore-table", OPT_REPLICATE_IGNORE_TABLE, + "Tells the slave thread to not replicate to the specified table. To specify more than one table to ignore, use the directive multiple times, once for each table. This will work for cross-datbase updates, in contrast to replicate-ignore-db.", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"replicate-wild-ignore-table", OPT_REPLICATE_WILD_IGNORE_TABLE, + "Tells the slave thread to not replicate to the tables that match the given wildcard pattern. To specify more than one table to ignore, use the directive multiple times, once for each table. This will work for cross-database updates. Example: replicate-wild-ignore-table=foo%.bar% will not do updates to tables in databases that start with foo and whose table names start with bar.", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"replicate-rewrite-db", OPT_REPLICATE_REWRITE_DB, + "Updates to a database with a different name than the original. Example: replicate-rewrite-db=master_db_name->slave_db_name", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + // In replication, we may need to tell the other servers how to connect + {"report-host", OPT_REPORT_HOST, + "Hostname or IP of the slave to be reported to to the master during slave registration. Will appear in the output of SHOW SLAVE HOSTS. Leave unset if you do not want the slave to register itself with the master. Note that it is not sufficient for the master to simply read the IP of the slave off the socket once the slave connects. Due to NAT and other routing issues, that IP may not be valid for connecting to the slave from the master or other hosts.", + (gptr*) &report_host, (gptr*) &report_host, 0, GET_STR, REQUIRED_ARG, 0, 0, + 0, 0, 0, 0}, + {"report-user", OPT_REPORT_USER, "Undocumented", (gptr*) &report_user, + (gptr*) &report_user, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"report-password", OPT_REPORT_PASSWORD, "Undocumented", + (gptr*) &report_password, (gptr*) &report_password, 0, GET_STR, + REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"report-port", OPT_REPORT_PORT, + "Port for connecting to slave reported to the master during slave registration. Set it only if the slave is listening on a non-default port or if you have a special tunnel from the master or other clients to the slave. If not sure, leave this option unset.", + (gptr*) &report_port, (gptr*) &report_port, 0, GET_UINT, REQUIRED_ARG, + MYSQL_PORT, 0, 0, 0, 0, 0}, + {"rpl-recovery-rank", OPT_RPL_RECOVERY_RANK, "Undocumented", + (gptr*) &rpl_recovery_rank, (gptr*) &rpl_recovery_rank, 0, GET_ULONG, + REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"relay-log", OPT_RELAY_LOG, "Undocumented", + (gptr*) &opt_relay_logname, (gptr*) &opt_relay_logname, 0, + GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"relay-log-index", OPT_RELAY_LOG_INDEX, "Undocumented", + (gptr*) &opt_relaylog_index_name, (gptr*) &opt_relaylog_index_name, 0, + GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"safe-mode", OPT_SAFE, "Skip some optimize stages (for testing).", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, +#ifndef TO_BE_DELETED + {"safe-show-database", OPT_SAFE_SHOW_DB, + "Deprecated option; One should use GRANT SHOW DATABASES instead...", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, +#endif + {"safe-user-create", OPT_SAFE_USER_CREATE, + "Don't allow new user creation by the user who has no write privileges to the mysql.user table", + (gptr*) &opt_safe_user_create, (gptr*) &opt_safe_user_create, 0, GET_BOOL, + NO_ARG, 0, 0, 0, 0, 0, 0}, + {"server-id", OPT_SERVER_ID, + "Uniquely identifies the server instance in the community of replication partners", + (gptr*) &server_id, (gptr*) &server_id, 0, GET_ULONG, REQUIRED_ARG, 0, 0, 0, + 0, 0, 0}, + {"set-variable", 'O', + "Change the value of a variable. Please note that this option is deprecated;you can set variables directly with --variable-name=value.", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"show-slave-auth-info", OPT_SHOW_SLAVE_AUTH_INFO, + "Show user and password in SHOW SLAVE STATUS", + (gptr*) &opt_show_slave_auth_info, (gptr*) &opt_show_slave_auth_info, 0, + GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"concurrent-insert", OPT_CONCURRENT_INSERT, + "Use concurrent insert with MyISAM. Disable with prefix --skip-", + (gptr*) &myisam_concurrent_insert, (gptr*) &myisam_concurrent_insert, + 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, + {"skip-grant-tables", OPT_SKIP_GRANT, + "Start without grant tables. This gives all users FULL ACCESS to all tables!", + (gptr*) &opt_noacl, (gptr*) &opt_noacl, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, + 0}, + {"skip-innodb", OPT_INNODB_SKIP, "Don't use Innodb (will save memory)", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"skip-locking", OPT_SKIP_LOCK, + "Deprecated option, use --skip-external-locking instead", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"skip-host-cache", OPT_SKIP_HOST_CACHE, "Don't cache host names", 0, 0, 0, + GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"skip-name-resolve", OPT_SKIP_RESOLVE, + "Don't resolve hostnames. All hostnames are IP's or 'localhost'", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"skip-networking", OPT_SKIP_NETWORKING, + "Don't allow connection with TCP/IP.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, + 0, 0, 0}, + {"skip-new", OPT_SKIP_NEW, "Don't use new, possible wrong routines.", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"skip-show-database", OPT_SKIP_SHOW_DB, + "Don't allow 'SHOW DATABASE' commands", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, + 0, 0, 0, 0}, + {"skip-slave-start", OPT_SKIP_SLAVE_START, + "If set, slave is not autostarted.", (gptr*) &opt_skip_slave_start, + (gptr*) &opt_skip_slave_start, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"skip-stack-trace", OPT_SKIP_STACK_TRACE, + "Don't print a stack trace on failure", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, + 0, 0, 0, 0}, + {"skip-symlink", OPT_SKIP_SYMLINKS, "Don't allow symlinking of tables", + 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"skip-thread-priority", OPT_SKIP_PRIOR, + "Don't give threads different priorities.", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, + 0, 0, 0, 0, 0}, + {"relay-log-info-file", OPT_RELAY_LOG_INFO_FILE, "Undocumented", + (gptr*) &relay_log_info_file, (gptr*) &relay_log_info_file, 0, GET_STR, + REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"slave-load-tmpdir", OPT_SLAVE_LOAD_TMPDIR, "Undocumented", + (gptr*) &slave_load_tmpdir, (gptr*) &slave_load_tmpdir, 0, GET_STR_ALLOC, + REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"slave-skip-errors", OPT_SLAVE_SKIP_ERRORS, + "Tells the slave thread to continue replication when a query returns an error from the provided list", + 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"socket", OPT_SOCKET, "Socket file to use for connection", + (gptr*) &mysql_unix_port, (gptr*) &mysql_unix_port, 0, GET_STR, + REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"sql-bin-update-same", OPT_SQL_BIN_UPDATE_SAME, + "If set, setting SQL_LOG_BIN to a value will automatically set SQL_LOG_UPDATE to the same value and vice versa.", + (gptr*) &opt_sql_bin_update, (gptr*) &opt_sql_bin_update, 0, GET_BOOL, + NO_ARG, 0, 0, 0, 0, 0, 0}, + {"sql-mode", OPT_SQL_MODE, + "Syntax: sql-mode=option[,option[,option...]] where option can be one of: REAL_AS_FLOAT, PIPES_AS_CONCAT, ANSI_QUOTES, IGNORE_SPACE, SERIALIZE, ONLY_FULL_GROUP_BY, NO_UNSIGNED_SUBTRACTION.", + (gptr*) &sql_mode_str, (gptr*) &sql_mode_str, 0, GET_STR, REQUIRED_ARG, 0, + 0, 0, 0, 0, 0}, +#ifdef HAVE_OPENSSL #include "sslopt-longopts.h" -#ifdef __WIN__ - {"standalone", no_argument, 0, (int) OPT_STANDALONE}, #endif - {"transaction-isolation", required_argument, 0, (int) OPT_TX_ISOLATION}, - {"temp-pool", no_argument, 0, (int) OPT_TEMP_POOL}, - {"tmpdir", required_argument, 0, 't'}, - {"use-locking", no_argument, 0, (int) OPT_USE_LOCKING}, + {"temp-pool", OPT_TEMP_POOL, + "Using this option will cause most temporary files created to use a small set of names, rather than a unique name for each new file.", + (gptr*) &use_temp_pool, (gptr*) &use_temp_pool, 0, GET_BOOL, NO_ARG, 1, + 0, 0, 0, 0, 0}, + {"tmpdir", 't', "Path for temporary files", (gptr*) &opt_mysql_tmpdir, + (gptr*) &opt_mysql_tmpdir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"transaction-isolation", OPT_TX_ISOLATION, + "Default transaction isolation level", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, + 0, 0, 0, 0, 0}, + {"external-locking", OPT_USE_LOCKING, "Use system (external) locking. With this option enabled you can run myisamchk to test (not repair) tables while the MySQL server is running", + (gptr*) &opt_external_locking, (gptr*) &opt_external_locking, + 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, #ifdef USE_SYMDIR - {"use-symbolic-links", no_argument, 0, 's'}, -#endif - {"user", required_argument, 0, 'u'}, - {"version", no_argument, 0, 'V'}, - {"warnings", no_argument, 0, 'W'}, - {0, 0, 0, 0} -}; - -#define LONG_TIMEOUT ((ulong) 3600L*24L*365L) - -CHANGEABLE_VAR changeable_vars[] = { - { "back_log", (long*) &back_log, - 50, 1, 65535, 0, 1 }, + {"use-symbolic-links", 's', "Enable symbolic link support", + (gptr*) &my_use_symdir, (gptr*) &my_use_symdir, 0, GET_BOOL, NO_ARG, + IF_PURIFY(0,1), 0, 0, 0, 0, 0}, +#endif + {"user", 'u', "Run mysqld daemon as user", (gptr*) &mysqld_user, + (gptr*) &mysqld_user, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"version", 'V', "Output version information and exit", 0, 0, 0, GET_NO_ARG, + NO_ARG, 0, 0, 0, 0, 0, 0}, + {"version", 'v', "Synonym for option -v", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, + 0, 0, 0, 0}, + {"log-warnings", 'W', "Log some not critical warnings to the log file", + (gptr*) &global_system_variables.log_warnings, + (gptr*) &max_system_variables.log_warnings, 0, GET_BOOL, NO_ARG, 0, 0, 0, + 0, 0, 0}, + {"warnings", 'W', "Deprecated ; Use --log-warnings instead", + (gptr*) &global_system_variables.log_warnings, + (gptr*) &max_system_variables.log_warnings, 0, GET_BOOL, NO_ARG, 0, 0, 0, + 0, 0, 0}, + { "back_log", OPT_BACK_LOG, + "The number of outstanding connection requests MySQL can have. This comes into play when the main MySQL thread gets very many connection requests in a very short time.", + (gptr*) &back_log, (gptr*) &back_log, 0, GET_ULONG, + REQUIRED_ARG, 50, 1, 65535, 0, 1, 0 }, #ifdef HAVE_BERKELEY_DB - { "bdb_cache_size", (long*) &berkeley_cache_size, - KEY_CACHE_SIZE, 20*1024, (long) ~0, 0, IO_SIZE }, - {"bdb_log_buffer_size", (long*) &berkeley_log_buffer_size, 0, 256*1024L, - ~0L, 0, 1024}, - { "bdb_max_lock", (long*) &berkeley_max_lock, - 10000, 0, (long) ~0, 0, 1 }, - /* QQ: The following should be removed soon! */ - { "bdb_lock_max", (long*) &berkeley_max_lock, - 10000, 0, (long) ~0, 0, 1 }, -#endif - { "binlog_cache_size", (long*) &binlog_cache_size, - 32*1024L, IO_SIZE, ~0L, 0, IO_SIZE }, - { "connect_timeout", (long*) &connect_timeout, - CONNECT_TIMEOUT, 2, LONG_TIMEOUT, 0, 1 }, - { "delayed_insert_timeout", (long*) &delayed_insert_timeout, - DELAYED_WAIT_TIMEOUT, 1, LONG_TIMEOUT, 0, 1 }, - { "delayed_insert_limit", (long*) &delayed_insert_limit, - DELAYED_LIMIT, 1, ~0L, 0, 1 }, - { "delayed_queue_size", (long*) &delayed_queue_size, - DELAYED_QUEUE_SIZE, 1, ~0L, 0, 1 }, - { "flush_time", (long*) &flush_time, - FLUSH_TIME, 0, LONG_TIMEOUT, 0, 1 }, -#ifdef HAVE_GEMINI_DB - { "gemini_buffer_cache", (long*) &gemini_buffer_cache, - 128 * 8192, 16, LONG_MAX, 0, 1 }, - { "gemini_connection_limit", (long*) &gemini_connection_limit, - 100, 10, LONG_MAX, 0, 1 }, - { "gemini_io_threads", (long*) &gemini_io_threads, - 2, 0, 256, 0, 1 }, - { "gemini_log_cluster_size", (long*) &gemini_log_cluster_size, - 256 * 1024, 16 * 1024, LONG_MAX, 0, 1 }, - { "gemini_lock_table_size", (long*) &gemini_locktablesize, - 4096, 1024, LONG_MAX, 0, 1 }, - { "gemini_lock_wait_timeout",(long*) &gemini_lock_wait_timeout, - 10, 1, LONG_MAX, 0, 1 }, - { "gemini_spin_retries", (long*) &gemini_spin_retries, - 1, 0, LONG_MAX, 0, 1 }, -#endif + { "bdb_cache_size", OPT_BDB_CACHE_SIZE, + "The buffer that is allocated to cache index and rows for BDB tables.", + (gptr*) &berkeley_cache_size, (gptr*) &berkeley_cache_size, 0, GET_ULONG, + REQUIRED_ARG, KEY_CACHE_SIZE, 20*1024, (long) ~0, 0, IO_SIZE, 0}, + {"bdb_log_buffer_size", OPT_BDB_LOG_BUFFER_SIZE, + "The buffer that is allocated to cache index and rows for BDB tables.", + (gptr*) &berkeley_log_buffer_size, (gptr*) &berkeley_log_buffer_size, 0, + GET_ULONG, REQUIRED_ARG, 0, 256*1024L, ~0L, 0, 1024, 0}, + {"bdb_max_lock", OPT_BDB_MAX_LOCK, + "The maximum number of locks you can have active on a BDB table.", + (gptr*) &berkeley_max_lock, (gptr*) &berkeley_max_lock, 0, GET_ULONG, + REQUIRED_ARG, 10000, 0, (long) ~0, 0, 1, 0}, + /* QQ: The following should be removed soon! */ + {"bdb_lock_max", OPT_BDB_MAX_LOCK, "Synonym for bdb_max_lock", + (gptr*) &berkeley_max_lock, (gptr*) &berkeley_max_lock, 0, GET_ULONG, + REQUIRED_ARG, 10000, 0, (long) ~0, 0, 1, 0}, +#endif /* HAVE_BERKELEY_DB */ + {"binlog_cache_size", OPT_BINLOG_CACHE_SIZE, + "The size of the cache to hold the SQL statements for the binary log during a transaction. If you often use big, multi-statement transactions you can increase this to get more performance.", + (gptr*) &binlog_cache_size, (gptr*) &binlog_cache_size, 0, GET_ULONG, + REQUIRED_ARG, 32*1024L, IO_SIZE, ~0L, 0, IO_SIZE, 0}, + {"connect_timeout", OPT_CONNECT_TIMEOUT, + "The number of seconds the mysqld server is waiting for a connect packet before responding with Bad handshake", + (gptr*) &connect_timeout, (gptr*) &connect_timeout, + 0, GET_ULONG, REQUIRED_ARG, CONNECT_TIMEOUT, 2, LONG_TIMEOUT, 0, 1, 0 }, + {"delayed_insert_timeout", OPT_DELAYED_INSERT_TIMEOUT, + "How long a INSERT DELAYED thread should wait for INSERT statements before terminating.", + (gptr*) &delayed_insert_timeout, (gptr*) &delayed_insert_timeout, 0, + GET_ULONG, REQUIRED_ARG, DELAYED_WAIT_TIMEOUT, 1, LONG_TIMEOUT, 0, 1, 0}, + {"delayed_insert_limit", OPT_DELAYED_INSERT_LIMIT, + "After inserting delayed_insert_limit rows, the INSERT DELAYED handler will check if there are any SELECT statements pending. If so, it allows these to execute before continuing.", + (gptr*) &delayed_insert_limit, (gptr*) &delayed_insert_limit, 0, GET_ULONG, + REQUIRED_ARG, DELAYED_LIMIT, 1, ~0L, 0, 1, 0}, + { "delayed_queue_size", OPT_DELAYED_QUEUE_SIZE, + "What size queue (in rows) should be allocated for handling INSERT DELAYED. If the queue becomes full, any client that does INSERT DELAYED will wait until there is room in the queue again.", + (gptr*) &delayed_queue_size, (gptr*) &delayed_queue_size, 0, GET_ULONG, + REQUIRED_ARG, DELAYED_QUEUE_SIZE, 1, ~0L, 0, 1, 0}, + { "flush_time", OPT_FLUSH_TIME, + "A dedicated thread is created to flush all tables at the given interval.", + (gptr*) &flush_time, (gptr*) &flush_time, 0, GET_ULONG, REQUIRED_ARG, + FLUSH_TIME, 0, LONG_TIMEOUT, 0, 1, 0}, + { "ft_min_word_len", OPT_FT_MIN_WORD_LEN, + "The minimum length of the word to be included in a FULLTEXT index. Note: FULLTEXT indexes must be rebuilt after changing this variable.", + (gptr*) &ft_min_word_len, (gptr*) &ft_min_word_len, 0, GET_ULONG, + REQUIRED_ARG, 4, 1, HA_FT_MAXLEN, 0, 1, 0}, + { "ft_max_word_len", OPT_FT_MAX_WORD_LEN, + "The maximum length of the word to be included in a FULLTEXT index. Note: FULLTEXT indexes must be rebuilt after changing this variable.", + (gptr*) &ft_max_word_len, (gptr*) &ft_max_word_len, 0, GET_ULONG, + REQUIRED_ARG, HA_FT_MAXLEN, 10, HA_FT_MAXLEN, 0, 1, 0}, + { "ft_max_word_len_for_sort", OPT_FT_MAX_WORD_LEN_FOR_SORT, + "The maximum length of the word for repair_by_sorting. Longer words are included the slow way. The lower this value, the more words will be put in one sort bucket.", + (gptr*) &ft_max_word_len_for_sort, (gptr*) &ft_max_word_len_for_sort, 0, GET_ULONG, + REQUIRED_ARG, 20, 4, HA_FT_MAXLEN, 0, 1, 0}, + { "ft_stopword_file", OPT_FT_STOPWORD_FILE, + "Use stopwords from this file instead of built-in list.", + (gptr*) &ft_stopword_file, (gptr*) &ft_stopword_file, 0, GET_STR, + REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, #ifdef HAVE_INNOBASE_DB - {"innodb_mirrored_log_groups", - (long*) &innobase_mirrored_log_groups, 1, 1, 10, 0, 1}, - {"innodb_log_files_in_group", - (long*) &innobase_log_files_in_group, 2, 2, 100, 0, 1}, - {"innodb_log_file_size", - (long*) &innobase_log_file_size, 5*1024*1024L, 1*1024*1024L, - ~0L, 0, 1024*1024L}, - {"innodb_log_buffer_size", - (long*) &innobase_log_buffer_size, 1024*1024L, 256*1024L, - ~0L, 0, 1024}, - {"innodb_buffer_pool_size", - (long*) &innobase_buffer_pool_size, 8*1024*1024L, 1024*1024L, - ~0L, 0, 1024*1024L}, - {"innodb_additional_mem_pool_size", - (long*) &innobase_additional_mem_pool_size, 1*1024*1024L, 512*1024L, - ~0L, 0, 1024}, - {"innodb_file_io_threads", - (long*) &innobase_file_io_threads, 4, 4, 64, 0, 1}, - {"innodb_lock_wait_timeout", - (long*) &innobase_lock_wait_timeout, 50, 1, - 1024 * 1024 * 1024, 0, 1}, - {"innodb_thread_concurrency", - (long*) &innobase_thread_concurrency, 8, 1, 1000, 0, 1}, - {"innodb_force_recovery", - (long*) &innobase_force_recovery, 0, 0, 6, 0, 1}, -#endif - { "interactive_timeout", (long*) &net_interactive_timeout, - NET_WAIT_TIMEOUT, 1, LONG_TIMEOUT, 0, 1 }, - { "join_buffer_size", (long*) &join_buff_size, - 128*1024L, IO_SIZE*2+MALLOC_OVERHEAD, ~0L, MALLOC_OVERHEAD, IO_SIZE }, - { "key_buffer_size", (long*) &keybuff_size, - KEY_CACHE_SIZE, MALLOC_OVERHEAD, (long) ~0, MALLOC_OVERHEAD, IO_SIZE }, - { "long_query_time", (long*) &long_query_time, - 10, 1, LONG_TIMEOUT, 0, 1 }, - { "lower_case_table_names", (long*) &lower_case_table_names, + {"innodb_mirrored_log_groups", OPT_INNODB_MIRRORED_LOG_GROUPS, + "Number of identical copies of log groups we keep for the database. Currently this should be set to 1.", + (gptr*) &innobase_mirrored_log_groups, + (gptr*) &innobase_mirrored_log_groups, 0, GET_LONG, REQUIRED_ARG, 1, 1, 10, + 0, 1, 0}, + {"innodb_log_files_in_group", OPT_INNODB_LOG_FILES_IN_GROUP, + "Number of log files in the log group. InnoDB writes to the files in a circular fashion. Value 3 is recommended here.", + (gptr*) &innobase_log_files_in_group, (gptr*) &innobase_log_files_in_group, + 0, GET_LONG, REQUIRED_ARG, 2, 2, 100, 0, 1, 0}, + {"innodb_log_file_size", OPT_INNODB_LOG_FILE_SIZE, + "Size of each log file in a log group in megabytes.", + (gptr*) &innobase_log_file_size, (gptr*) &innobase_log_file_size, 0, + GET_LONG, REQUIRED_ARG, 5*1024*1024L, 1*1024*1024L, ~0L, 0, 1024*1024L, 0}, + {"innodb_log_buffer_size", OPT_INNODB_LOG_BUFFER_SIZE, + "The size of the buffer which InnoDB uses to write log to the log files on disk.", + (gptr*) &innobase_log_buffer_size, (gptr*) &innobase_log_buffer_size, 0, + GET_LONG, REQUIRED_ARG, 1024*1024L, 256*1024L, ~0L, 0, 1024, 0}, + {"innodb_buffer_pool_size", OPT_INNODB_BUFFER_POOL_SIZE, + "The size of the memory buffer InnoDB uses to cache data and indexes of its tables.", + (gptr*) &innobase_buffer_pool_size, (gptr*) &innobase_buffer_pool_size, 0, + GET_LONG, REQUIRED_ARG, 8*1024*1024L, 1024*1024L, ~0L, 0, 1024*1024L, 0}, + {"innodb_additional_mem_pool_size", OPT_INNODB_ADDITIONAL_MEM_POOL_SIZE, + "Size of a memory pool InnoDB uses to store data dictionary information and other internal data structures.", + (gptr*) &innobase_additional_mem_pool_size, + (gptr*) &innobase_additional_mem_pool_size, 0, GET_LONG, REQUIRED_ARG, + 1*1024*1024L, 512*1024L, ~0L, 0, 1024, 0}, + {"innodb_file_io_threads", OPT_INNODB_FILE_IO_THREADS, + "Number of file I/O threads in InnoDB.", (gptr*) &innobase_file_io_threads, + (gptr*) &innobase_file_io_threads, 0, GET_LONG, REQUIRED_ARG, 4, 4, 64, 0, + 1, 0}, + {"innodb_lock_wait_timeout", OPT_INNODB_LOCK_WAIT_TIMEOUT, + "Timeout in seconds an InnoDB transaction may wait for a lock before being rolled back.", + (gptr*) &innobase_lock_wait_timeout, (gptr*) &innobase_lock_wait_timeout, + 0, GET_LONG, REQUIRED_ARG, 50, 1, 1024 * 1024 * 1024, 0, 1, 0}, + {"innodb_thread_concurrency", OPT_INNODB_THREAD_CONCURRENCY, + "Helps in performance tuning in heavily concurrent environments.", + (gptr*) &innobase_thread_concurrency, (gptr*) &innobase_thread_concurrency, + 0, GET_LONG, REQUIRED_ARG, 8, 1, 1000, 0, 1, 0}, + {"innodb_force_recovery", OPT_INNODB_FORCE_RECOVERY, + "Helps to save your data in case the disk image of the database becomes corrupt.", + (gptr*) &innobase_force_recovery, (gptr*) &innobase_force_recovery, 0, + GET_LONG, REQUIRED_ARG, 0, 0, 6, 0, 1, 0}, +#endif /* HAVE_INNOBASE_DB */ + {"interactive_timeout", OPT_INTERACTIVE_TIMEOUT, + "The number of seconds the server waits for activity on an interactive connection before closing it.", + (gptr*) &global_system_variables.net_interactive_timeout, + (gptr*) &max_system_variables.net_interactive_timeout, 0, + GET_ULONG, REQUIRED_ARG, NET_WAIT_TIMEOUT, 1, LONG_TIMEOUT, 0, 1, 0}, + {"join_buffer_size", OPT_JOIN_BUFF_SIZE, + "The size of the buffer that is used for full joins.", + (gptr*) &global_system_variables.join_buff_size, + (gptr*) &max_system_variables.join_buff_size, 0, GET_ULONG, + REQUIRED_ARG, 128*1024L, IO_SIZE*2+MALLOC_OVERHEAD, ~0L, MALLOC_OVERHEAD, + IO_SIZE, 0}, + {"key_buffer_size", OPT_KEY_BUFFER_SIZE, + "The size of the buffer used for index blocks. Increase this to get better index handling (for all reads and multiple writes) to as much as you can afford; 64M on a 256M machine that mainly runs MySQL is quite common.", + (gptr*) &keybuff_size, (gptr*) &keybuff_size, 0, GET_ULL, + REQUIRED_ARG, KEY_CACHE_SIZE, MALLOC_OVERHEAD, (long) ~0, MALLOC_OVERHEAD, + IO_SIZE, 0}, + {"long_query_time", OPT_LONG_QUERY_TIME, + "Log all queries that have taken more than long_query_time seconds to execute to file.", + (gptr*) &global_system_variables.long_query_time, + (gptr*) &max_system_variables.long_query_time, 0, GET_ULONG, + REQUIRED_ARG, 10, 1, LONG_TIMEOUT, 0, 1, 0}, + {"lower_case_table_names", OPT_LOWER_CASE_TABLE_NAMES, + "If set to 1 table names are stored in lowercase on disk and table names will be case-insensitive.", + (gptr*) &lower_case_table_names, + (gptr*) &lower_case_table_names, 0, GET_BOOL, NO_ARG, #ifdef FN_NO_CASE_SENCE 1 #else 0 #endif - ,0, 1, 0, 1 }, - { "max_allowed_packet", (long*) &max_allowed_packet, - 1024*1024L, 80, 16*1024*1024L, MALLOC_OVERHEAD, 1024 }, - { "max_binlog_cache_size", (long*) &max_binlog_cache_size, - ~0L, IO_SIZE, ~0L, 0, IO_SIZE }, - { "max_binlog_size", (long*) &max_binlog_size, - 1024*1024L*1024L, 1024, 1024*1024L*1024L, 0, 1 }, - { "max_connections", (long*) &max_connections, - 100, 1, 16384, 0, 1 }, - { "max_connect_errors", (long*) &max_connect_errors, - MAX_CONNECT_ERRORS, 1, ~0L, 0, 1 }, - { "max_delayed_threads", (long*) &max_insert_delayed_threads, - 20, 1, 16384, 0, 1 }, - { "max_heap_table_size", (long*) &max_heap_table_size, - 16*1024*1024L, 16384, ~0L, MALLOC_OVERHEAD, 1024 }, - { "max_join_size", (long*) &max_join_size, - ~0L, 1, ~0L, 0, 1 }, - { "max_sort_length", (long*) &max_item_sort_length, - 1024, 4, 8192*1024L, 0, 1 }, - { "max_tmp_tables", (long*) &max_tmp_tables, - 32, 1, ~0L, 0, 1 }, - { "max_user_connections", (long*) &max_user_connections, - 0, 1, ~0L, 0, 1 }, - { "max_write_lock_count", (long*) &max_write_lock_count, - ~0L, 1, ~0L, 0, 1 }, - { "myisam_max_extra_sort_file_size", - (long*) &myisam_max_extra_sort_file_size, - (long) (MI_MAX_TEMP_LENGTH/(1024L*1024L)), 0, ~0L, 0, 1 }, - { "myisam_max_sort_file_size", (long*) &myisam_max_sort_file_size, - (long) (LONG_MAX/(1024L*1024L)), 0, ~0L, 0, 1 }, - { "myisam_sort_buffer_size", (long*) &myisam_sort_buffer_size, - 8192*1024, 4, ~0L, 0, 1 }, - { "net_buffer_length", (long*) &net_buffer_length, - 16384, 1024, 1024*1024L, MALLOC_OVERHEAD, 1024 }, - { "net_retry_count", (long*) &mysqld_net_retry_count, - MYSQLD_NET_RETRY_COUNT, 1, ~0L, 0, 1 }, - { "net_read_timeout", (long*) &net_read_timeout, - NET_READ_TIMEOUT, 1, LONG_TIMEOUT, 0, 1 }, - { "net_write_timeout", (long*) &net_write_timeout, - NET_WRITE_TIMEOUT, 1, LONG_TIMEOUT, 0, 1 }, - { "open_files_limit", (long*) &open_files_limit, - 0, 0, 65535, 0, 1}, - { "query_buffer_size", (long*) &query_buff_size, - 0, MALLOC_OVERHEAD, (long) ~0, MALLOC_OVERHEAD, IO_SIZE }, - { "record_buffer", (long*) &my_default_record_cache_size, - 128*1024L, IO_SIZE*2+MALLOC_OVERHEAD, ~0L, MALLOC_OVERHEAD, IO_SIZE }, - { "record_rnd_buffer", (long*) &record_rnd_cache_size, - 0, IO_SIZE*2+MALLOC_OVERHEAD, ~0L, MALLOC_OVERHEAD, IO_SIZE }, - { "slave_net_timeout", (long*) &slave_net_timeout, - SLAVE_NET_TIMEOUT, 1, LONG_TIMEOUT, 0, 1 }, - { "slow_launch_time", (long*) &slow_launch_time, - 2L, 0L, LONG_TIMEOUT, 0, 1 }, - { "sort_buffer", (long*) &sortbuff_size, - MAX_SORT_MEMORY, MIN_SORT_MEMORY+MALLOC_OVERHEAD*2, ~0L, MALLOC_OVERHEAD, 1 }, - { "table_cache", (long*) &table_cache_size, - 64, 1, 16384, 0, 1 }, - { "thread_concurrency", (long*) &concurrency, - DEFAULT_CONCURRENCY, 1, 512, 0, 1 }, - { "thread_cache_size", (long*) &thread_cache_size, - 0, 0, 16384, 0, 1 }, - { "tmp_table_size", (long*) &tmp_table_size, - 32*1024*1024L, 1024, ~0L, 0, 1 }, - { "thread_stack", (long*) &thread_stack, - DEFAULT_THREAD_STACK, 1024*32, ~0L, 0, 1024 }, - { "wait_timeout", (long*) &net_wait_timeout, - NET_WAIT_TIMEOUT, 1, LONG_TIMEOUT, 0, 1 }, - { NullS, (long*) 0, 0, 0, 0, 0, 0} + , 0, 1, 0, 1, 0}, + {"max_allowed_packet", OPT_MAX_ALLOWED_PACKET, + "Max packetlength to send/receive from to server.", + (gptr*) &global_system_variables.max_allowed_packet, + (gptr*) &max_system_variables.max_allowed_packet, 0, GET_ULONG, + REQUIRED_ARG, 1024*1024L, 80, 1024L*1024L*1024L, MALLOC_OVERHEAD, 1024, 0}, + {"max_binlog_cache_size", OPT_MAX_BINLOG_CACHE_SIZE, + "Can be used to restrict the total size used to cache a multi-transaction query.", + (gptr*) &max_binlog_cache_size, (gptr*) &max_binlog_cache_size, 0, + GET_ULONG, REQUIRED_ARG, ~0L, IO_SIZE, ~0L, 0, IO_SIZE, 0}, + {"max_binlog_size", OPT_MAX_BINLOG_SIZE, + "Binary log will be rotated automatically when the size crosses the limit.", + (gptr*) &max_binlog_size, (gptr*) &max_binlog_size, 0, GET_ULONG, + REQUIRED_ARG, 1024*1024L*1024L, 1024, 1024*1024L*1024L, 0, 1, 0}, + {"max_connections", OPT_MAX_CONNECTIONS, + "The number of simultaneous clients allowed.", (gptr*) &max_connections, + (gptr*) &max_connections, 0, GET_ULONG, REQUIRED_ARG, 100, 1, 16384, 0, 1, + 0}, + {"max_connect_errors", OPT_MAX_CONNECT_ERRORS, + "If there is more than this number of interrupted connections from a host this host will be blocked from further connections.", + (gptr*) &max_connect_errors, (gptr*) &max_connect_errors, 0, GET_ULONG, + REQUIRED_ARG, MAX_CONNECT_ERRORS, 1, ~0L, 0, 1, 0}, + {"max_delayed_threads", OPT_MAX_DELAYED_THREADS, + "Don't start more than this number of threads to handle INSERT DELAYED statements.", + (gptr*) &max_insert_delayed_threads, (gptr*) &max_insert_delayed_threads, + 0, GET_ULONG, REQUIRED_ARG, 20, 1, 16384, 0, 1, 0}, + {"max_heap_table_size", OPT_MAX_HEP_TABLE_SIZE, + "Don't allow creation of heap tables bigger than this.", + (gptr*) &global_system_variables.max_heap_table_size, + (gptr*) &max_system_variables.max_heap_table_size, 0, GET_ULONG, + REQUIRED_ARG, 16*1024*1024L, 16384, ~0L, MALLOC_OVERHEAD, 1024, 0}, + {"max_join_size", OPT_MAX_JOIN_SIZE, + "Joins that are probably going to read more than max_join_size records return an error.", + (gptr*) &global_system_variables.max_join_size, + (gptr*) &max_system_variables.max_join_size, 0, GET_HA_ROWS, REQUIRED_ARG, + ~0L, 1, ~0L, 0, 1, 0}, + {"max_sort_length", OPT_MAX_SORT_LENGTH, + "The number of bytes to use when sorting BLOB or TEXT values (only the first max_sort_length bytes of each value are used; the rest are ignored).", + (gptr*) &global_system_variables.max_sort_length, + (gptr*) &max_system_variables.max_sort_length, 0, GET_ULONG, + REQUIRED_ARG, 1024, 4, 8192*1024L, 0, 1, 0}, + {"max_tmp_tables", OPT_MAX_TMP_TABLES, + "Maximum number of temporary tables a client can keep open at a time.", + (gptr*) &global_system_variables.max_tmp_tables, + (gptr*) &max_system_variables.max_tmp_tables, 0, GET_ULONG, + REQUIRED_ARG, 32, 1, ~0L, 0, 1, 0}, + {"max_user_connections", OPT_MAX_USER_CONNECTIONS, + "The maximum number of active connections for a single user (0 = no limit).", + (gptr*) &max_user_connections, (gptr*) &max_user_connections, 0, GET_ULONG, + REQUIRED_ARG, 0, 1, ~0L, 0, 1, 0}, + {"max_write_lock_count", OPT_MAX_WRITE_LOCK_COUNT, + "After this many write locks, allow some read locks to run in between.", + (gptr*) &max_write_lock_count, (gptr*) &max_write_lock_count, 0, GET_ULONG, + REQUIRED_ARG, ~0L, 1, ~0L, 0, 1, 0}, + {"bulk_insert_buffer_size", OPT_BULK_INSERT_BUFFER_SIZE, + "Size of tree cache used in bulk insert optimisation. Note that this is a limit per thread!", + (gptr*) &global_system_variables.bulk_insert_buff_size, + (gptr*) &max_system_variables.bulk_insert_buff_size, + 0, GET_ULONG, REQUIRED_ARG, 8192*1024, 0, ~0L, 0, 1, 0}, + {"myisam_block_size", OPT_MYISAM_BLOCK_SIZE, + "Block size to be used for MyISAM index pages", + (gptr*) &opt_myisam_block_size, + (gptr*) &opt_myisam_block_size, 0, GET_ULONG, REQUIRED_ARG, + MI_KEY_BLOCK_LENGTH, MI_MIN_KEY_BLOCK_LENGTH, MI_MAX_KEY_BLOCK_LENGTH, + 0, MI_MIN_KEY_BLOCK_LENGTH, 0}, + {"myisam_max_extra_sort_file_size", OPT_MYISAM_MAX_EXTRA_SORT_FILE_SIZE, + "Used to help MySQL to decide when to use the slow but safe key cache index create method", + (gptr*) &global_system_variables.myisam_max_extra_sort_file_size, + (gptr*) &max_system_variables.myisam_max_extra_sort_file_size, + 0, GET_ULL, REQUIRED_ARG, (ulonglong) MI_MAX_TEMP_LENGTH, + 0, ~0L, 0, 1, 0}, + {"myisam_max_sort_file_size", OPT_MYISAM_MAX_SORT_FILE_SIZE, + "Don't use the fast sort index method to created index if the temporary file would get bigger than this!", + (gptr*) &global_system_variables.myisam_max_sort_file_size, + (gptr*) &max_system_variables.myisam_max_sort_file_size, 0, + GET_ULL, REQUIRED_ARG, (longlong) LONG_MAX, 0, ~0L, 0, 1024*1024, 0}, + {"myisam_sort_buffer_size", OPT_MYISAM_SORT_BUFFER_SIZE, + "The buffer that is allocated when sorting the index when doing a REPAIR or when creating indexes with CREATE INDEX or ALTER TABLE.", + (gptr*) &global_system_variables.myisam_sort_buff_size, + (gptr*) &max_system_variables.myisam_sort_buff_size, 0, + GET_ULONG, REQUIRED_ARG, 8192*1024, 4, ~0L, 0, 1, 0}, + {"net_buffer_length", OPT_NET_BUFFER_LENGTH, + "Buffer length for TCP/IP and socket communication.", + (gptr*) &global_system_variables.net_buffer_length, + (gptr*) &max_system_variables.net_buffer_length, 0, GET_ULONG, + REQUIRED_ARG, 16384, 1024, 1024*1024L, 0, 1024, 0}, + {"net_retry_count", OPT_NET_RETRY_COUNT, + "If a read on a communication port is interrupted, retry this many times before giving up.", + (gptr*) &global_system_variables.net_retry_count, + (gptr*) &max_system_variables.net_retry_count,0, + GET_ULONG, REQUIRED_ARG, MYSQLD_NET_RETRY_COUNT, 1, ~0L, 0, 1, 0}, + {"net_read_timeout", OPT_NET_READ_TIMEOUT, + "Number of seconds to wait for more data from a connection before aborting the read.", + (gptr*) &global_system_variables.net_read_timeout, + (gptr*) &max_system_variables.net_read_timeout, 0, GET_ULONG, + REQUIRED_ARG, NET_READ_TIMEOUT, 1, LONG_TIMEOUT, 0, 1, 0}, + {"net_write_timeout", OPT_NET_WRITE_TIMEOUT, + "Number of seconds to wait for a block to be written to a connection before aborting the write.", + (gptr*) &global_system_variables.net_write_timeout, + (gptr*) &max_system_variables.net_write_timeout, 0, GET_ULONG, + REQUIRED_ARG, NET_WRITE_TIMEOUT, 1, LONG_TIMEOUT, 0, 1, 0}, + {"open_files_limit", OPT_OPEN_FILES_LIMIT, + "If this is not 0, then mysqld will use this value to reserve file descriptors to use with setrlimit(). If this value is 0 then mysqld will reserve max_connections*5 or max_connections + table_cache*2 (whichever is larger) number of files.", + (gptr*) &open_files_limit, (gptr*) &open_files_limit, 0, GET_ULONG, + REQUIRED_ARG, 0, 0, 65535, 0, 1, 0}, +#ifdef HAVE_QUERY_CACHE + {"query_cache_limit", OPT_QUERY_CACHE_LIMIT, + "Don't cache results that are bigger than this.", + (gptr*) &query_cache_limit, (gptr*) &query_cache_limit, 0, GET_ULONG, + REQUIRED_ARG, 1024*1024L, 0, (longlong) ULONG_MAX, 0, 1, 0}, +#endif /*HAVE_QUERY_CACHE*/ + {"query_cache_size", OPT_QUERY_CACHE_SIZE, + "The memory allocated to store results from old queries.", + (gptr*) &query_cache_size, (gptr*) &query_cache_size, 0, GET_ULONG, + REQUIRED_ARG, 0, 0, (longlong) ULONG_MAX, 0, 1024, 0}, +#ifdef HAVE_QUERY_CACHE + {"query_cache_type", OPT_QUERY_CACHE_TYPE, + "0 = OFF = Don't cache or retrieve results. 1 = ON = Cache all results except SELECT SQL_NO_CACHE ... queries. 2 = DEMAND = Cache only SELECT SQL_CACHE ... queries.", + (gptr*) &global_system_variables.query_cache_type, + (gptr*) &max_system_variables.query_cache_type, + 0, GET_ULONG, REQUIRED_ARG, 1, 0, 2, 0, 1, 0}, +#endif /*HAVE_QUERY_CACHE*/ + {"read_buffer_size", OPT_RECORD_BUFFER, + "Each thread that does a sequential scan allocates a buffer of this size for each table it scans. If you do many sequential scans, you may want to increase this value.", + (gptr*) &global_system_variables.read_buff_size, + (gptr*) &max_system_variables.read_buff_size,0, GET_ULONG, REQUIRED_ARG, + 128*1024L, IO_SIZE*2+MALLOC_OVERHEAD, ~0L, MALLOC_OVERHEAD, IO_SIZE, 0}, + {"read_rnd_buffer_size", OPT_RECORD_RND_BUFFER, + "When reading rows in sorted order after a sort, the rows are read through this buffer to avoid a disk seeks. If not set, then it's set to the value of record_buffer.", + (gptr*) &global_system_variables.read_rnd_buff_size, + (gptr*) &max_system_variables.read_rnd_buff_size, 0, + GET_ULONG, REQUIRED_ARG, 256*1024L, IO_SIZE*2+MALLOC_OVERHEAD, + ~0L, MALLOC_OVERHEAD, IO_SIZE, 0}, + {"record_buffer", OPT_RECORD_BUFFER, + "Alias for read_buffer_size", + (gptr*) &global_system_variables.read_buff_size, + (gptr*) &max_system_variables.read_buff_size,0, GET_ULONG, REQUIRED_ARG, + 128*1024L, IO_SIZE*2+MALLOC_OVERHEAD, ~0L, MALLOC_OVERHEAD, IO_SIZE, 0}, + {"relay_log_space_limit", OPT_RELAY_LOG_SPACE_LIMIT, + "Max space to use for all relay logs", + (gptr*) &relay_log_space_limit, + (gptr*) &relay_log_space_limit, 0, GET_ULL, REQUIRED_ARG, 0L, 0L, + (longlong) ULONG_MAX, 0, 1, 0}, + {"slave_compressed_protocol", OPT_SLAVE_COMPRESSED_PROTOCOL, + "Use compression on master/slave protocol", + (gptr*) &opt_slave_compressed_protocol, + (gptr*) &opt_slave_compressed_protocol, + 0, GET_BOOL, REQUIRED_ARG, 0, 0, 1, 0, 1, 0}, + {"slave_net_timeout", OPT_SLAVE_NET_TIMEOUT, + "Number of seconds to wait for more data from a master/slave connection before aborting the read.", + (gptr*) &slave_net_timeout, (gptr*) &slave_net_timeout, 0, + GET_ULONG, REQUIRED_ARG, SLAVE_NET_TIMEOUT, 1, LONG_TIMEOUT, 0, 1, 0}, + {"slow_launch_time", OPT_SLOW_LAUNCH_TIME, + "If creating the thread takes longer than this value (in seconds), the Slow_launch_threads counter will be incremented.", + (gptr*) &slow_launch_time, (gptr*) &slow_launch_time, 0, GET_ULONG, + REQUIRED_ARG, 2L, 0L, LONG_TIMEOUT, 0, 1, 0}, + {"sort_buffer_size", OPT_SORT_BUFFER, + "Each thread that needs to do a sort allocates a buffer of this size.", + (gptr*) &global_system_variables.sortbuff_size, + (gptr*) &max_system_variables.sortbuff_size, 0, GET_ULONG, REQUIRED_ARG, + MAX_SORT_MEMORY, MIN_SORT_MEMORY+MALLOC_OVERHEAD*2, ~0L, MALLOC_OVERHEAD, + 1, 0}, + {"table_cache", OPT_TABLE_CACHE, + "The number of open tables for all threads.", (gptr*) &table_cache_size, + (gptr*) &table_cache_size, 0, GET_ULONG, REQUIRED_ARG, 64, 1, 16384, 0, 1, + 0}, + {"thread_concurrency", OPT_THREAD_CONCURRENCY, + "Permits the application to give the threads system a hint for the desired number of threads that should be run at the same time.", + (gptr*) &concurrency, (gptr*) &concurrency, 0, GET_ULONG, REQUIRED_ARG, + DEFAULT_CONCURRENCY, 1, 512, 0, 1, 0}, + {"thread_cache_size", OPT_THREAD_CACHE_SIZE, + "How many threads we should keep in a cache for reuse.", + (gptr*) &thread_cache_size, (gptr*) &thread_cache_size, 0, GET_ULONG, + REQUIRED_ARG, 0, 0, 16384, 0, 1, 0}, + {"tmp_table_size", OPT_TMP_TABLE_SIZE, + "If an in-memory temporary table exceeds this size, MySQL will automatically convert it to an on-disk MyISAM table.", + (gptr*) &global_system_variables.tmp_table_size, + (gptr*) &max_system_variables.tmp_table_size, 0, GET_ULONG, + REQUIRED_ARG, 32*1024*1024L, 1024, ~0L, 0, 1, 0}, + {"thread_stack", OPT_THREAD_STACK, + "The stack size for each thread.", (gptr*) &thread_stack, + (gptr*) &thread_stack, 0, GET_ULONG, REQUIRED_ARG,DEFAULT_THREAD_STACK, + 1024*32, ~0L, 0, 1024, 0}, + {"wait_timeout", OPT_WAIT_TIMEOUT, + "The number of seconds the server waits for activity on a connection before closing it", + (gptr*) &global_system_variables.net_wait_timeout, + (gptr*) &max_system_variables.net_wait_timeout, 0, GET_ULONG, + REQUIRED_ARG, NET_WAIT_TIMEOUT, 1, LONG_TIMEOUT, 0, 1, 0}, + {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} }; -struct show_var_st init_vars[]= { - {"back_log", (char*) &back_log, SHOW_LONG}, - {"basedir", mysql_home, SHOW_CHAR}, -#ifdef HAVE_BERKELEY_DB - {"bdb_cache_size", (char*) &berkeley_cache_size, SHOW_LONG}, - {"bdb_log_buffer_size", (char*) &berkeley_log_buffer_size, SHOW_LONG}, - {"bdb_home", (char*) &berkeley_home, SHOW_CHAR_PTR}, - {"bdb_max_lock", (char*) &berkeley_max_lock, SHOW_LONG}, - {"bdb_logdir", (char*) &berkeley_logdir, SHOW_CHAR_PTR}, - {"bdb_shared_data", (char*) &berkeley_shared_data, SHOW_BOOL}, - {"bdb_tmpdir", (char*) &berkeley_tmpdir, SHOW_CHAR_PTR}, - {"bdb_version", (char*) DB_VERSION_STRING, SHOW_CHAR}, -#endif - {"binlog_cache_size", (char*) &binlog_cache_size, SHOW_LONG}, - {"character_set", default_charset, SHOW_CHAR}, - {"character_sets", (char*) &charsets_list, SHOW_CHAR_PTR}, - {"concurrent_insert", (char*) &myisam_concurrent_insert, SHOW_MY_BOOL}, - {"connect_timeout", (char*) &connect_timeout, SHOW_LONG}, - {"datadir", mysql_real_data_home, SHOW_CHAR}, - {"delay_key_write", (char*) &myisam_delay_key_write, SHOW_MY_BOOL}, - {"delayed_insert_limit", (char*) &delayed_insert_limit, SHOW_LONG}, - {"delayed_insert_timeout", (char*) &delayed_insert_timeout, SHOW_LONG}, - {"delayed_queue_size", (char*) &delayed_queue_size, SHOW_LONG}, - {"flush", (char*) &myisam_flush, SHOW_MY_BOOL}, - {"flush_time", (char*) &flush_time, SHOW_LONG}, -#ifdef HAVE_GEMINI_DB - {"gemini_buffer_cache", (char*) &gemini_buffer_cache, SHOW_LONG}, - {"gemini_connection_limit", (char*) &gemini_connection_limit, SHOW_LONG}, - {"gemini_io_threads", (char*) &gemini_io_threads, SHOW_LONG}, - {"gemini_log_cluster_size", (char*) &gemini_log_cluster_size, SHOW_LONG}, - {"gemini_lock_table_size", (char*) &gemini_locktablesize, SHOW_LONG}, - {"gemini_lock_wait_timeout",(char*) &gemini_lock_wait_timeout, SHOW_LONG}, - {"gemini_recovery_options", (char*) &gemini_recovery_options_str, SHOW_CHAR_PTR}, - {"gemini_spin_retries", (char*) &gemini_spin_retries, SHOW_LONG}, -#endif - {"have_bdb", (char*) &have_berkeley_db, SHOW_HAVE}, - {"have_gemini", (char*) &have_gemini, SHOW_HAVE}, - {"have_innodb", (char*) &have_innodb, SHOW_HAVE}, - {"have_isam", (char*) &have_isam, SHOW_HAVE}, - {"have_raid", (char*) &have_raid, SHOW_HAVE}, - {"have_openssl", (char*) &have_ssl, SHOW_HAVE}, - {"init_file", (char*) &opt_init_file, SHOW_CHAR_PTR}, -#ifdef HAVE_INNOBASE_DB - {"innodb_additional_mem_pool_size", (char*) &innobase_additional_mem_pool_size, SHOW_LONG }, - {"innodb_buffer_pool_size", (char*) &innobase_buffer_pool_size, SHOW_LONG }, - {"innodb_data_file_path", (char*) &innobase_data_file_path, SHOW_CHAR_PTR}, - {"innodb_data_home_dir", (char*) &innobase_data_home_dir, SHOW_CHAR_PTR}, - {"innodb_file_io_threads", (char*) &innobase_file_io_threads, SHOW_LONG }, - {"innodb_force_recovery", (char*) &innobase_force_recovery, SHOW_LONG }, - {"innodb_thread_concurrency", (char*) &innobase_thread_concurrency, SHOW_LONG }, - {"innodb_flush_log_at_trx_commit", (char*) &innobase_flush_log_at_trx_commit, SHOW_LONG}, - {"innodb_fast_shutdown", (char*) &innobase_fast_shutdown, SHOW_MY_BOOL}, - {"innodb_flush_method", (char*) &innobase_unix_file_flush_method, SHOW_CHAR_PTR}, - {"innodb_lock_wait_timeout", (char*) &innobase_lock_wait_timeout, SHOW_LONG }, - {"innodb_log_arch_dir", (char*) &innobase_log_arch_dir, SHOW_CHAR_PTR}, - {"innodb_log_archive", (char*) &innobase_log_archive, SHOW_MY_BOOL}, - {"innodb_log_buffer_size", (char*) &innobase_log_buffer_size, SHOW_LONG }, - {"innodb_log_file_size", (char*) &innobase_log_file_size, SHOW_LONG}, - {"innodb_log_files_in_group", (char*) &innobase_log_files_in_group, SHOW_LONG}, - {"innodb_log_group_home_dir", (char*) &innobase_log_group_home_dir, SHOW_CHAR_PTR}, - {"innodb_mirrored_log_groups", (char*) &innobase_mirrored_log_groups, SHOW_LONG}, -#endif - {"interactive_timeout", (char*) &net_interactive_timeout, SHOW_LONG}, - {"join_buffer_size", (char*) &join_buff_size, SHOW_LONG}, - {"key_buffer_size", (char*) &keybuff_size, SHOW_LONG_AS_LONGLONG}, - {"language", language, SHOW_CHAR}, - {"large_files_support", (char*) &opt_large_files, SHOW_BOOL}, -#ifdef HAVE_MLOCKALL - {"locked_in_memory", (char*) &locked_in_memory, SHOW_BOOL}, -#endif - {"log", (char*) &opt_log, SHOW_BOOL}, - {"log_update", (char*) &opt_update_log, SHOW_BOOL}, - {"log_bin", (char*) &opt_bin_log, SHOW_BOOL}, - {"log_slave_updates", (char*) &opt_log_slave_updates, SHOW_BOOL}, - {"log_long_queries", (char*) &opt_slow_log, SHOW_BOOL}, - {"long_query_time", (char*) &long_query_time, SHOW_LONG}, - {"low_priority_updates", (char*) &low_priority_updates, SHOW_BOOL}, - {"lower_case_table_names", (char*) &lower_case_table_names, SHOW_LONG}, - {"max_allowed_packet", (char*) &max_allowed_packet, SHOW_LONG}, - {"max_binlog_cache_size", (char*) &max_binlog_cache_size, SHOW_LONG}, - {"max_binlog_size", (char*) &max_binlog_size, SHOW_LONG}, - {"max_connections", (char*) &max_connections, SHOW_LONG}, - {"max_connect_errors", (char*) &max_connect_errors, SHOW_LONG}, - {"max_delayed_threads", (char*) &max_insert_delayed_threads, SHOW_LONG}, - {"max_heap_table_size", (char*) &max_heap_table_size, SHOW_LONG}, - {"max_join_size", (char*) &max_join_size, SHOW_LONG}, - {"max_sort_length", (char*) &max_item_sort_length, SHOW_LONG}, - {"max_user_connections", (char*) &max_user_connections, SHOW_LONG}, - {"max_tmp_tables", (char*) &max_tmp_tables, SHOW_LONG}, - {"max_write_lock_count", (char*) &max_write_lock_count, SHOW_LONG}, - {"myisam_max_extra_sort_file_size", (char*) &myisam_max_extra_sort_file_size, - SHOW_LONG}, - {"myisam_max_sort_file_size",(char*) &myisam_max_sort_file_size, SHOW_LONG}, - {"myisam_recover_options", (char*) &myisam_recover_options, SHOW_LONG}, - {"myisam_sort_buffer_size", (char*) &myisam_sort_buffer_size, SHOW_LONG}, -#ifdef __NT__ - {"named_pipe", (char*) &opt_enable_named_pipe, SHOW_BOOL}, -#endif - {"net_buffer_length", (char*) &net_buffer_length, SHOW_LONG}, - {"net_read_timeout", (char*) &net_read_timeout, SHOW_LONG}, - {"net_retry_count", (char*) &mysqld_net_retry_count, SHOW_LONG}, - {"net_write_timeout", (char*) &net_write_timeout, SHOW_LONG}, - {"open_files_limit", (char*) &open_files_limit, SHOW_LONG}, - {"pid_file", (char*) pidfile_name, SHOW_CHAR}, - {"port", (char*) &mysql_port, SHOW_INT}, - {"protocol_version", (char*) &protocol_version, SHOW_INT}, - {"record_buffer", (char*) &my_default_record_cache_size,SHOW_LONG}, - {"record_rnd_buffer", (char*) &record_rnd_cache_size, SHOW_LONG}, - {"query_buffer_size", (char*) &query_buff_size, SHOW_LONG}, - {"safe_show_database", (char*) &opt_safe_show_db, SHOW_BOOL}, - {"server_id", (char*) &server_id, SHOW_LONG}, - {"slave_net_timeout", (char*) &slave_net_timeout, SHOW_LONG}, - {"skip_locking", (char*) &my_disable_locking, SHOW_MY_BOOL}, - {"skip_networking", (char*) &opt_disable_networking, SHOW_BOOL}, - {"skip_show_database", (char*) &opt_skip_show_db, SHOW_BOOL}, - {"slow_launch_time", (char*) &slow_launch_time, SHOW_LONG}, - {"socket", (char*) &mysql_unix_port, SHOW_CHAR_PTR}, - {"sort_buffer", (char*) &sortbuff_size, SHOW_LONG}, - {"sql_mode", (char*) &opt_sql_mode, SHOW_LONG}, - {"table_cache", (char*) &table_cache_size, SHOW_LONG}, - {"table_type", (char*) &default_table_type_name, SHOW_CHAR_PTR}, - {"thread_cache_size", (char*) &thread_cache_size, SHOW_LONG}, -#ifdef HAVE_THR_SETCONCURRENCY - {"thread_concurrency", (char*) &concurrency, SHOW_LONG}, -#endif - {"thread_stack", (char*) &thread_stack, SHOW_LONG}, - {"transaction_isolation", (char*) &default_tx_isolation_name, SHOW_CHAR_PTR}, -#ifdef HAVE_TZNAME - {"timezone", time_zone, SHOW_CHAR}, -#endif - {"tmp_table_size", (char*) &tmp_table_size, SHOW_LONG}, - {"tmpdir", (char*) &mysql_tmpdir, SHOW_CHAR_PTR}, - {"version", server_version, SHOW_CHAR}, - {"wait_timeout", (char*) &net_wait_timeout, SHOW_LONG}, - {NullS, NullS, SHOW_LONG} -}; - struct show_var_st status_vars[]= { {"Aborted_clients", (char*) &aborted_threads, SHOW_LONG}, {"Aborted_connects", (char*) &aborted_connects, SHOW_LONG}, @@ -3208,16 +3956,21 @@ struct show_var_st status_vars[]= { {"Com_create_index", (char*) (com_stat+(uint) SQLCOM_CREATE_INDEX),SHOW_LONG}, {"Com_create_table", (char*) (com_stat+(uint) SQLCOM_CREATE_TABLE),SHOW_LONG}, {"Com_delete", (char*) (com_stat+(uint) SQLCOM_DELETE),SHOW_LONG}, + {"Com_delete_multi", (char*) (com_stat+(uint) SQLCOM_DELETE_MULTI),SHOW_LONG}, {"Com_drop_db", (char*) (com_stat+(uint) SQLCOM_DROP_DB),SHOW_LONG}, {"Com_drop_function", (char*) (com_stat+(uint) SQLCOM_DROP_FUNCTION),SHOW_LONG}, {"Com_drop_index", (char*) (com_stat+(uint) SQLCOM_DROP_INDEX),SHOW_LONG}, {"Com_drop_table", (char*) (com_stat+(uint) SQLCOM_DROP_TABLE),SHOW_LONG}, {"Com_flush", (char*) (com_stat+(uint) SQLCOM_FLUSH),SHOW_LONG}, {"Com_grant", (char*) (com_stat+(uint) SQLCOM_GRANT),SHOW_LONG}, + {"Com_ha_close", (char*) (com_stat+(uint) SQLCOM_HA_CLOSE),SHOW_LONG}, + {"Com_ha_open", (char*) (com_stat+(uint) SQLCOM_HA_OPEN),SHOW_LONG}, + {"Com_ha_read", (char*) (com_stat+(uint) SQLCOM_HA_READ),SHOW_LONG}, {"Com_insert", (char*) (com_stat+(uint) SQLCOM_INSERT),SHOW_LONG}, {"Com_insert_select", (char*) (com_stat+(uint) SQLCOM_INSERT_SELECT),SHOW_LONG}, {"Com_kill", (char*) (com_stat+(uint) SQLCOM_KILL),SHOW_LONG}, {"Com_load", (char*) (com_stat+(uint) SQLCOM_LOAD),SHOW_LONG}, + {"Com_load_master_data", (char*) (com_stat+(uint) SQLCOM_LOAD_MASTER_DATA),SHOW_LONG}, {"Com_load_master_table", (char*) (com_stat+(uint) SQLCOM_LOAD_MASTER_TABLE),SHOW_LONG}, {"Com_lock_tables", (char*) (com_stat+(uint) SQLCOM_LOCK_TABLES),SHOW_LONG}, {"Com_optimize", (char*) (com_stat+(uint) SQLCOM_OPTIMIZE),SHOW_LONG}, @@ -3232,6 +3985,7 @@ struct show_var_st status_vars[]= { {"Com_rollback", (char*) (com_stat+(uint) SQLCOM_ROLLBACK),SHOW_LONG}, {"Com_select", (char*) (com_stat+(uint) SQLCOM_SELECT),SHOW_LONG}, {"Com_set_option", (char*) (com_stat+(uint) SQLCOM_SET_OPTION),SHOW_LONG}, + {"Com_show_binlog_events", (char*) (com_stat+(uint) SQLCOM_SHOW_BINLOG_EVENTS),SHOW_LONG}, {"Com_show_binlogs", (char*) (com_stat+(uint) SQLCOM_SHOW_BINLOGS),SHOW_LONG}, {"Com_show_create", (char*) (com_stat+(uint) SQLCOM_SHOW_CREATE),SHOW_LONG}, {"Com_show_databases", (char*) (com_stat+(uint) SQLCOM_SHOW_DATABASES),SHOW_LONG}, @@ -3240,8 +3994,10 @@ struct show_var_st status_vars[]= { {"Com_show_keys", (char*) (com_stat+(uint) SQLCOM_SHOW_KEYS),SHOW_LONG}, {"Com_show_logs", (char*) (com_stat+(uint) SQLCOM_SHOW_LOGS),SHOW_LONG}, {"Com_show_master_status", (char*) (com_stat+(uint) SQLCOM_SHOW_MASTER_STAT),SHOW_LONG}, + {"Com_show_new_master", (char*) (com_stat+(uint) SQLCOM_SHOW_NEW_MASTER),SHOW_LONG}, {"Com_show_open_tables", (char*) (com_stat+(uint) SQLCOM_SHOW_OPEN_TABLES),SHOW_LONG}, {"Com_show_processlist", (char*) (com_stat+(uint) SQLCOM_SHOW_PROCESSLIST),SHOW_LONG}, + {"Com_show_slave_hosts", (char*) (com_stat+(uint) SQLCOM_SHOW_SLAVE_HOSTS),SHOW_LONG}, {"Com_show_slave_status", (char*) (com_stat+(uint) SQLCOM_SHOW_SLAVE_STAT),SHOW_LONG}, {"Com_show_status", (char*) (com_stat+(uint) SQLCOM_SHOW_STATUS),SHOW_LONG}, {"Com_show_innodb_status", (char*) (com_stat+(uint) SQLCOM_SHOW_INNODB_STATUS),SHOW_LONG}, @@ -3260,6 +4016,7 @@ struct show_var_st status_vars[]= { {"Delayed_writes", (char*) &delayed_insert_writes, SHOW_LONG}, {"Delayed_errors", (char*) &delayed_insert_errors, SHOW_LONG}, {"Flush_commands", (char*) &refresh_version, SHOW_LONG_CONST}, + {"Handler_commit", (char*) &ha_commit_count, SHOW_LONG}, {"Handler_delete", (char*) &ha_delete_count, SHOW_LONG}, {"Handler_read_first", (char*) &ha_read_first_count, SHOW_LONG}, {"Handler_read_key", (char*) &ha_read_key_count, SHOW_LONG}, @@ -3267,6 +4024,7 @@ struct show_var_st status_vars[]= { {"Handler_read_prev", (char*) &ha_read_prev_count, SHOW_LONG}, {"Handler_read_rnd", (char*) &ha_read_rnd_count, SHOW_LONG}, {"Handler_read_rnd_next", (char*) &ha_read_rnd_next_count, SHOW_LONG}, + {"Handler_rollback", (char*) &ha_rollback_count, SHOW_LONG}, {"Handler_update", (char*) &ha_update_count, SHOW_LONG}, {"Handler_write", (char*) &ha_write_count, SHOW_LONG}, {"Key_blocks_used", (char*) &_my_blocks_used, SHOW_LONG_CONST}, @@ -3282,19 +4040,58 @@ struct show_var_st status_vars[]= { {"Open_streams", (char*) &my_stream_opened, SHOW_LONG_CONST}, {"Opened_tables", (char*) &opened_tables, SHOW_LONG}, {"Questions", (char*) 0, SHOW_QUESTION}, +#ifdef HAVE_QUERY_CACHE + {"Qcache_queries_in_cache", (char*) &query_cache.queries_in_cache, SHOW_LONG_CONST}, + {"Qcache_inserts", (char*) &query_cache.inserts, SHOW_LONG}, + {"Qcache_hits", (char*) &query_cache.hits, SHOW_LONG}, + {"Qcache_lowmem_prunes", (char*) &query_cache.lowmem_prunes, SHOW_LONG}, + {"Qcache_not_cached", (char*) &query_cache.refused, SHOW_LONG}, + {"Qcache_free_memory", (char*) &query_cache.free_memory, + SHOW_LONG_CONST}, + {"Qcache_free_blocks", (char*) &query_cache.free_memory_blocks, + SHOW_LONG_CONST}, + {"Qcache_total_blocks", (char*) &query_cache.total_blocks, + SHOW_LONG_CONST}, +#endif /*HAVE_QUERY_CACHE*/ + {"Rpl_status", (char*) 0, SHOW_RPL_STATUS}, {"Select_full_join", (char*) &select_full_join_count, SHOW_LONG}, {"Select_full_range_join", (char*) &select_full_range_join_count, SHOW_LONG}, {"Select_range", (char*) &select_range_count, SHOW_LONG}, {"Select_range_check", (char*) &select_range_check_count, SHOW_LONG}, {"Select_scan", (char*) &select_scan_count, SHOW_LONG}, - {"Slave_running", (char*) &slave_running, SHOW_BOOL}, {"Slave_open_temp_tables", (char*) &slave_open_temp_tables, SHOW_LONG}, + {"Slave_running", (char*) 0, SHOW_SLAVE_RUNNING}, {"Slow_launch_threads", (char*) &slow_launch_threads, SHOW_LONG}, {"Slow_queries", (char*) &long_query_count, SHOW_LONG}, {"Sort_merge_passes", (char*) &filesort_merge_passes, SHOW_LONG}, {"Sort_range", (char*) &filesort_range_count, SHOW_LONG}, {"Sort_rows", (char*) &filesort_rows, SHOW_LONG}, {"Sort_scan", (char*) &filesort_scan_count, SHOW_LONG}, +#ifdef HAVE_OPENSSL + {"Ssl_accepts", (char*) 0, SHOW_SSL_CTX_SESS_ACCEPT}, + {"Ssl_finished_accepts", (char*) 0, SHOW_SSL_CTX_SESS_ACCEPT_GOOD}, + {"Ssl_finished_connects", (char*) 0, SHOW_SSL_CTX_SESS_CONNECT_GOOD}, + {"Ssl_accept_renegotiates", (char*) 0, SHOW_SSL_CTX_SESS_ACCEPT_RENEGOTIATE}, + {"Ssl_connect_renegotiates", (char*) 0, SHOW_SSL_CTX_SESS_CONNECT_RENEGOTIATE}, + {"Ssl_callback_cache_hits", (char*) 0, SHOW_SSL_CTX_SESS_CB_HITS}, + {"Ssl_session_cache_hits", (char*) 0, SHOW_SSL_CTX_SESS_HITS}, + {"Ssl_session_cache_misses", (char*) 0, SHOW_SSL_CTX_SESS_MISSES}, + {"Ssl_session_cache_timeouts", (char*) 0, SHOW_SSL_CTX_SESS_TIMEOUTS}, + {"Ssl_used_session_cache_entries",(char*) 0, SHOW_SSL_CTX_SESS_NUMBER}, + {"Ssl_client_connects", (char*) 0, SHOW_SSL_CTX_SESS_CONNECT}, + {"Ssl_session_cache_overflows", (char*) 0, SHOW_SSL_CTX_SESS_CACHE_FULL}, + {"Ssl_session_cache_size", (char*) 0, SHOW_SSL_CTX_SESS_GET_CACHE_SIZE}, + {"Ssl_session_cache_mode", (char*) 0, SHOW_SSL_CTX_GET_SESSION_CACHE_MODE}, + {"Ssl_sessions_reused", (char*) 0, SHOW_SSL_SESSION_REUSED}, + {"Ssl_ctx_verify_mode", (char*) 0, SHOW_SSL_CTX_GET_VERIFY_MODE}, + {"Ssl_ctx_verify_depth", (char*) 0, SHOW_SSL_CTX_GET_VERIFY_DEPTH}, + {"Ssl_verify_mode", (char*) 0, SHOW_SSL_GET_VERIFY_MODE}, + {"Ssl_verify_depth", (char*) 0, SHOW_SSL_GET_VERIFY_DEPTH}, + {"Ssl_version", (char*) 0, SHOW_SSL_GET_VERSION}, + {"Ssl_cipher", (char*) 0, SHOW_SSL_GET_CIPHER}, + {"Ssl_cipher_list", (char*) 0, SHOW_SSL_GET_CIPHER_LIST}, + {"Ssl_default_timeout", (char*) 0, SHOW_SSL_GET_DEFAULT_TIMEOUT}, +#endif /* HAVE_OPENSSL */ {"Table_locks_immediate", (char*) &locks_immediate, SHOW_LONG}, {"Table_locks_waited", (char*) &locks_waited, SHOW_LONG}, {"Threads_cached", (char*) &cached_thread_count, SHOW_LONG_CONST}, @@ -3315,7 +4112,7 @@ static void use_help(void) { print_version(); printf("Use '--help' or '--no-defaults --help' for a list of available options\n"); -} +} static void usage(void) { @@ -3327,216 +4124,54 @@ and you are welcome to modify and redistribute it under the GPL license\n\ Starts the MySQL server\n"); printf("Usage: %s [OPTIONS]\n", my_progname); - puts("\n\ - --ansi Use ANSI SQL syntax instead of MySQL syntax\n\ - -b, --basedir=path Path to installation directory. All paths are\n\ - usually resolved relative to this\n\ - --big-tables Allow big result sets by saving all temporary sets\n\ - on file (Solves most 'table full' errors)\n\ - --bind-address=IP Ip address to bind to\n\ - --bootstrap Used by mysql installation scripts\n\ - --character-sets-dir=...\n\ - Directory where character sets are\n\ - --chroot=path Chroot mysqld daemon during startup\n\ - --core-file Write core on errors\n\ - -h, --datadir=path Path to the database root"); -#ifndef DBUG_OFF - printf("\ - -#, --debug[=...] Debug log. Default is '%s'\n",default_dbug_option); -#ifdef SAFEMALLOC - puts("\ - --skip-safemalloc Don't use the memory allocation checking"); -#endif -#endif - puts("\ - --default-character-set=charset\n\ - Set the default character set\n\ - --default-table-type=type\n\ - Set the default table type for tables\n\ - --delay-key-write-for-all-tables\n\ - Don't flush key buffers between writes for any MyISAM\n\ - table\n\ - --enable-locking Enable system locking\n\ - -T, --exit-info Used for debugging; Use at your own risk!\n\ - --flush Flush tables to disk between SQL commands\n\ - -?, --help Display this help and exit\n\ - --init-file=file Read SQL commands from this file at startup\n\ - -L, --language=... Client error messages in given language. May be\n\ - given as a full path\n\ - --local-infile=[1|0] Enable/disable LOAD DATA LOCAL INFILE\n\ - -l, --log[=file] Log connections and queries to file\n\ - --log-bin[=file] Log queries in new binary format (for replication)\n\ - --log-bin-index=file File that holds the names for last binary log files\n\ - --log-update[=file] Log updates to file.# where # is a unique number\n\ - if not given.\n\ - --log-isam[=file] Log all MyISAM changes to file\n\ - --log-long-format Log some extra information to update log\n\ - --low-priority-updates INSERT/DELETE/UPDATE has lower priority than selects\n\ - --log-slow-queries=[file]\n\ - Log slow queries to this log file. Defaults logging\n\ - to hostname-slow.log\n\ - --pid-file=path Pid file used by safe_mysqld\n\ - --myisam-recover[=option[,option...]] where options is one of DEAULT,\n\ - BACKUP or FORCE.\n\ - --memlock Lock mysqld in memory\n\ - -n, --new Use very new possible 'unsafe' functions\n\ - -o, --old-protocol Use the old (3.20) protocol\n\ - -P, --port=... Port number to use for connection\n"); -#ifdef ONE_THREAD - puts("\ - --one-thread Only use one thread (for debugging under Linux)\n"); -#endif - puts("\ - -O, --set-variable var=option\n\ - Give a variable an value. --help lists variables\n\ - -Sg, --skip-grant-tables\n\ - Start without grant tables. This gives all users\n\ - FULL ACCESS to all tables!\n\ - --safe-mode Skip some optimize stages (for testing)\n\ - --safe-show-database Don't show databases for which the user has no\n\ - privileges\n\ - --safe-user-create Don't new users cretaion without privileges to the\n\ - mysql.user table\n\ - --skip-concurrent-insert\n\ - Don't use concurrent insert with MyISAM\n\ - --skip-delay-key-write\n\ - Ignore the delay_key_write option for all tables\n\ - --skip-host-cache Don't cache host names\n\ - --skip-locking Don't use system locking. To use isamchk one has\n\ - to shut down the server.\n\ - --skip-name-resolve Don't resolve hostnames.\n\ - All hostnames are IP's or 'localhost'\n\ - --skip-networking Don't allow connection with TCP/IP.\n\ - --skip-new Don't use new, possible wrong routines.\n"); - /* We have to break the string here because of VC++ limits */ - puts("\ - --skip-stack-trace Don't print a stack trace on failure\n\ - --skip-show-database Don't allow 'SHOW DATABASE' commands\n\ - --skip-thread-priority\n\ - Don't give threads different priorities.\n\ - --socket=... Socket file to use for connection\n\ - -t, --tmpdir=path Path for temporary files\n\ - --sql-mode=option[,option[,option...]] where option can be one of:\n\ - REAL_AS_FLOAT, PIPES_AS_CONCAT, ANSI_QUOTES,\n\ - IGNORE_SPACE, SERIALIZE, ONLY_FULL_GROUP_BY.\n\ - --transaction-isolation\n\ - Default transaction isolation level\n\ - --temp-pool Use a pool of temporary files\n\ - -u, --user=user_name Run mysqld daemon as user\n\ - -V, --version output version information and exit\n\ - -W, --warnings Log some not critical warnings to the log file\n"); #ifdef __WIN__ puts("NT and Win32 specific options:\n\ - --console Don't remove the console window\n\ --install Install the default service (NT)\n\ --install-manual Install the default service started manually (NT)\n\ + --install service_name Install an optional service (NT)\n\ + --install-manual service_name Install an optional service started manually (NT)\n\ --remove Remove the default service from the service list (NT)\n\ - --enable-named-pipe Enable the named pipe (NT)\n\ - --standalone Dummy option to start as a standalone program (NT)\ + --remove service_name Remove the service_name from the service list (NT)\n\ + --enable-named-pipe Only to be used for the default server (NT)\n\ + --standalone Dummy option to start as a standalone server (NT)\ "); -#ifdef USE_SYMDIR - puts("--use-symbolic-links Enable symbolic link support"); -#endif puts(""); #endif -#ifdef HAVE_BERKELEY_DB - puts("\ - --bdb-home= directory Berkeley home direcory\n\ - --bdb-lock-detect=# Berkeley lock detect\n\ - (DEFAULT, OLDEST, RANDOM or YOUNGEST, # sec)\n\ - --bdb-logdir=directory Berkeley DB log file directory\n\ - --bdb-no-sync Don't synchronously flush logs\n\ - --bdb-no-recover Don't try to recover Berkeley DB tables on start\n\ - --bdb-shared-data Start Berkeley DB in multi-process mode\n\ - --bdb-tmpdir=directory Berkeley DB tempfile name\n\ - --skip-bdb Don't use berkeley db (will save memory)\n\ -"); -#endif /* HAVE_BERKELEY_DB */ -#ifdef HAVE_GEMINI_DB - puts("\ - --gemini-recovery=mode Set Crash Recovery operating mode\n\ - (FULL, NONE, FORCE - default FULL)\n\ - --gemini-flush-log-at-commit\n\ - Every commit forces a write to the reovery log\n\ - --gemini-unbuffered-io Use unbuffered i/o\n\ - --skip-gemini Don't use gemini (will save memory)\n\ -"); -#endif -#ifdef HAVE_INNOBASE_DB - puts("\ - --innodb_data_home_dir=dir The common part for Innodb table spaces\n\ - --innodb_data_file_path=dir Path to individual files and their sizes\n\ - --innodb_flush_method=# With which method to flush data\n\ - --innodb_flush_log_at_trx_commit[=#]\n\ - Value 0: write and flush once per second\n\ - Value 1: write and flush at each commit\n\ - Value 2: write at commit, flush once per second\n\ - --innodb_log_arch_dir=dir Where full logs should be archived\n\ - --innodb_log_archive[=#] Set to 1 if you want to have logs archived\n\ - --innodb_log_group_home_dir=dir Path to innodb log files.\n\ - --skip-innodb Don't use Innodb (will save memory)\n\ -"); -#endif /* HAVE_INNOBASE_DB */ print_defaults(MYSQL_CONFIG_NAME,load_default_groups); puts(""); - -#include "sslopt-usage.h" - fix_paths(); set_ports(); - printf("\ -To see what values a running MySQL server is using, type\n\ -'mysqladmin variables' instead of 'mysqld --help'.\n\ -The default values (after parsing the command line arguments) are:\n\n"); - printf("basedir: %s\n",mysql_home); - printf("datadir: %s\n",mysql_real_data_home); - printf("tmpdir: %s\n",mysql_tmpdir); - printf("language: %s\n",language); -#ifndef __WIN__ - printf("pid file: %s\n",pidfile_name); -#endif - if (opt_logname) - printf("logfile: %s\n",opt_logname); - if (opt_update_logname) - printf("update log: %s\n",opt_update_logname); - if (opt_bin_log) - { - printf("binary log: %s\n",opt_bin_logname ? opt_bin_logname : ""); - printf("binary log index: %s\n", - opt_binlog_index_name ? opt_binlog_index_name : ""); - } - if (opt_slow_logname) - printf("update log: %s\n",opt_slow_logname); - printf("TCP port: %d\n",mysql_port); -#if defined(HAVE_SYS_UN_H) - printf("Unix socket: %s\n",mysql_unix_port); -#endif - if (my_disable_locking) - puts("\nsystem locking is not in use"); - if (opt_noacl) - puts("\nGrant tables are not used. All users have full access rights"); - printf("\nPossible variables for option --set-variable (-O) are:\n"); - for (uint i=0 ; changeable_vars[i].name ; i++) - printf("%-20s current value: %lu\n", - changeable_vars[i].name, - (ulong) *changeable_vars[i].varptr); + my_print_help(my_long_options); + my_print_variables(my_long_options); + + puts("\n\ +To see what values a running MySQL server is using, type\n\ +'mysqladmin variables' instead of 'mysqld --help'."); } static void set_options(void) { - set_all_changeable_vars( changeable_vars ); #if !defined( my_pthread_setprio ) && !defined( HAVE_PTHREAD_SETSCHEDPARAM ) opt_specialflag |= SPECIAL_NO_PRIOR; #endif - (void) strmake(default_charset, MYSQL_CHARSET, sizeof(default_charset)-1); + sys_charset.value= (char*) MYSQL_CHARSET; (void) strmake(language, LANGUAGE, sizeof(language)-1); (void) strmake(mysql_real_data_home, get_relative_path(DATADIR), sizeof(mysql_real_data_home)-1); -#ifdef __WIN__ - /* Allow Win32 users to move MySQL anywhere */ + + /* Set default values for some variables */ + global_system_variables.table_type=DB_TYPE_MYISAM; + global_system_variables.tx_isolation=ISO_REPEATABLE_READ; + global_system_variables.select_limit= HA_POS_ERROR; + max_system_variables.select_limit= HA_POS_ERROR; + global_system_variables.max_join_size= HA_POS_ERROR; + max_system_variables.max_join_size= HA_POS_ERROR; + +#if defined(__WIN__) || defined(__NETWARE__) + /* Allow Win32 and NetWare users to move MySQL anywhere */ { char prg_dev[LIBLEN]; my_path(prg_dev,my_progname,"mysql/bin"); @@ -3550,637 +4185,522 @@ static void set_options(void) (void) strmake(mysql_home, tmpenv, sizeof(mysql_home)-1); #endif -#if defined( HAVE_mit_thread ) || defined( __WIN__ ) || defined( HAVE_LINUXTHREADS ) - my_disable_locking = 1; -#endif + my_disable_locking=myisam_single_user= 1; + opt_external_locking=0; my_bind_addr = htonl( INADDR_ANY ); } - /* Initiates DEBUG - but no debugging here ! */ -static void get_options(int argc,char **argv) -{ - int c,option_index=0; - myisam_delay_key_write=1; // Allow use of this - while ((c=getopt_long(argc,argv,"ab:C:h:#::T::?l::L:O:P:sS::t:u:noVvWI?", - long_options, &option_index)) != EOF) - { - switch(c) { - case '#': +extern "C" my_bool +get_one_option(int optid, const struct my_option *opt __attribute__((unused)), + char *argument) +{ + switch(optid) { + case '#': #ifndef DBUG_OFF - DBUG_PUSH(optarg ? optarg : default_dbug_option); -#endif - opt_endinfo=1; /* unireg: memory allocation */ - break; - case 'W': - opt_warnings=1; - break; - case 'a': - opt_sql_mode = (MODE_REAL_AS_FLOAT | MODE_PIPES_AS_CONCAT | - MODE_ANSI_QUOTES | MODE_IGNORE_SPACE | MODE_SERIALIZABLE - | MODE_ONLY_FULL_GROUP_BY); - default_tx_isolation= ISO_SERIALIZABLE; - break; - case 'b': - strmake(mysql_home,optarg,sizeof(mysql_home)-1); - break; - case 'l': - opt_log=1; - opt_logname=optarg; // Use hostname.log if null - break; - case 'h': - strmake(mysql_real_data_home,optarg, sizeof(mysql_real_data_home)-1); - break; - case 'L': - strmake(language, optarg, sizeof(language)-1); - break; - case 'n': - opt_specialflag|= SPECIAL_NEW_FUNC; - break; - case 'o': - protocol_version=PROTOCOL_VERSION-1; - break; - case 'O': - if (set_changeable_var(optarg, changeable_vars)) - { - use_help(); - exit(1); - } - break; - case 'P': - mysql_port= (unsigned int) atoi(optarg); - break; - case OPT_LOCAL_INFILE: - opt_local_infile= test(!optarg || atoi(optarg) != 0); - break; - case OPT_SLAVE_SKIP_ERRORS: - init_slave_skip_errors(optarg); - break; - case OPT_SAFEMALLOC_MEM_LIMIT: + DBUG_PUSH(argument ? argument : default_dbug_option); +#endif + opt_endinfo=1; /* unireg: memory allocation */ + break; + case 'a': + opt_sql_mode = (MODE_REAL_AS_FLOAT | MODE_PIPES_AS_CONCAT | + MODE_ANSI_QUOTES | MODE_IGNORE_SPACE | MODE_SERIALIZABLE + | MODE_ONLY_FULL_GROUP_BY); + global_system_variables.tx_isolation= ISO_SERIALIZABLE; + break; + case 'b': + strmake(mysql_home,argument,sizeof(mysql_home)-1); + break; + case 'l': + opt_log=1; + break; + case 'h': + strmake(mysql_real_data_home,argument, sizeof(mysql_real_data_home)-1); + /* Correct pointer set by my_getopt (for embedded library) */ + mysql_data_home= mysql_real_data_home; + break; + case 'L': + strmake(language, argument, sizeof(language)-1); + break; + case 'n': + opt_specialflag|= SPECIAL_NEW_FUNC; + break; + case 'o': + protocol_version=PROTOCOL_VERSION-1; + break; + case OPT_SLAVE_SKIP_ERRORS: + init_slave_skip_errors(argument); + break; + case OPT_SAFEMALLOC_MEM_LIMIT: #if !defined(DBUG_OFF) && defined(SAFEMALLOC) - safemalloc_mem_limit = atoi(optarg); -#endif - break; - case OPT_SOCKET: - mysql_unix_port= optarg; - break; - case 'r': - mysqld_chroot=optarg; - break; -#ifdef USE_SYMDIR - case 's': - my_use_symdir=1; /* Use internal symbolic links */ - break; -#endif - case 't': - mysql_tmpdir=optarg; - break; - case OPT_TEMP_POOL: - use_temp_pool=1; - break; - case 'u': - mysqld_user=optarg; - break; - case 'v': - case 'V': - print_version(); - exit(0); - case 'I': - case '?': - usage(); - exit(0); - case 'T': - test_flags= optarg ? (uint) atoi(optarg) : 0; - opt_endinfo=1; - break; - case 'S': - if (!optarg) - opt_specialflag|= SPECIAL_NO_NEW_FUNC | SPECIAL_SAFE_MODE; - else if (!strcmp(optarg,"l")) - my_disable_locking=1; - else if (!strcmp(optarg,"g")) - opt_noacl=1; - else - { - fprintf(stderr,"%s: Unrecognized option: %s\n",my_progname,optarg); - use_help(); - exit(1); - } - break; - case (int) OPT_BIG_TABLES: - thd_startup_options|=OPTION_BIG_TABLES; - break; - case (int) OPT_ISAM_LOG: - opt_myisam_log=1; - if (optarg) - myisam_log_filename=optarg; - break; - case (int) OPT_UPDATE_LOG: - opt_update_log=1; - opt_update_logname=optarg; // Use hostname.# if null - break; - case (int) OPT_BIN_LOG_INDEX: - opt_binlog_index_name = optarg; - break; - case (int) OPT_BIN_LOG: - opt_bin_log=1; - x_free(opt_bin_logname); - if (optarg && optarg[0]) - opt_bin_logname=my_strdup(optarg,MYF(0)); - break; - // needs to be handled (as no-op) in non-debugging mode for test suite - case (int)OPT_DISCONNECT_SLAVE_EVENT_COUNT: -#ifndef DBUG_OFF - disconnect_slave_event_count = atoi(optarg); -#endif - break; - case (int)OPT_ABORT_SLAVE_EVENT_COUNT: -#ifndef DBUG_OFF - abort_slave_event_count = atoi(optarg); + safemalloc_mem_limit = atoi(argument); #endif - break; - case (int)OPT_SPORADIC_BINLOG_DUMP_FAIL: -#ifndef DBUG_OFF - opt_sporadic_binlog_dump_fail = 1; -#endif - break; - case (int)OPT_MAX_BINLOG_DUMP_EVENTS: -#ifndef DBUG_OFF - max_binlog_dump_events = atoi(optarg); -#endif - break; - - case (int) OPT_LOG_SLAVE_UPDATES: - opt_log_slave_updates = 1; - break; + break; +#ifdef EMBEDDED_LIBRARY + case OPT_MAX_ALLOWED_PACKET: + max_allowed_packet= atoi(argument); + break; + case OPT_NET_BUFFER_LENGTH: + net_buffer_length= atoi(argument); + break; +#endif +#include <sslopt-case.h> + case 'v': + case 'V': + print_version(); + exit(0); + case 'I': + case '?': + usage(); + exit(0); + case 'T': + test_flags= argument ? (uint) atoi(argument) : 0; + test_flags&= ~TEST_NO_THREADS; + opt_endinfo=1; + break; + case (int) OPT_BIG_TABLES: + thd_startup_options|=OPTION_BIG_TABLES; + break; + case (int) OPT_ISAM_LOG: + opt_myisam_log=1; + break; + case (int) OPT_UPDATE_LOG: + opt_update_log=1; + break; + case (int) OPT_BIN_LOG: + opt_bin_log=1; + break; + case (int) OPT_ERROR_LOG_FILE: + opt_error_log= 1; + break; + case (int) OPT_INIT_RPL_ROLE: + { + int role; + if ((role=find_type(argument, &rpl_role_typelib, 2)) <= 0) + { + fprintf(stderr, "Unknown replication role: %s\n", argument); + exit(1); + } + rpl_status = (role == 1) ? RPL_AUTH_MASTER : RPL_IDLE_SLAVE; + break; + } + case (int)OPT_REPLICATE_IGNORE_DB: + { + i_string *db = new i_string(argument); + replicate_ignore_db.push_back(db); + break; + } + case (int)OPT_REPLICATE_DO_DB: + { + i_string *db = new i_string(argument); + replicate_do_db.push_back(db); + break; + } + case (int)OPT_REPLICATE_REWRITE_DB: + { + char* key = argument,*p, *val; + + if (!(p= strstr(argument, "->"))) + { + fprintf(stderr, + "Bad syntax in replicate-rewrite-db - missing '->'!\n"); + exit(1); + } + val= p--; + while (isspace(*p) && p > argument) + *p-- = 0; + if (p == argument) + { + fprintf(stderr, + "Bad syntax in replicate-rewrite-db - empty FROM db!\n"); + exit(1); + } + *val= 0; + val+= 2; + while (*val && isspace(*val)) + *val++; + if (!*val) + { + fprintf(stderr, + "Bad syntax in replicate-rewrite-db - empty TO db!\n"); + exit(1); + } - case (int)OPT_REPLICATE_IGNORE_DB: - { - i_string *db = new i_string(optarg); - replicate_ignore_db.push_back(db); - break; - } - case (int)OPT_REPLICATE_DO_DB: - { - i_string *db = new i_string(optarg); - replicate_do_db.push_back(db); - break; - } - case (int)OPT_REPLICATE_REWRITE_DB: - { - char* key = optarg,*p, *val; - p = strstr(optarg, "->"); - if (!p) - { - fprintf(stderr, - "Bad syntax in replicate-rewrite-db - missing '->'!\n"); - exit(1); - } - val = p--; - while(isspace(*p) && p > optarg) *p-- = 0; - if(p == optarg) - { - fprintf(stderr, - "Bad syntax in replicate-rewrite-db - empty FROM db!\n"); - exit(1); - } - *val = 0; - val += 2; - while(*val && isspace(*val)) *val++; - if (!*val) - { - fprintf(stderr, - "Bad syntax in replicate-rewrite-db - empty TO db!\n"); - exit(1); - } - - i_string_pair* db_pair = new i_string_pair(key, val); - replicate_rewrite_db.push_back(db_pair); - break; - } + i_string_pair *db_pair = new i_string_pair(key, val); + replicate_rewrite_db.push_back(db_pair); + break; + } - case (int)OPT_BINLOG_IGNORE_DB: - { - i_string *db = new i_string(optarg); - binlog_ignore_db.push_back(db); - break; - } - case (int)OPT_BINLOG_DO_DB: - { - i_string *db = new i_string(optarg); - binlog_do_db.push_back(db); - break; - } - case (int)OPT_REPLICATE_DO_TABLE: - { - if (!do_table_inited) - init_table_rule_hash(&replicate_do_table, &do_table_inited); - if(add_table_rule(&replicate_do_table, optarg)) - { - fprintf(stderr, "Could not add do table rule '%s'!\n", optarg); - exit(1); - } - table_rules_on = 1; - break; - } - case (int)OPT_REPLICATE_WILD_DO_TABLE: - { - if (!wild_do_table_inited) - init_table_rule_array(&replicate_wild_do_table, - &wild_do_table_inited); - if(add_wild_table_rule(&replicate_wild_do_table, optarg)) - { - fprintf(stderr, "Could not add do table rule '%s'!\n", optarg); - exit(1); - } - table_rules_on = 1; - break; - } - case (int)OPT_REPLICATE_WILD_IGNORE_TABLE: - { - if (!wild_ignore_table_inited) - init_table_rule_array(&replicate_wild_ignore_table, - &wild_ignore_table_inited); - if(add_wild_table_rule(&replicate_wild_ignore_table, optarg)) - { - fprintf(stderr, "Could not add ignore table rule '%s'!\n", optarg); - exit(1); - } - table_rules_on = 1; - break; - } - case (int)OPT_REPLICATE_IGNORE_TABLE: - { - if (!ignore_table_inited) - init_table_rule_hash(&replicate_ignore_table, &ignore_table_inited); - if(add_table_rule(&replicate_ignore_table, optarg)) - { - fprintf(stderr, "Could not add ignore table rule '%s'!\n", optarg); - exit(1); - } - table_rules_on = 1; - break; - } - case (int) OPT_SQL_BIN_UPDATE_SAME: - opt_sql_bin_update = 1; - break; - case (int) OPT_SLOW_QUERY_LOG: - opt_slow_log=1; - opt_slow_logname=optarg; - break; - case (int)OPT_SKIP_SLAVE_START: - opt_skip_slave_start = 1; - break; - case (int) OPT_SKIP_NEW: - opt_specialflag|= SPECIAL_NO_NEW_FUNC; - default_table_type=DB_TYPE_ISAM; - myisam_delay_key_write=0; - myisam_concurrent_insert=0; - myisam_recover_options= HA_RECOVER_NONE; - my_disable_symlinks=1; - ha_open_options&= ~HA_OPEN_ABORT_IF_CRASHED; - break; - case (int) OPT_SAFE: - opt_specialflag|= SPECIAL_SAFE_MODE; - myisam_delay_key_write=0; - myisam_recover_options= HA_RECOVER_NONE; // To be changed - ha_open_options&= ~HA_OPEN_ABORT_IF_CRASHED; - break; - case (int) OPT_SKIP_CONCURRENT_INSERT: - myisam_concurrent_insert=0; - break; - case (int) OPT_SKIP_PRIOR: - opt_specialflag|= SPECIAL_NO_PRIOR; - break; - case (int) OPT_SKIP_GRANT: - opt_noacl=1; - break; - case (int) OPT_SKIP_LOCK: - my_disable_locking=1; - break; - case (int) OPT_SKIP_HOST_CACHE: - opt_specialflag|= SPECIAL_NO_HOST_CACHE; - break; - case (int) OPT_ENABLE_LOCK: - my_disable_locking=0; - break; - case (int) OPT_USE_LOCKING: - my_disable_locking=0; - break; - case (int) OPT_SKIP_RESOLVE: - opt_specialflag|=SPECIAL_NO_RESOLVE; - break; - case (int) OPT_LONG_FORMAT: - opt_specialflag|=SPECIAL_LONG_LOG_FORMAT; - break; - case (int) OPT_SKIP_NETWORKING: - opt_disable_networking=1; - mysql_port=0; - break; - case (int) OPT_SKIP_SHOW_DB: - opt_skip_show_db=1; - opt_specialflag|=SPECIAL_SKIP_SHOW_DB; - break; - case (int) OPT_MEMLOCK: - locked_in_memory=1; - break; - case (int) OPT_ONE_THREAD: - test_flags |= TEST_NO_THREADS; - break; - case (int) OPT_WANT_CORE: - test_flags |= TEST_CORE_ON_SIGNAL; - break; - case (int) OPT_SKIP_STACK_TRACE: - test_flags|=TEST_NO_STACKTRACE; - break; - case (int) OPT_SKIP_SYMLINKS: - my_disable_symlinks=1; - break; - case (int) OPT_BIND_ADDRESS: - if (optarg && isdigit(optarg[0])) - { - my_bind_addr = (ulong) inet_addr(optarg); - } + case (int)OPT_BINLOG_IGNORE_DB: + { + i_string *db = new i_string(argument); + binlog_ignore_db.push_back(db); + break; + } + case (int)OPT_BINLOG_DO_DB: + { + i_string *db = new i_string(argument); + binlog_do_db.push_back(db); + break; + } + case (int)OPT_REPLICATE_DO_TABLE: + { + if (!do_table_inited) + init_table_rule_hash(&replicate_do_table, &do_table_inited); + if (add_table_rule(&replicate_do_table, argument)) + { + fprintf(stderr, "Could not add do table rule '%s'!\n", argument); + exit(1); + } + table_rules_on = 1; + break; + } + case (int)OPT_REPLICATE_WILD_DO_TABLE: + { + if (!wild_do_table_inited) + init_table_rule_array(&replicate_wild_do_table, + &wild_do_table_inited); + if (add_wild_table_rule(&replicate_wild_do_table, argument)) + { + fprintf(stderr, "Could not add do table rule '%s'!\n", argument); + exit(1); + } + table_rules_on = 1; + break; + } + case (int)OPT_REPLICATE_WILD_IGNORE_TABLE: + { + if (!wild_ignore_table_inited) + init_table_rule_array(&replicate_wild_ignore_table, + &wild_ignore_table_inited); + if (add_wild_table_rule(&replicate_wild_ignore_table, argument)) + { + fprintf(stderr, "Could not add ignore table rule '%s'!\n", argument); + exit(1); + } + table_rules_on = 1; + break; + } + case (int)OPT_REPLICATE_IGNORE_TABLE: + { + if (!ignore_table_inited) + init_table_rule_hash(&replicate_ignore_table, &ignore_table_inited); + if (add_table_rule(&replicate_ignore_table, argument)) + { + fprintf(stderr, "Could not add ignore table rule '%s'!\n", argument); + exit(1); + } + table_rules_on = 1; + break; + } + case (int) OPT_SLOW_QUERY_LOG: + opt_slow_log=1; + break; + case (int)OPT_RECKLESS_SLAVE: + opt_reckless_slave = 1; + init_slave_skip_errors("all"); + break; + case (int) OPT_SKIP_NEW: + opt_specialflag|= SPECIAL_NO_NEW_FUNC; + delay_key_write_options= (uint) DELAY_KEY_WRITE_NONE; + myisam_concurrent_insert=0; + myisam_recover_options= HA_RECOVER_NONE; + my_disable_symlinks=1; + my_use_symdir=0; + have_symlink=SHOW_OPTION_DISABLED; + ha_open_options&= ~(HA_OPEN_ABORT_IF_CRASHED | HA_OPEN_DELAY_KEY_WRITE); +#ifdef HAVE_QUERY_CACHE + query_cache_size=0; +#endif + break; + case (int) OPT_SAFE: + opt_specialflag|= SPECIAL_SAFE_MODE; + delay_key_write_options= (uint) DELAY_KEY_WRITE_NONE; + myisam_recover_options= HA_RECOVER_DEFAULT; + ha_open_options&= ~(HA_OPEN_DELAY_KEY_WRITE); + break; + case (int) OPT_SKIP_PRIOR: + opt_specialflag|= SPECIAL_NO_PRIOR; + break; + case (int) OPT_SKIP_LOCK: + opt_external_locking=0; + break; + case (int) OPT_SKIP_HOST_CACHE: + opt_specialflag|= SPECIAL_NO_HOST_CACHE; + break; + case (int) OPT_SKIP_RESOLVE: + opt_specialflag|=SPECIAL_NO_RESOLVE; + break; + case (int) OPT_LONG_FORMAT: + opt_specialflag|=SPECIAL_LONG_LOG_FORMAT; + break; + case (int) OPT_SKIP_NETWORKING: + opt_disable_networking=1; + mysql_port=0; + break; + case (int) OPT_SKIP_SHOW_DB: + opt_skip_show_db=1; + opt_specialflag|=SPECIAL_SKIP_SHOW_DB; + break; +#ifdef ONE_THREAD + case (int) OPT_ONE_THREAD: + test_flags |= TEST_NO_THREADS; +#endif + break; + case (int) OPT_WANT_CORE: + test_flags |= TEST_CORE_ON_SIGNAL; + break; + case (int) OPT_SKIP_STACK_TRACE: + test_flags|=TEST_NO_STACKTRACE; + break; + case (int) OPT_SKIP_SYMLINKS: + my_disable_symlinks=1; + my_use_symdir=0; + have_symlink=SHOW_OPTION_DISABLED; + break; + case (int) OPT_BIND_ADDRESS: + if (argument && isdigit(argument[0])) + { + my_bind_addr = (ulong) inet_addr(argument); + } + else + { + struct hostent *ent; + if (argument || argument[0]) + ent=gethostbyname(argument); else { - struct hostent *ent; - if (optarg && optarg[0]) - ent=gethostbyname(optarg); - else - { - char myhostname[255]; - if (gethostname(myhostname,sizeof(myhostname)) < 0) - { - sql_perror("Can't start server: cannot get my own hostname!"); - exit(1); - } - ent=gethostbyname(myhostname); - } - if (!ent) + char myhostname[255]; + if (gethostname(myhostname,sizeof(myhostname)) < 0) { - sql_perror("Can't start server: cannot resolve hostname!"); + sql_perror("Can't start server: cannot get my own hostname!"); exit(1); } - my_bind_addr = (ulong) ((in_addr*)ent->h_addr_list[0])->s_addr; + ent=gethostbyname(myhostname); } - break; - case (int) OPT_PID_FILE: - strmake(pidfile_name, optarg, sizeof(pidfile_name)-1); - break; - case (int) OPT_INIT_FILE: - opt_init_file=optarg; - break; - case (int) OPT_HAVE_NAMED_PIPE: -#if __NT__ - opt_enable_named_pipe=1; -#endif - break; -#ifdef __WIN__ - case (int) OPT_STANDALONE: /* Dummy option for NT */ - break; - case (int) OPT_CONSOLE: - opt_console=1; - break; -#endif - case (int) OPT_FLUSH: - nisam_flush=myisam_flush=1; - flush_time=0; // No auto flush - break; - case OPT_LOW_PRIORITY_UPDATES: - thd_startup_options|=OPTION_LOW_PRIORITY_UPDATES; - thr_upgraded_concurrent_insert_lock= TL_WRITE_LOW_PRIORITY; - low_priority_updates=1; - break; - case OPT_BOOTSTRAP: - opt_noacl=opt_bootstrap=1; - break; - case OPT_TABLE_TYPE: - { - int type; - if ((type=find_type(optarg, &ha_table_typelib, 2)) <= 0) + if (!ent) { - fprintf(stderr,"Unknown table type: %s\n",optarg); + sql_perror("Can't start server: cannot resolve hostname!"); exit(1); } - default_table_type= (enum db_type) type; - break; + my_bind_addr = (ulong) ((in_addr*)ent->h_addr_list[0])->s_addr; } - case OPT_SERVER_ID: - server_id = atol(optarg); - server_id_supplied = 1; - break; - case OPT_DELAY_KEY_WRITE: - ha_open_options|=HA_OPEN_DELAY_KEY_WRITE; - myisam_delay_key_write=1; - break; - case OPT_SKIP_DELAY_KEY_WRITE: - myisam_delay_key_write=0; - break; - case 'C': - strmake(default_charset, optarg, sizeof(default_charset)-1); - break; - case OPT_CHARSETS_DIR: - strmake(mysql_charsets_dir, optarg, sizeof(mysql_charsets_dir)-1); - charsets_dir = mysql_charsets_dir; - break; -#include "sslopt-case.h" - case OPT_TX_ISOLATION: + break; + case (int) OPT_PID_FILE: + strmake(pidfile_name, argument, sizeof(pidfile_name)-1); + break; +#ifdef __WIN__ + case (int) OPT_STANDALONE: /* Dummy option for NT */ + break; +#endif + case OPT_CONSOLE: + if (opt_console) + opt_error_log= 0; // Force logs to stdout + break; + case (int) OPT_FLUSH: +#ifdef HAVE_ISAM + nisam_flush=1; +#endif + myisam_flush=1; + flush_time=0; // No auto flush + break; + case OPT_LOW_PRIORITY_UPDATES: + thr_upgraded_concurrent_insert_lock= TL_WRITE_LOW_PRIORITY; + global_system_variables.low_priority_updates=1; + break; + case OPT_BOOTSTRAP: + opt_noacl=opt_bootstrap=1; + break; + case OPT_TABLE_TYPE: + { + int type; + if ((type=find_type(argument, &ha_table_typelib, 2)) <= 0) + { + fprintf(stderr,"Unknown table type: %s\n",argument); + exit(1); + } + global_system_variables.table_type= type-1; + break; + } + case OPT_SERVER_ID: + server_id_supplied = 1; + break; + case OPT_DELAY_KEY_WRITE_ALL: + if (argument != disabled_my_option) + argument= (char*) "ALL"; + /* Fall through */ + case OPT_DELAY_KEY_WRITE: + if (argument == disabled_my_option) + delay_key_write_options= (uint) DELAY_KEY_WRITE_NONE; + else if (! argument) + delay_key_write_options= (uint) DELAY_KEY_WRITE_ON; + else { int type; - if ((type=find_type(optarg, &tx_isolation_typelib, 2)) <= 0) + if ((type=find_type(argument, &delay_key_write_typelib, 2)) <= 0) { - fprintf(stderr,"Unknown transaction isolation type: %s\n",optarg); + fprintf(stderr,"Unknown delay_key_write type: %s\n",argument); exit(1); } - default_tx_isolation= (enum_tx_isolation) (type-1); - break; + delay_key_write_options= (uint) type-1; } + break; + case OPT_CHARSETS_DIR: + strmake(mysql_charsets_dir, argument, sizeof(mysql_charsets_dir)-1); + charsets_dir = mysql_charsets_dir; + break; + case OPT_TX_ISOLATION: + { + int type; + if ((type=find_type(argument, &tx_isolation_typelib, 2)) <= 0) + { + fprintf(stderr,"Unknown transaction isolation type: %s\n",argument); + exit(1); + } + global_system_variables.tx_isolation= (type-1); + break; + } #ifdef HAVE_BERKELEY_DB - case OPT_BDB_LOG: - berkeley_logdir=optarg; - break; - case OPT_BDB_HOME: - berkeley_home=optarg; - break; - case OPT_BDB_NOSYNC: - berkeley_env_flags|=DB_TXN_NOSYNC; - break; - case OPT_BDB_NO_RECOVER: - berkeley_init_flags&= ~(DB_RECOVER); - break; - case OPT_BDB_TMP: - berkeley_tmpdir=optarg; - break; - case OPT_BDB_LOCK: + case OPT_BDB_NOSYNC: + berkeley_env_flags|=DB_TXN_NOSYNC; + break; + case OPT_BDB_NO_RECOVER: + berkeley_init_flags&= ~(DB_RECOVER); + break; + case OPT_BDB_LOCK: + { + int type; + if ((type=find_type(argument, &berkeley_lock_typelib, 2)) > 0) + berkeley_lock_type=berkeley_lock_types[type-1]; + else { - int type; - if ((type=find_type(optarg, &berkeley_lock_typelib, 2)) > 0) - berkeley_lock_type=berkeley_lock_types[type-1]; + if (test_if_int(argument,(uint) strlen(argument))) + berkeley_lock_scan_time=atoi(argument); else { - if (test_if_int(optarg,(uint) strlen(optarg))) - berkeley_lock_scan_time=atoi(optarg); - else - { - fprintf(stderr,"Unknown lock type: %s\n",optarg); - exit(1); - } + fprintf(stderr,"Unknown lock type: %s\n",argument); + exit(1); } - break; } - case OPT_BDB_SHARED: - berkeley_init_flags&= ~(DB_PRIVATE); - berkeley_shared_data=1; - break; + break; + } + case OPT_BDB_SHARED: + berkeley_init_flags&= ~(DB_PRIVATE); + berkeley_shared_data=1; + break; #endif /* HAVE_BERKELEY_DB */ - case OPT_BDB_SKIP: + case OPT_BDB_SKIP: #ifdef HAVE_BERKELEY_DB - berkeley_skip=1; - have_berkeley_db=SHOW_OPTION_DISABLED; -#endif - break; - case OPT_GEMINI_SKIP: -#ifdef HAVE_GEMINI_DB - gemini_skip=1; - have_gemini=SHOW_OPTION_DISABLED; - break; - case OPT_GEMINI_RECOVER: - gemini_recovery_options_str=optarg; - if ((gemini_recovery_options= - find_bit_type(optarg, &gemini_recovery_typelib)) == ~(ulong) 0) - { - fprintf(stderr, "Unknown option to gemini-recovery: %s\n",optarg); - exit(1); - } - break; - case OPT_GEMINI_FLUSH_LOG: - gemini_options |= GEMOPT_FLUSH_LOG; - break; - case OPT_GEMINI_UNBUFFERED_IO: - gemini_options |= GEMOPT_UNBUFFERED_IO; + berkeley_skip=1; + have_berkeley_db=SHOW_OPTION_DISABLED; #endif - break; - case OPT_INNODB_SKIP: + break; + case OPT_INNODB_SKIP: #ifdef HAVE_INNOBASE_DB - innodb_skip=1; - have_innodb=SHOW_OPTION_DISABLED; + innodb_skip=1; + have_innodb=SHOW_OPTION_DISABLED; #endif - break; - case OPT_INNODB_DATA_FILE_PATH: + break; + case OPT_INNODB_DATA_FILE_PATH: #ifdef HAVE_INNOBASE_DB - innobase_data_file_path=optarg; + innobase_data_file_path=argument; #endif - break; + break; #ifdef HAVE_INNOBASE_DB - case OPT_INNODB_DATA_HOME_DIR: - innobase_data_home_dir=optarg; - break; - case OPT_INNODB_LOG_GROUP_HOME_DIR: - innobase_log_group_home_dir=optarg; - break; - case OPT_INNODB_LOG_ARCH_DIR: - innobase_log_arch_dir=optarg; - break; - case OPT_INNODB_LOG_ARCHIVE: - innobase_log_archive= optarg ? test(atoi(optarg)) : 1; - break; - case OPT_INNODB_FLUSH_LOG_AT_TRX_COMMIT: - innobase_flush_log_at_trx_commit= optarg ? atoi(optarg) : 1; - break; - case OPT_INNODB_FAST_SHUTDOWN: - innobase_fast_shutdown= optarg ? test(atoi(optarg)) : 1; - break; - case OPT_INNODB_UNIX_FILE_FLUSH_METHOD: - innobase_unix_file_flush_method=optarg; - break; + case OPT_INNODB_LOG_ARCHIVE: + innobase_log_archive= argument ? test(atoi(argument)) : 1; + break; + case OPT_INNODB_FAST_SHUTDOWN: + innobase_fast_shutdown= argument ? test(atoi(argument)) : 1; + break; #endif /* HAVE_INNOBASE_DB */ - case OPT_MYISAM_RECOVER: + case OPT_MYISAM_RECOVER: + { + if (!argument || !argument[0]) { - if (!optarg) - { - myisam_recover_options= HA_RECOVER_DEFAULT; - myisam_recover_options_str= myisam_recover_typelib.type_names[0]; - } - else - { - myisam_recover_options_str=optarg; - if ((myisam_recover_options= - find_bit_type(optarg, &myisam_recover_typelib)) == ~(ulong) 0) - { - fprintf(stderr, "Unknown option to myisam-recover: %s\n",optarg); - exit(1); - } - } - ha_open_options|=HA_OPEN_ABORT_IF_CRASHED; - break; + myisam_recover_options= HA_RECOVER_DEFAULT; + myisam_recover_options_str= myisam_recover_typelib.type_names[0]; } - case OPT_SQL_MODE: + else { - sql_mode_str = optarg; - if ((opt_sql_mode = - find_bit_type(optarg, &sql_mode_typelib)) == ~(ulong) 0) + myisam_recover_options_str=argument; + if ((myisam_recover_options= + find_bit_type(argument, &myisam_recover_typelib)) == ~(ulong) 0) { - fprintf(stderr, "Unknown option to sql-mode: %s\n", optarg); + fprintf(stderr, "Unknown option to myisam-recover: %s\n",argument); exit(1); } - default_tx_isolation= ((opt_sql_mode & MODE_SERIALIZABLE) ? - ISO_SERIALIZABLE : - ISO_READ_COMMITTED); - break; } - case OPT_MASTER_HOST: - master_host=optarg; - break; - case OPT_MASTER_USER: - master_user=optarg; - break; - case OPT_MASTER_PASSWORD: - master_password=optarg; - break; - case OPT_MASTER_INFO_FILE: - master_info_file=optarg; - break; - case OPT_MASTER_PORT: - master_port= atoi(optarg); - break; - case OPT_MASTER_CONNECT_RETRY: - master_connect_retry= atoi(optarg); - break; - case OPT_MASTER_RETRY_COUNT: - master_retry_count= atoi(optarg); - break; - case OPT_SAFE_SHOW_DB: - opt_safe_show_db=1; - break; - case OPT_SAFE_USER_CREATE: - opt_safe_user_create=1; - break; - case OPT_SKIP_SAFEMALLOC: -#ifdef SAFEMALLOC - sf_malloc_quick=1; -#endif - break; - default: - fprintf(stderr,"%s: Unrecognized option: %c\n",my_progname,c); - use_help(); + ha_open_options|=HA_OPEN_ABORT_IF_CRASHED; + break; + } + case OPT_SQL_MODE: + { + sql_mode_str = argument; + if ((opt_sql_mode = + find_bit_type(argument, &sql_mode_typelib)) == ~(ulong) 0) + { + fprintf(stderr, "Unknown option to sql-mode: %s\n", argument); exit(1); } + global_system_variables.tx_isolation= ((opt_sql_mode & MODE_SERIALIZABLE) ? + ISO_SERIALIZABLE : + ISO_REPEATABLE_READ); + break; + } + case OPT_MASTER_PASSWORD: + master_password=argument; + break; + case OPT_SKIP_SAFEMALLOC: +#ifdef SAFEMALLOC + sf_malloc_quick=1; +#endif + break; } - // Skipp empty arguments (from shell) - while (argc != optind && !argv[optind][0]) - optind++; - if (argc != optind) + return 0; +} + /* Initiates DEBUG - but no debugging here ! */ + +static void get_options(int argc,char **argv) +{ + int ho_error; + + if ((ho_error=handle_options(&argc, &argv, my_long_options, get_one_option))) + exit(ho_error); + +#if defined(HAVE_BROKEN_REALPATH) + my_use_symdir=0; + my_disable_symlinks=1; + have_symlink=SHOW_OPTION_NO; +#else + if (!my_use_symdir) { - fprintf(stderr,"%s: Too many parameters\n",my_progname); - use_help(); - exit(1); + my_disable_symlinks=1; + have_symlink=SHOW_OPTION_DISABLED; } +#endif + /* Set global MyISAM variables from delay_key_write_options */ + fix_delay_key_write((THD*) 0, OPT_GLOBAL); + + if (mysqld_chroot) + set_root(mysqld_chroot); fix_paths(); - default_table_type_name=ha_table_typelib.type_names[default_table_type-1]; - default_tx_isolation_name=tx_isolation_typelib.type_names[default_tx_isolation]; - /* To be deleted in MySQL 4.0 */ - if (!record_rnd_cache_size) - record_rnd_cache_size=my_default_record_cache_size; + + /* + Set some global variables from the global_system_variables + In most cases the global variables will not be used + */ + my_disable_locking= myisam_single_user= test(opt_external_locking == 0); + my_default_record_cache_size=global_system_variables.read_buff_size; + myisam_max_temp_length= + (my_off_t) min(global_system_variables.myisam_max_sort_file_size, + (ulonglong) MAX_FILE_SIZE); + myisam_max_extra_temp_length= + (my_off_t) min(global_system_variables.myisam_max_extra_sort_file_size, + (ulonglong) MAX_FILE_SIZE); + + /* Set global variables based on startup options */ + myisam_block_size=(uint) 1 << my_bit_log2(opt_myisam_block_size); } @@ -4198,22 +4718,53 @@ static char *get_relative_path(const char *path) } +/* + Fix filename and replace extension where 'dir' is relative to + mysql_real_data_home. + Return 1 if len(path) > FN_REFLEN +*/ + +bool +fn_format_relative_to_data_home(my_string to, const char *name, + const char *dir, const char *extension) +{ + char tmp_path[FN_REFLEN]; + if (!test_if_hard_path(dir)) + { + strxnmov(tmp_path,sizeof(tmp_path)-1, mysql_real_data_home, + dir, NullS); + dir=tmp_path; + } + return !fn_format(to, name, dir, extension, + MY_REPLACE_EXT | MY_UNPACK_FILENAME | MY_SAFE_PATH); +} + + static void fix_paths(void) { - (void) fn_format(mysql_home,mysql_home,"","",16); // Remove symlinks - convert_dirname(mysql_home); - convert_dirname(mysql_real_data_home); - convert_dirname(language); + char buff[FN_REFLEN],*pos; + convert_dirname(mysql_home,mysql_home,NullS); + /* Resolve symlinks to allow 'mysql_home' to be a relative symlink */ + my_realpath(mysql_home,mysql_home,MYF(0)); + /* Ensure that mysql_home ends in FN_LIBCHAR */ + pos=strend(mysql_home); + if (pos[-1] != FN_LIBCHAR) + { + pos[0]= FN_LIBCHAR; + pos[1]= 0; + } + convert_dirname(mysql_real_data_home,mysql_real_data_home,NullS); + convert_dirname(language,language,NullS); (void) my_load_path(mysql_home,mysql_home,""); // Resolve current dir (void) my_load_path(mysql_real_data_home,mysql_real_data_home,mysql_home); (void) my_load_path(pidfile_name,pidfile_name,mysql_real_data_home); - char buff[FN_REFLEN],*sharedir=get_relative_path(SHAREDIR); + char *sharedir=get_relative_path(SHAREDIR); if (test_if_hard_path(sharedir)) strmake(buff,sharedir,sizeof(buff)-1); /* purecov: tested */ else strxnmov(buff,sizeof(buff)-1,mysql_home,sharedir,NullS); - convert_dirname(buff); + convert_dirname(buff,buff,NullS); (void) my_load_path(language,language,buff); /* If --character-sets-dir isn't given, use shared library dir */ @@ -4224,19 +4775,19 @@ static void fix_paths(void) charsets_dir=mysql_charsets_dir; } - /* Add '/' to TMPDIR if needed */ - char *tmp= (char*) my_malloc(FN_REFLEN,MYF(MY_FAE)); - if (tmp) + char *end=convert_dirname(buff, opt_mysql_tmpdir, NullS); + if (!(mysql_tmpdir= my_memdup((byte*) buff,(uint) (end-buff)+1, + MYF(MY_FAE)))) + exit(1); + if (!slave_load_tmpdir) { - strmake(tmp, mysql_tmpdir, FN_REFLEN-1); - mysql_tmpdir=tmp; - convert_dirname(mysql_tmpdir); - mysql_tmpdir=(char*) my_realloc(mysql_tmpdir,(uint) strlen(mysql_tmpdir)+1, - MYF(MY_HOLD_ON_ERROR)); + if (!(slave_load_tmpdir = (char*) my_strdup(mysql_tmpdir, MYF(MY_FAE)))) + exit(1); } } + #ifdef SET_RLIMIT_NOFILE static uint set_maximum_open_files(uint max_file_limit) { @@ -4251,16 +4802,17 @@ static uint set_maximum_open_files(uint max_file_limit) rlimit.rlim_cur=rlimit.rlim_max=max_file_limit; if (setrlimit(RLIMIT_NOFILE,&rlimit)) { - sql_print_error("Warning: setrlimit couldn't increase number of open files to more than %ld", - old_cur); /* purecov: inspected */ + sql_print_error("Warning: setrlimit couldn't increase number of open files to more than %lu (request: %u)", + old_cur, max_file_limit); /* purecov: inspected */ max_file_limit=old_cur; } else { (void) getrlimit(RLIMIT_NOFILE,&rlimit); if ((uint) rlimit.rlim_cur != max_file_limit) - sql_print_error("Warning: setrlimit returned ok, but didn't change limits. Max open files is %ld", - (ulong) rlimit.rlim_cur); /* purecov: inspected */ + sql_print_error("Warning: setrlimit returned ok, but didn't change limits. Max open files is %ld (request: %u)", + (ulong) rlimit.rlim_cur, + max_file_limit); /* purecov: inspected */ max_file_limit=rlimit.rlim_cur; } } @@ -4292,10 +4844,10 @@ static uint set_maximum_open_files(uint max_file_limit) } #endif - /* - Return a bitfield from a string of substrings separated by ',' - returns ~(ulong) 0 on error. - */ +/* + Return a bitfield from a string of substrings separated by ',' + returns ~(ulong) 0 on error. +*/ static ulong find_bit_type(const char *x, TYPELIB *bit_lib) { @@ -4308,6 +4860,7 @@ static ulong find_bit_type(const char *x, TYPELIB *bit_lib) DBUG_PRINT("enter",("x: '%s'",x)); found=0; + found_end= 0; pos=(my_string) x; while (*pos == ' ') pos++; found_end= *pos == 0; @@ -4316,7 +4869,7 @@ static ulong find_bit_type(const char *x, TYPELIB *bit_lib) if (!*(end=strcend(pos,','))) /* Let end point at fieldend */ { while (end > pos && end[-1] == ' ') - end--; /* Skipp end-space */ + end--; /* Skip end-space */ found_end=1; } found_int=0; found_count=0; @@ -4352,7 +4905,7 @@ skipp: ; /***************************************************************************** -** Instantiate templates + Instantiate templates *****************************************************************************/ #ifdef __GNUC__ diff --git a/sql/net_pkg.cc b/sql/net_pkg.cc index 381e0ded3f4..315cad5ca6d 100644 --- a/sql/net_pkg.cc +++ b/sql/net_pkg.cc @@ -1,15 +1,15 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - +/* Copyright (C) 2000-2003 MySQL AB + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -30,13 +30,14 @@ void send_error(NET *net, uint sql_errno, const char *err) err ? err : net->last_error[0] ? net->last_error : "NULL")); + query_cache_abort(net); if (thd) thd->query_error = 1; // needed to catch query errors during replication if (!err) { if (sql_errno) err=ER(sql_errno); - else if (!err) + else { if ((err=net->last_error)[0]) sql_errno=net->last_errno; @@ -51,6 +52,7 @@ void send_error(NET *net, uint sql_errno, const char *err) { if (thd && thd->bootstrap) { + /* In bootstrap it's ok to print on stderr */ fprintf(stderr,"ERROR: %d %s\n",sql_errno,err); } DBUG_VOID_RETURN; @@ -85,9 +87,10 @@ void send_warning(NET *net, uint sql_errno, const char *err) DBUG_VOID_RETURN; } -/** -** write error package and flush to client -** It's a little too low level, but I don't want to allow another buffer + +/* + Write error package and flush to client + It's a little too low level, but I don't want to allow another buffer */ /* VARARGS3 */ @@ -102,11 +105,23 @@ net_printf(NET *net, uint errcode, ...) DBUG_ENTER("net_printf"); DBUG_PRINT("enter",("message: %u",errcode)); - if(thd) thd->query_error = 1; - // if we are here, something is wrong :-) - + if (thd) + thd->query_error = 1; // if we are here, something is wrong :-) + query_cache_abort(net); // Safety va_start(args,errcode); - format=ER(errcode); + /* + The following is needed to make net_printf() work with 0 argument for + errorcode and use the argument after that as the format string. This + is useful for rare errors that are not worth the hassle to put in + errmsg.sys, but at the same time, the message is not fixed text + */ + if (errcode) + format= ER(errcode); + else + { + format=va_arg(args,char*); + errcode= ER_UNKNOWN_ERROR; + } offset= net->return_errno ? 2 : 0; text_pos=(char*) net->buff+head_length+offset+1; (void) vsprintf(my_const_cast(char*) (text_pos),format,args); @@ -119,6 +134,10 @@ net_printf(NET *net, uint errcode, ...) { if (thd && thd->bootstrap) { + /* + In bootstrap it's ok to print on stderr + This may also happen when we get an error from a slave thread + */ fprintf(stderr,"ERROR: %d %s\n",errcode,text_pos); thd->fatal_error=1; } @@ -142,7 +161,7 @@ send_ok(NET *net,ha_rows affected_rows,ulonglong id,const char *message) { if (net->no_send_ok) // hack for re-parsing queries return; - + char buff[MYSQL_ERRMSG_SIZE+10],*pos; DBUG_ENTER("send_ok"); buff[0]=0; // No fields @@ -264,8 +283,8 @@ bool net_store_data(String *packet,const char *from,uint length) { ulong packet_length=packet->length(); - if (packet_length+5+length > packet->alloced_length() && - packet->realloc(packet_length+5+length)) + if (packet_length+9+length > packet->alloced_length() && + packet->realloc(packet_length+9+length)) return 1; char *to=(char*) net_store_length((char*) packet->ptr()+packet_length, (ulonglong) length); @@ -281,6 +300,10 @@ net_store_data(String *packet,const char *from) { uint length=(uint) strlen(from); uint packet_length=packet->length(); + /* + 3 is the longest coding for storing a string with the used + net_store_length() function. We use 5 here 'just in case' + */ if (packet_length+5+length > packet->alloced_length() && packet->realloc(packet_length+5+length)) return 1; @@ -330,9 +353,9 @@ bool net_store_data(String* packet, I_List<i_string>* str_list) I_List_iterator<i_string> it(*str_list); i_string* s; - while((s=it++)) + while ((s=it++)) { - if(tmp.length()) + if (tmp.length()) tmp.append(','); tmp.append(s->ptr); } @@ -360,3 +383,19 @@ net_store_data(String *packet, CONVERT *convert, const char *from) return convert->store(packet, from, length); return net_store_data(packet,from,length); } + +/* + Function called by my_net_init() to set some check variables +*/ + +extern "C" { +void my_net_local_init(NET *net) +{ + net->max_packet= (uint) global_system_variables.net_buffer_length; + net->read_timeout= (uint) global_system_variables.net_read_timeout; + net->write_timeout=(uint) global_system_variables.net_write_timeout; + net->retry_count= (uint) global_system_variables.net_retry_count; + net->max_packet_size= max(global_system_variables.net_buffer_length, + global_system_variables.max_allowed_packet); +} +} diff --git a/sql/net_serv.cc b/sql/net_serv.cc index c6a0b8c5b3e..19f68e0b631 100644 --- a/sql/net_serv.cc +++ b/sql/net_serv.cc @@ -1,202 +1,189 @@ -/* 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, +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - 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 */ - -/* Write and read of logical packets to/from socket -** Writes are cached into net_buffer_length big packets. -** Read packets are reallocated dynamicly when reading big packets. -** Each logical packet has the following pre-info: -** 3 byte length & 1 byte package-number. + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* + Write and read of logical packets to/from socket + + Writes are cached into net_buffer_length big packets. + Read packets are reallocated dynamicly when reading big packets. + Each logical packet has the following pre-info: + 3 byte length & 1 byte package-number. */ #ifdef __WIN__ #include <winsock.h> #endif -#include <global.h> -#include <violite.h> +#include <my_global.h> +#include <mysql.h> +#include <mysql_embed.h> +#include <mysql_com.h> +#include <mysqld_error.h> #include <my_sys.h> #include <m_string.h> -#include "mysql.h" -#include "mysqld_error.h" +#include <my_net.h> +#include <violite.h> #include <signal.h> #include <errno.h> -#include <sys/types.h> -#ifdef MYSQL_SERVER -#include <violite.h> -#endif -#define MAX_PACKET_LENGTH (256L*256L*256L-1) +/* + The following handles the differences when this is linked between the + client and the server. + + This gives an error if a too big packet is found + The server can change this with the -O switch, but because the client + can't normally do this the client should have a bigger max_allowed_packet. +*/ #ifdef MYSQL_SERVER -ulong max_allowed_packet=65536; -extern ulong net_read_timeout,net_write_timeout; +#define USE_QUERY_CACHE extern uint test_flags; +extern void query_cache_insert(NET *net, const char *packet, ulong length); #else -ulong max_allowed_packet=MAX_PACKET_LENGTH; -ulong net_read_timeout= NET_READ_TIMEOUT; -ulong net_write_timeout= NET_WRITE_TIMEOUT; #endif -ulong net_buffer_length=8192; /* Default length. Enlarged if necessary */ -#if !defined(__WIN__) && !defined(MSDOS) -#include <sys/socket.h> -#else -#undef MYSQL_SERVER /* Win32 can't handle interrupts */ -#endif -#if !defined(MSDOS) && !defined(__WIN__) && !defined(HAVE_BROKEN_NETINET_INCLUDES) && !defined(__BEOS__) -#include <netinet/in_systm.h> -#include <netinet/in.h> -#include <netinet/ip.h> -#if !defined(alpha_linux_port) -#include <netinet/tcp.h> +#if defined(__WIN__) || !defined(MYSQL_SERVER) + /* The following is because alarms doesn't work on windows. */ +#define NO_ALARM #endif -#endif -#include "mysqld_error.h" -#ifdef MYSQL_SERVER + +#ifndef NO_ALARM #include "my_pthread.h" -#include "thr_alarm.h" void sql_print_error(const char *format,...); -#define RETRY_COUNT mysqld_net_retry_count -extern ulong mysqld_net_retry_count; -#else - -#ifdef OS2 /* avoid name conflict */ -#define thr_alarm_t thr_alarm_t_net -#define ALARM ALARM_net -#endif - -typedef my_bool thr_alarm_t; -typedef my_bool ALARM; -#define thr_alarm_init(A) (*(A))=0 -#define thr_alarm_in_use(A) (*(A)) -#define thr_end_alarm(A) -#define thr_alarm(A,B,C) local_thr_alarm((A),(B),(C)) -inline int local_thr_alarm(my_bool *A,int B __attribute__((unused)),ALARM *C __attribute__((unused))) -{ - *A=1; - return 0; -} -#define thr_got_alarm(A) 0 -#define RETRY_COUNT 1 -#endif - -#ifdef MYSQL_SERVER -extern ulong bytes_sent, bytes_received; +extern ulong bytes_sent, bytes_received; extern pthread_mutex_t LOCK_bytes_sent , LOCK_bytes_received; #else #undef statistic_add #define statistic_add(A,B,C) -#endif +#define DONT_USE_THR_ALARM +#endif /* NO_ALARM */ -/* -** Give error if a too big packet is found -** The server can change this with the -O switch, but because the client -** can't normally do this the client should have a bigger max-buffer. -*/ +#include "thr_alarm.h" #define TEST_BLOCKING 8 -static int net_write_buff(NET *net,const char *packet,uint len); +#define MAX_PACKET_LENGTH (256L*256L*256L-1) + +static my_bool net_write_buff(NET *net,const char *packet,ulong len); /* Init with packet info */ int my_net_init(NET *net, Vio* vio) { - if (!(net->buff=(uchar*) my_malloc(net_buffer_length,MYF(MY_WME)))) - return 1; - if (net_buffer_length > max_allowed_packet) - max_allowed_packet=net_buffer_length; - net->buff_end=net->buff+(net->max_packet=net_buffer_length); + DBUG_ENTER("my_net_init"); + my_net_local_init(net); /* Set some limits */ + if (!(net->buff=(uchar*) my_malloc((uint32) net->max_packet+ + NET_HEADER_SIZE + COMP_HEADER_SIZE, + MYF(MY_WME)))) + DBUG_RETURN(1); + net->buff_end=net->buff+net->max_packet; net->vio = vio; net->no_send_ok = 0; net->error=0; net->return_errno=0; net->return_status=0; - net->timeout=(uint) net_read_timeout; /* Timeout for read */ - net->pkt_nr=0; + net->pkt_nr=net->compress_pkt_nr=0; net->write_pos=net->read_pos = net->buff; net->last_error[0]=0; net->compress=0; net->reading_or_writing=0; net->where_b = net->remain_in_buf=0; net->last_errno=0; + net->query_cache_query=0; if (vio != 0) /* If real connection */ { net->fd = vio_fd(vio); /* For perl DBI/DBD */ #if defined(MYSQL_SERVER) && !defined(___WIN__) && !defined(__EMX__) && !defined(OS2) if (!(test_flags & TEST_BLOCKING)) - vio_blocking(vio, FALSE); + { + my_bool old_mode; + vio_blocking(vio, FALSE, &old_mode); + } #endif vio_fastsend(vio); } - return 0; + DBUG_RETURN(0); } + void net_end(NET *net) { + DBUG_ENTER("net_end"); my_free((gptr) net->buff,MYF(MY_ALLOW_ZERO_PTR)); net->buff=0; + DBUG_VOID_RETURN; } + /* Realloc the packet buffer */ static my_bool net_realloc(NET *net, ulong length) { uchar *buff; ulong pkt_length; - if (length >= max_allowed_packet) + DBUG_ENTER("net_realloc"); + DBUG_PRINT("enter",("length: %lu", length)); + + if (length >= net->max_packet_size) { - DBUG_PRINT("error",("Packet too large (%lu)", length)); + DBUG_PRINT("error",("Packet too large. Max sixe: %lu", + net->max_packet_size)); net->error=1; net->last_errno=ER_NET_PACKET_TOO_LARGE; - return 1; + DBUG_RETURN(1); } - pkt_length = (length+IO_SIZE-1) & ~(IO_SIZE-1); - if (!(buff=(uchar*) my_realloc((char*) net->buff, pkt_length, MYF(MY_WME)))) + pkt_length = (length+IO_SIZE-1) & ~(IO_SIZE-1); + /* + We must allocate some extra bytes for the end 0 and to be able to + read big compressed blocks + */ + if (!(buff=(uchar*) my_realloc((char*) net->buff, (uint32) pkt_length + + NET_HEADER_SIZE + COMP_HEADER_SIZE, + MYF(MY_WME)))) { net->error=1; #ifdef MYSQL_SERVER net->last_errno=ER_OUT_OF_RESOURCES; #endif - return 1; + DBUG_RETURN(1); } net->buff=net->write_pos=buff; net->buff_end=buff+(net->max_packet=pkt_length); - return 0; + DBUG_RETURN(0); } /* Remove unwanted characters from connection */ void net_clear(NET *net) { -#ifndef EXTRA_DEBUG - int count; // One may get 'unused' warning - bool is_blocking=vio_is_blocking(net->vio); - if (is_blocking) - vio_blocking(net->vio, FALSE); - if (!vio_is_blocking(net->vio)) /* Safety if SSL */ + DBUG_ENTER("net_clear"); +#if !defined(EXTRA_DEBUG) && !defined(EMBEDDED_LIBRARY) { - while ( (count = vio_read(net->vio, (char*) (net->buff), - net->max_packet)) > 0) - DBUG_PRINT("info",("skipped %d bytes from file: %s", - count,vio_description(net->vio))); - if (is_blocking) - vio_blocking(net->vio, TRUE); + int count; /* One may get 'unused' warn */ + my_bool old_mode; + if (!vio_blocking(net->vio, FALSE, &old_mode)) + { + while ((count = vio_read(net->vio, (char*) (net->buff), + (uint32) net->max_packet)) > 0) + DBUG_PRINT("info",("skipped %d bytes from file: %s", + count, vio_description(net->vio))); + vio_blocking(net->vio, TRUE, &old_mode); + } } #endif /* EXTRA_DEBUG */ - net->pkt_nr=0; /* Ready for new command */ + net->pkt_nr=net->compress_pkt_nr=0; /* Ready for new command */ net->write_pos=net->buff; + DBUG_VOID_RETURN; } /* Flush write_buffer if not empty. */ @@ -208,9 +195,12 @@ int net_flush(NET *net) if (net->buff != net->write_pos) { error=net_real_write(net,(char*) net->buff, - (uint) (net->write_pos - net->buff)); + (ulong) (net->write_pos - net->buff)); net->write_pos=net->buff; } + /* Sync packet number if using compression */ + if (net->compress) + net->pkt_nr=net->compress_pkt_nr; DBUG_RETURN(error); } @@ -219,86 +209,185 @@ int net_flush(NET *net) ** Write something to server/client buffer *****************************************************************************/ - /* ** Write a logical packet with packet header ** Format: Packet length (3 bytes), packet number(1 byte) ** When compression is used a 3 byte compression length is added -** NOTE: If compression is used the original package is destroyed! +** NOTE: If compression is used the original package is modified! */ int my_net_write(NET *net,const char *packet,ulong len) { uchar buff[NET_HEADER_SIZE]; - if (len >= MAX_PACKET_LENGTH) + /* + Big packets are handled by splitting them in packets of MAX_PACKET_LENGTH + length. The last packet is always a packet that is < MAX_PACKET_LENGTH. + (The last packet may even have a length of 0) + */ + while (len >= MAX_PACKET_LENGTH) { - net->error=1; - net->last_errno=ER_NET_PACKET_TOO_LARGE; - return 1; + const ulong z_size = MAX_PACKET_LENGTH; + int3store(buff, z_size); + buff[3]= (uchar) net->pkt_nr++; + if (net_write_buff(net, (char*) buff, NET_HEADER_SIZE) || + net_write_buff(net, packet, z_size)) + return 1; + packet += z_size; + len-= z_size; } + /* Write last packet */ int3store(buff,len); - buff[3]= (net->compress) ? 0 : (uchar) (net->pkt_nr++); + buff[3]= (uchar) net->pkt_nr++; if (net_write_buff(net,(char*) buff,NET_HEADER_SIZE)) return 1; - return net_write_buff(net,packet,len); + DBUG_DUMP("packet_header",(char*) buff,NET_HEADER_SIZE); + return test(net_write_buff(net,packet,len)); } +/* + Send a command to the server. + As the command is part of the first data packet, we have to do some data + juggling to put the command in there, without having to create a new + packet. + This function will split big packets into sub-packets if needed. + (Each sub packet can only be 2^24 bytes) +*/ + int net_write_command(NET *net,uchar command,const char *packet,ulong len) { + ulong length=len+1; /* 1 extra byte for command */ uchar buff[NET_HEADER_SIZE+1]; - uint length=len+1; /* 1 extra byte for command */ + uint header_size=NET_HEADER_SIZE+1; + DBUG_ENTER("net_write_command"); + DBUG_PRINT("enter",("length: %lu", len)); + + buff[4]=command; /* For first packet */ + if (length >= MAX_PACKET_LENGTH) { - net->error=1; - net->last_errno=ER_NET_PACKET_TOO_LARGE; - return 1; + /* Take into account that we have the command in the first header */ + len= MAX_PACKET_LENGTH -1; + do + { + int3store(buff, MAX_PACKET_LENGTH); + buff[3]= (uchar) net->pkt_nr++; + if (net_write_buff(net,(char*) buff, header_size) || + net_write_buff(net,packet,len)) + DBUG_RETURN(1); + packet+= len; + length-= MAX_PACKET_LENGTH; + len= MAX_PACKET_LENGTH; + header_size= NET_HEADER_SIZE; + } while (length >= MAX_PACKET_LENGTH); + len=length; /* Data left to be written */ } int3store(buff,length); - buff[3]= (net->compress) ? 0 : (uchar) (net->pkt_nr++); - buff[4]=command; - if (net_write_buff(net,(char*) buff,5)) - return 1; - return test(net_write_buff(net,packet,len) || net_flush(net)); + buff[3]= (uchar) net->pkt_nr++; + DBUG_RETURN(test(net_write_buff(net,(char*) buff,header_size) || + net_write_buff(net,packet,len) || net_flush(net))); } +/* + Caching the data in a local buffer before sending it. + + SYNOPSIS + net_write_buff() + net Network handler + packet Packet to send + len Length of packet -static int -net_write_buff(NET *net,const char *packet,uint len) + DESCRIPTION + Fill up net->buffer and send it to the client when full. + + If the rest of the to-be-sent-packet is bigger than buffer, + send it in one big block (to avoid copying to internal buffer). + If not, copy the rest of the data to the buffer and return without + sending data. + + NOTES + The cached buffer can be sent as it is with 'net_flush()'. + + In this code we have to be careful to not send a packet longer than + MAX_PACKET_LENGTH to net_real_write() if we are using the compressed protocol + as we store the length of the compressed packet in 3 bytes. + + RETURN + 0 ok + 1 +*/ + +static my_bool +net_write_buff(NET *net,const char *packet,ulong len) { - uint left_length=(uint) (net->buff_end - net->write_pos); + ulong left_length; + if (net->compress && net->max_packet > MAX_PACKET_LENGTH) + left_length= MAX_PACKET_LENGTH - (net->write_pos - net->buff); + else + left_length= (ulong) (net->buff_end - net->write_pos); - while (len > left_length) + if (len > left_length) { - memcpy((char*) net->write_pos,packet,left_length); - if (net_real_write(net,(char*) net->buff,net->max_packet)) - return 1; - net->write_pos=net->buff; - packet+=left_length; - len-=left_length; - left_length=net->max_packet; + if (net->write_pos != net->buff) + { + /* Fill up already used packet and write it */ + memcpy((char*) net->write_pos,packet,left_length); + if (net_real_write(net,(char*) net->buff, + (ulong) (net->write_pos - net->buff) + left_length)) + return 1; + net->write_pos= net->buff; + packet+= left_length; + len-= left_length; + } + if (net->compress) + { + /* + We can't have bigger packets than 16M with compression + Because the uncompressed length is stored in 3 bytes + */ + left_length= MAX_PACKET_LENGTH; + while (len > left_length) + { + if (net_real_write(net, packet, left_length)) + return 1; + packet+= left_length; + len-= left_length; + } + } + if (len > net->max_packet) + return net_real_write(net, packet, len) ? 1 : 0; + /* Send out rest of the blocks as full sized blocks */ } memcpy((char*) net->write_pos,packet,len); - net->write_pos+=len; + net->write_pos+= len; return 0; } -/* Read and write using timeouts */ + +/* + Read and write one packet using timeouts. + If needed, the packet is compressed before sending. +*/ int net_real_write(NET *net,const char *packet,ulong len) { - int length; + long int length; char *pos,*end; thr_alarm_t alarmed; -#if !defined(__WIN__) && !defined(__EMX__) && !defined(OS2) +#ifndef NO_ALARM ALARM alarm_buff; #endif uint retry_count=0; my_bool net_blocking = vio_is_blocking(net->vio); DBUG_ENTER("net_real_write"); +#if defined(MYSQL_SERVER) && defined(HAVE_QUERY_CACHE) + if (net->query_cache_query != 0) + query_cache_insert(net, packet, len); +#endif + if (net->error == 2) DBUG_RETURN(-1); /* socket can't be used */ @@ -309,8 +398,8 @@ net_real_write(NET *net,const char *packet,ulong len) ulong complen; uchar *b; uint header_length=NET_HEADER_SIZE+COMP_HEADER_SIZE; - if (!(b=(uchar*) my_malloc(len + NET_HEADER_SIZE + COMP_HEADER_SIZE, - MYF(MY_WME)))) + if (!(b=(uchar*) my_malloc((uint32) len + NET_HEADER_SIZE + + COMP_HEADER_SIZE, MYF(MY_WME)))) { #ifdef MYSQL_SERVER net->last_errno=ER_OUT_OF_RESOURCES; @@ -322,53 +411,50 @@ net_real_write(NET *net,const char *packet,ulong len) memcpy(b+header_length,packet,len); if (my_compress((byte*) b+header_length,&len,&complen)) - { - DBUG_PRINT("warning", - ("Compression error; Continuing without compression")); complen=0; - } int3store(&b[NET_HEADER_SIZE],complen); int3store(b,len); - b[3]=(uchar) (net->pkt_nr++); + b[3]=(uchar) (net->compress_pkt_nr++); len+= header_length; packet= (char*) b; } #endif /* HAVE_COMPRESS */ /* DBUG_DUMP("net",packet,len); */ -#ifdef MYSQL_SERVER +#ifndef NO_ALARM thr_alarm_init(&alarmed); if (net_blocking) - thr_alarm(&alarmed,(uint) net_write_timeout,&alarm_buff); + thr_alarm(&alarmed,(uint) net->write_timeout,&alarm_buff); #else alarmed=0; -#endif /* MYSQL_SERVER */ +#endif /* NO_ALARM */ pos=(char*) packet; end=pos+len; while (pos != end) { - if ((int) (length=vio_write(net->vio,pos,(int) (end-pos))) <= 0) + if ((long) (length=vio_write(net->vio,pos,(uint32) (end-pos))) <= 0) { my_bool interrupted = vio_should_retry(net->vio); #if (!defined(__WIN__) && !defined(__EMX__) && !defined(OS2)) if ((interrupted || length==0) && !thr_alarm_in_use(&alarmed)) { - if (!thr_alarm(&alarmed,(uint) net_write_timeout,&alarm_buff)) + if (!thr_alarm(&alarmed,(uint) net->write_timeout,&alarm_buff)) { /* Always true for client */ - if (!vio_is_blocking(net->vio)) + my_bool old_mode; + while (vio_blocking(net->vio, TRUE, &old_mode) < 0) { - while (vio_blocking(net->vio, TRUE) < 0) - { - if (vio_should_retry(net->vio) && retry_count++ < RETRY_COUNT) - continue; + if (vio_should_retry(net->vio) && retry_count++ < net->retry_count) + continue; #ifdef EXTRA_DEBUG - fprintf(stderr, - "%s: my_net_write: fcntl returned error %d, aborting thread\n", - my_progname,vio_errno(net->vio)); + fprintf(stderr, + "%s: my_net_write: fcntl returned error %d, aborting thread\n", + my_progname,vio_errno(net->vio)); #endif /* EXTRA_DEBUG */ - net->error=2; /* Close socket */ - goto end; - } +#ifdef MYSQL_SERVER + net->last_errno=ER_NET_ERROR_ON_WRITE; +#endif + net->error=2; /* Close socket */ + goto end; } retry_count=0; continue; @@ -379,7 +465,7 @@ net_real_write(NET *net,const char *packet,ulong len) if (thr_alarm_in_use(&alarmed) && !thr_got_alarm(&alarmed) && interrupted) { - if (retry_count++ < RETRY_COUNT) + if (retry_count++ < net->retry_count) continue; #ifdef EXTRA_DEBUG fprintf(stderr, "%s: write looped, aborting thread\n", @@ -412,8 +498,9 @@ net_real_write(NET *net,const char *packet,ulong len) #endif if (thr_alarm_in_use(&alarmed)) { + my_bool old_mode; thr_end_alarm(&alarmed); - vio_blocking(net->vio, net_blocking); + vio_blocking(net->vio, net_blocking, &old_mode); } net->reading_or_writing=0; DBUG_RETURN(((int) (pos != end))); @@ -424,44 +511,88 @@ net_real_write(NET *net,const char *packet,ulong len) ** Read something from server/clinet *****************************************************************************/ -#ifdef MYSQL_SERVER +#ifndef NO_ALARM + +static my_bool net_safe_read(NET *net, char *buff, uint32 length, + thr_alarm_t *alarmed) +{ + uint retry_count=0; + while (length > 0) + { + int tmp; + if ((tmp=vio_read(net->vio,(char*) net->buff, length)) <= 0) + { + my_bool interrupted = vio_should_retry(net->vio); + if (!thr_got_alarm(&alarmed) && interrupted) + { /* Probably in MIT threads */ + if (retry_count++ < net->retry_count) + continue; + } + return 1; + } + length-= tmp; + } + return 0; +} /* - Help function to clear the commuication buffer when we get a too - big packet + Help function to clear the commuication buffer when we get a too big packet. + + SYNOPSIS + my_net_skip_rest() + net Communication handle + remain Bytes to read + alarmed Parameter for thr_alarm() + alarm_buff Parameter for thr_alarm() + + RETURN VALUES + 0 Was able to read the whole packet + 1 Got mailformed packet from client */ -static void my_net_skip_rest(NET *net, ulong remain, thr_alarm_t *alarmed) +static my_bool my_net_skip_rest(NET *net, uint32 remain, thr_alarm_t *alarmed, + ALARM *alarm_buff) { - ALARM alarm_buff; - uint retry_count=0; + uint32 old=remain; + DBUG_ENTER("my_net_skip_rest"); + DBUG_PRINT("enter",("bytes_to_skip: %u", (uint) remain)); + if (!thr_alarm_in_use(&alarmed)) { - if (!thr_alarm(alarmed,net->timeout,&alarm_buff) || - (!vio_is_blocking(net->vio) && vio_blocking(net->vio,TRUE) < 0)) - return; /* Can't setup, abort */ + my_bool old_mode; + if (!thr_alarm(alarmed,net->read_timeout, alarm_buff) || + vio_blocking(net->vio, TRUE, &old_mode) < 0) + DBUG_RETURN(1); /* Can't setup, abort */ } - while (remain > 0) + for (;;) { - ulong length; - if ((int) (length=vio_read(net->vio,(char*) net->buff,remain)) <= 0L) + while (remain > 0) { - my_bool interrupted = vio_should_retry(net->vio); - if (!thr_got_alarm(alarmed) && interrupted) - { /* Probably in MIT threads */ - if (retry_count++ < RETRY_COUNT) - continue; - } - return; + uint length= min(remain, net->max_packet); + if (net_safe_read(net, (char*) net->buff, length, alarmed)) + DBUG_RETURN(1); + statistic_add(bytes_received, length, &LOCK_bytes_received); + remain -= (uint32) length; } - remain -=(ulong) length; - statistic_add(bytes_received,(ulong) length,&LOCK_bytes_received); + if (old != MAX_PACKET_LENGTH) + break; + if (net_safe_read(net, (char*) net->buff, NET_HEADER_SIZE, alarmed)) + DBUG_RETURN(1); + old=remain= uint3korr(net->buff); + net->pkt_nr++; } + DBUG_RETURN(0); } -#endif /* MYSQL_SERVER */ +#endif /* NO_ALARM */ -static uint +/* + Reads one packet to net->buff + net->where_b + Returns length of packet. Long packets are handled by my_net_read(). + This function reallocates the net->buff buffer if necessary. +*/ + +static ulong my_real_read(NET *net, ulong *complen) { uchar *pos; @@ -469,20 +600,20 @@ my_real_read(NET *net, ulong *complen) uint i,retry_count=0; ulong len=packet_error; thr_alarm_t alarmed; -#if (!defined(__WIN__) && !defined(__EMX__) && !defined(OS2)) || defined(MYSQL_SERVER) +#ifndef NO_ALARM ALARM alarm_buff; #endif my_bool net_blocking=vio_is_blocking(net->vio); - ulong remain= (net->compress ? NET_HEADER_SIZE+COMP_HEADER_SIZE : - NET_HEADER_SIZE); + uint32 remain= (net->compress ? NET_HEADER_SIZE+COMP_HEADER_SIZE : + NET_HEADER_SIZE); *complen = 0; net->reading_or_writing=1; thr_alarm_init(&alarmed); -#ifdef MYSQL_SERVER +#ifndef NO_ALARM if (net_blocking) - thr_alarm(&alarmed,net->timeout,&alarm_buff); -#endif /* MYSQL_SERVER */ + thr_alarm(&alarmed,net->read_timeout,&alarm_buff); +#endif /* NO_ALARM */ pos = net->buff + net->where_b; /* net->packet -4 */ for (i=0 ; i < 2 ; i++) @@ -504,31 +635,29 @@ my_real_read(NET *net, ulong *complen) */ if ((interrupted || length == 0) && !thr_alarm_in_use(&alarmed)) { - if (!thr_alarm(&alarmed,net->timeout,&alarm_buff)) /* Don't wait too long */ + if (!thr_alarm(&alarmed,net->read_timeout,&alarm_buff)) /* Don't wait too long */ { - if (!vio_is_blocking(net->vio)) - { - while (vio_blocking(net->vio,TRUE) < 0) - { - if (vio_should_retry(net->vio) && - retry_count++ < RETRY_COUNT) - continue; - DBUG_PRINT("error", - ("fcntl returned error %d, aborting thread", - vio_errno(net->vio))); + my_bool old_mode; + while (vio_blocking(net->vio, TRUE, &old_mode) < 0) + { + if (vio_should_retry(net->vio) && + retry_count++ < net->retry_count) + continue; + DBUG_PRINT("error", + ("fcntl returned error %d, aborting thread", + vio_errno(net->vio))); #ifdef EXTRA_DEBUG - fprintf(stderr, - "%s: read: fcntl returned error %d, aborting thread\n", - my_progname,vio_errno(net->vio)); + fprintf(stderr, + "%s: read: fcntl returned error %d, aborting thread\n", + my_progname,vio_errno(net->vio)); #endif /* EXTRA_DEBUG */ - len= packet_error; - net->error=2; /* Close socket */ + len= packet_error; + net->error=2; /* Close socket */ #ifdef MYSQL_SERVER - net->last_errno=ER_NET_FCNTL_ERROR; + net->last_errno=ER_NET_FCNTL_ERROR; #endif - goto end; - } - } + goto end; + } retry_count=0; continue; } @@ -537,7 +666,7 @@ my_real_read(NET *net, ulong *complen) if (thr_alarm_in_use(&alarmed) && !thr_got_alarm(&alarmed) && interrupted) { /* Probably in MIT threads */ - if (retry_count++ < RETRY_COUNT) + if (retry_count++ < net->retry_count) continue; #ifdef EXTRA_DEBUG fprintf(stderr, "%s: read looped with error %d, aborting thread\n", @@ -551,7 +680,8 @@ my_real_read(NET *net, ulong *complen) continue; } #endif - DBUG_PRINT("error",("Couldn't read packet: remain: %d errno: %d length: %d alarmed: %d", remain,vio_errno(net->vio),length,alarmed)); + DBUG_PRINT("error",("Couldn't read packet: remain: %u errno: %d length: %ld", + remain, vio_errno(net->vio), length)); len= packet_error; net->error=2; /* Close socket */ #ifdef MYSQL_SERVER @@ -560,7 +690,7 @@ my_real_read(NET *net, ulong *complen) #endif goto end; } - remain -= (ulong) length; + remain -= (uint32) length; pos+= (ulong) length; statistic_add(bytes_received,(ulong) length,&LOCK_bytes_received); } @@ -572,9 +702,10 @@ my_real_read(NET *net, ulong *complen) if (net->buff[net->where_b] != (uchar) 255) { DBUG_PRINT("error", - ("Packets out of order (Found: %d, expected %d)", + ("Packets out of order (Found: %d, expected %u)", (int) net->buff[net->where_b + 3], - (uint) (uchar) net->pkt_nr)); + net->pkt_nr)); + DBUG_DUMP("packet_header",(char*) net->buff+net->where_b, 4); #ifdef EXTRA_DEBUG fprintf(stderr,"Packets out of order (Found: %d, expected %d)\n", (int) net->buff[net->where_b + 3], @@ -587,47 +718,67 @@ my_real_read(NET *net, ulong *complen) #endif goto end; } - net->pkt_nr++; + net->compress_pkt_nr= ++net->pkt_nr; #ifdef HAVE_COMPRESS if (net->compress) { - /* complen is > 0 if package is really compressed */ + /* + If the packet is compressed then complen > 0 and contains the + number of bytes in the uncompressed packet + */ *complen=uint3korr(&(net->buff[net->where_b + NET_HEADER_SIZE])); } #endif len=uint3korr(net->buff+net->where_b); + if (!len) /* End of big multi-packet */ + goto end; helping = max(len,*complen) + net->where_b; /* The necessary size of net->buff */ if (helping >= net->max_packet) { - /* We must allocate one extra byte for the end null */ - if (net_realloc(net,helping+1)) + if (net_realloc(net,helping)) { -#ifdef MYSQL_SERVER - if (i == 1) - my_net_skip_rest(net, len, &alarmed); +#if defined(MYSQL_SERVER) && !defined(NO_ALARM) + if (!net->compress && + !my_net_skip_rest(net, (uint32) len, &alarmed, &alarm_buff)) + net->error= 3; /* Successfully skiped packet */ #endif - len= packet_error; /* Return error */ + len= packet_error; /* Return error and close connection */ goto end; } } pos=net->buff + net->where_b; - remain = len; + remain = (uint32) len; } } end: if (thr_alarm_in_use(&alarmed)) { + my_bool old_mode; thr_end_alarm(&alarmed); - vio_blocking(net->vio, net_blocking); + vio_blocking(net->vio, net_blocking, &old_mode); } net->reading_or_writing=0; return(len); } -uint + +/* + Read a packet from the client/server and return it without the internal + package header. + If the packet is the first packet of a multi-packet packet + (which is indicated by the length of the packet = 0xffffff) then + all sub packets are read and concatenated. + If the packet was compressed, its uncompressed and the length of the + uncompressed packet is returned. + + The function returns the length of the found packet or packet_error. + net->read_pos points to the read data. +*/ + +ulong my_net_read(NET *net) { ulong len,complen; @@ -636,65 +787,136 @@ my_net_read(NET *net) if (!net->compress) { #endif - len = my_real_read (net,&complen); + len = my_real_read(net,&complen); + if (len == MAX_PACKET_LENGTH) + { + /* First packet of a multi-packet. Concatenate the packets */ + ulong save_pos = net->where_b; + ulong total_length=0; + do + { + net->where_b += len; + total_length += len; + len = my_real_read(net,&complen); + } while (len == MAX_PACKET_LENGTH); + if (len != packet_error) + len+= total_length; + net->where_b = save_pos; + } net->read_pos = net->buff + net->where_b; if (len != packet_error) net->read_pos[len]=0; /* Safeguard for mysql_use_result */ return len; #ifdef HAVE_COMPRESS } - if (net->remain_in_buf) - net->buff[net->buf_length - net->remain_in_buf]=net->save_char; - for (;;) + else { + /* We are using the compressed protocol */ + + ulong buf_length= net->buf_length; + ulong start_of_packet= net->buf_length - net->remain_in_buf; + ulong first_packet_offset=start_of_packet; + uint read_length, multi_byte_packet=0; + if (net->remain_in_buf) { - uchar *pos = net->buff + net->buf_length - net->remain_in_buf; - if (net->remain_in_buf >= 4) + /* Restore the character that was overwritten by the end 0 */ + net->buff[start_of_packet]=net->save_char; + } + else + { + /* reuse buffer, as there is nothing in it that we need */ + buf_length=start_of_packet=first_packet_offset=0; + } + for (;;) + { + ulong packet_len; + + if (buf_length - start_of_packet >= NET_HEADER_SIZE) { - net->length = uint3korr(pos); - if (net->length <= net->remain_in_buf - 4) + read_length = uint3korr(net->buff+start_of_packet); + if (!read_length) + { + /* End of multi-byte packet */ + start_of_packet += NET_HEADER_SIZE; + break; + } + if (read_length + NET_HEADER_SIZE <= buf_length - start_of_packet) { - /* We have a full packet */ - len=net->length; - net->remain_in_buf -= net->length + 4; - net->read_pos=pos + 4; - break; /* We have a full packet */ + if (multi_byte_packet) + { + /* Remove packet header for second packet */ + memmove(net->buff + first_packet_offset + start_of_packet, + net->buff + first_packet_offset + start_of_packet + + NET_HEADER_SIZE, + buf_length - start_of_packet); + start_of_packet += read_length; + buf_length -= NET_HEADER_SIZE; + } + else + start_of_packet+= read_length + NET_HEADER_SIZE; + + if (read_length != MAX_PACKET_LENGTH) /* last package */ + { + multi_byte_packet= 0; /* No last zero len packet */ + break; + } + multi_byte_packet= NET_HEADER_SIZE; + /* Move data down to read next data packet after current one */ + if (first_packet_offset) + { + memmove(net->buff,net->buff+first_packet_offset, + buf_length-first_packet_offset); + buf_length-=first_packet_offset; + start_of_packet -= first_packet_offset; + first_packet_offset=0; + } + continue; } } /* Move data down to read next data packet after current one */ - if (net->buf_length != net->remain_in_buf) + if (first_packet_offset) { - memmove(net->buff,pos,net->remain_in_buf); - net->buf_length=net->remain_in_buf; + memmove(net->buff,net->buff+first_packet_offset, + buf_length-first_packet_offset); + buf_length-=first_packet_offset; + start_of_packet -= first_packet_offset; + first_packet_offset=0; } - net->where_b=net->buf_length; - } - else - { - net->where_b=0; - net->buf_length=0; - } - if ((len = my_real_read(net,&complen)) == packet_error) - break; - if (my_uncompress((byte*) net->buff + net->where_b, &len, &complen)) - { - len= packet_error; - net->error=2; /* caller will close socket */ + net->where_b=buf_length; + if ((packet_len = my_real_read(net,&complen)) == packet_error) + return packet_error; + if (my_uncompress((byte*) net->buff + net->where_b, &packet_len, + &complen)) + { + net->error=2; /* caller will close socket */ #ifdef MYSQL_SERVER - net->last_errno=ER_NET_UNCOMPRESS_ERROR; + net->last_errno=ER_NET_UNCOMPRESS_ERROR; #endif - break; + return packet_error; + } + buf_length+=packet_len; } - net->buf_length+=len; - net->remain_in_buf+=len; - } - if (len != packet_error) - { + + net->read_pos= net->buff+ first_packet_offset + NET_HEADER_SIZE; + net->buf_length= buf_length; + net->remain_in_buf= (ulong) (buf_length - start_of_packet); + len = ((ulong) (start_of_packet - first_packet_offset) - NET_HEADER_SIZE - + multi_byte_packet); net->save_char= net->read_pos[len]; /* Must be saved */ net->read_pos[len]=0; /* Safeguard for mysql_use_result */ } +#endif /* HAVE_COMPRESS */ return len; -#endif +} + +bool net_request_file(NET* net, const char* fname) +{ + char tmp [FN_REFLEN+1],*end; + DBUG_ENTER("net_request_file"); + tmp[0] = (char) 251; /* NULL_LENGTH */ + end=strnmov(tmp+1,fname,sizeof(tmp)-2); + DBUG_RETURN(my_net_write(net,tmp,(uint) (end-tmp)) || + net_flush(net)); } diff --git a/sql/opt_ft.h b/sql/opt_ft.h index dcbbb8abcec..b055edc107c 100644 --- a/sql/opt_ft.h +++ b/sql/opt_ft.h @@ -29,7 +29,7 @@ public: TABLE_REF *ref; FT_SELECT(TABLE *table, TABLE_REF *tref) : - QUICK_SELECT (table,tref->key,1), ref(tref) {} + QUICK_SELECT (table,tref->key,1), ref(tref) { init(); } int init() { return error=file->ft_init(); } int get_next() { return error=file->ft_read(record); } diff --git a/sql/opt_range.cc b/sql/opt_range.cc index b2128c3eb4a..c1b03ed629f 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -23,8 +23,6 @@ */ - - #ifdef __GNUC__ #pragma implementation // gcc: Class implementation #endif @@ -33,6 +31,7 @@ #include <m_ctype.h> #include <nisam.h> #include "sql_select.h" +#include <assert.h> #ifndef EXTRA_DEBUG @@ -66,7 +65,9 @@ public: SEL_ARG(Field *field, uint8 part, char *min_value, char *max_value, uint8 min_flag, uint8 max_flag, uint8 maybe_flag); SEL_ARG(enum Type type_arg) - :elements(1),use_count(1),left(0),next_key_part(0),type(type_arg) {} + :elements(1),use_count(1),left(0),next_key_part(0),color(BLACK), + type(type_arg) + {} inline bool is_same(SEL_ARG *arg) { if (type != arg->type) @@ -279,21 +280,21 @@ public: typedef struct st_qsel_param { - uint baseflag,keys,max_key_part; - table_map prev_tables,read_tables,current_table; TABLE *table; - bool quick; // Don't calulate possible keys KEY_PART *key_parts,*key_parts_end,*key[MAX_KEY]; + MEM_ROOT *mem_root; + table_map prev_tables,read_tables,current_table; + uint baseflag,keys,max_key_part; uint real_keynr[MAX_KEY]; char min_key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH], max_key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH]; + bool quick; // Don't calulate possible keys } PARAM; - static SEL_TREE * get_mm_parts(PARAM *param,Field *field, Item_func::Functype type,Item *value, Item_result cmp_type); -static SEL_ARG *get_mm_leaf(Field *field,KEY_PART *key_part, +static SEL_ARG *get_mm_leaf(PARAM *param,Field *field,KEY_PART *key_part, Item_func::Functype type,Item *value); static bool like_range(const char *ptr,uint length,char wild_prefix, uint field_length, char *min_str,char *max_str, @@ -382,7 +383,7 @@ SQL_SELECT::~SQL_SELECT() #undef index // Fix for Unixware 7 QUICK_SELECT::QUICK_SELECT(TABLE *table,uint key_nr,bool no_alloc) - :error(0),index(key_nr),max_used_key_length(0),head(table), + :dont_free(0),error(0),index(key_nr),max_used_key_length(0),head(table), it(ranges),range(0) { if (!no_alloc) @@ -399,13 +400,11 @@ QUICK_SELECT::QUICK_SELECT(TABLE *table,uint key_nr,bool no_alloc) QUICK_SELECT::~QUICK_SELECT() { - file->index_end(); - free_root(&alloc,MYF(0)); -} - -int QUICK_SELECT::init() -{ - return error=file->index_init(index); + if (!dont_free) + { + file->index_end(); + free_root(&alloc,MYF(0)); + } } QUICK_RANGE::QUICK_RANGE() @@ -506,10 +505,11 @@ SEL_ARG *SEL_ARG::last() return next_arg; } + /* Check if a compare is ok, when one takes ranges in account Returns -2 or 2 if the ranges where 'joined' like < 2 and >= 2 - */ +*/ static int sel_cmp(Field *field, char *a,char *b,uint8 a_flag,uint8 b_flag) { @@ -533,7 +533,7 @@ static int sel_cmp(Field *field, char *a,char *b,uint8 a_flag,uint8 b_flag) } if (*a) goto end; // NULL where equal - a++; b++; // Skipp NULL marker + a++; b++; // Skip NULL marker } cmp=field->key_cmp((byte*) a,(byte*) b); if (cmp) return cmp < 0 ? -1 : 1; // The values differed @@ -586,6 +586,9 @@ int SQL_SELECT::test_quick_select(key_map keys_to_use, table_map prev_tables, uint idx; double scan_time; DBUG_ENTER("test_quick_select"); + DBUG_PRINT("enter",("keys_to_use: %lu prev_tables: %lu const_tables: %lu", + (ulong) keys_to_use, (ulong) prev_tables, + (ulong) const_tables)); delete quick; quick=0; @@ -593,7 +596,7 @@ int SQL_SELECT::test_quick_select(key_map keys_to_use, table_map prev_tables, if (!cond || (specialflag & SPECIAL_SAFE_MODE) && ! force_quick_range || !limit) DBUG_RETURN(0); /* purecov: inspected */ - if (!((basflag= head->file->option_flag()) & HA_KEYPOS_TO_RNDPOS) && + if (!((basflag= head->file->table_flags()) & HA_KEYPOS_TO_RNDPOS) && keys_to_use == (uint) ~0 || !keys_to_use) DBUG_RETURN(0); /* Not smart database */ records=head->file->records; @@ -601,12 +604,14 @@ int SQL_SELECT::test_quick_select(key_map keys_to_use, table_map prev_tables, records++; /* purecov: inspected */ scan_time=(double) records / TIME_FOR_COMPARE+1; read_time=(double) head->file->scan_time()+ scan_time + 1.0; + if (head->force_index) + scan_time= read_time= DBL_MAX; if (limit < records) read_time=(double) records+scan_time+1; // Force to use index else if (read_time <= 2.0 && !force_quick_range) - DBUG_RETURN(0); /* No nead for quick select */ + DBUG_RETURN(0); /* No need for quick select */ - DBUG_PRINT("info",("Time to scan table: %ld",(long) read_time)); + DBUG_PRINT("info",("Time to scan table: %g", read_time)); keys_to_use&=head->keys_in_use_for_query; if (keys_to_use) @@ -623,6 +628,7 @@ int SQL_SELECT::test_quick_select(key_map keys_to_use, table_map prev_tables, param.current_table= head->map; param.table=head; param.keys=0; + param.mem_root= &alloc; current_thd->no_errors=1; // Don't warn about NULL init_sql_alloc(&alloc,2048,0); @@ -679,27 +685,27 @@ int SQL_SELECT::test_quick_select(key_map keys_to_use, table_map prev_tables, { ha_rows found_records; double found_read_time; - if (*key) { + uint keynr= param.real_keynr[idx]; if ((*key)->type == SEL_ARG::MAYBE_KEY || (*key)->maybe_flag) - needed_reg|= (key_map) 1 << param.real_keynr[idx]; + needed_reg|= (key_map) 1 << keynr; - found_records=check_quick_select(¶m,idx, *key); + found_records=check_quick_select(¶m, idx, *key); if (found_records != HA_POS_ERROR && found_records > 2 && - head->used_keys & ((table_map) 1 << param.real_keynr[idx]) && - (head->file->option_flag() & HA_HAVE_KEY_READ_ONLY)) + head->used_keys & ((table_map) 1 << keynr) && + (head->file->index_flags(keynr) & HA_KEY_READ_ONLY)) { /* - ** We can resolve this by only reading through this key - ** Assume that we will read trough the whole key range - ** and that all key blocks are half full (normally things are - ** much better) + We can resolve this by only reading through this key. + Assume that we will read trough the whole key range + and that all key blocks are half full (normally things are + much better). */ - uint keys_per_block= head->file->block_size/2/ - (head->key_info[param.real_keynr[idx]].key_length+ - head->file->ref_length) + 1; + uint keys_per_block= (head->file->block_size/2/ + (head->key_info[keynr].key_length+ + head->file->ref_length) + 1); found_read_time=((double) (found_records+keys_per_block-1)/ (double) keys_per_block); } @@ -882,7 +888,7 @@ get_mm_parts(PARAM *param,Field *field, Item_func::Functype type,Item *value, if (value && value->used_tables() & ~(param->prev_tables | param->read_tables)) DBUG_RETURN(0); - for ( ; key_part != end ; key_part++) + for (; key_part != end ; key_part++) { if (field->eq(key_part->field)) { @@ -891,7 +897,7 @@ get_mm_parts(PARAM *param,Field *field, Item_func::Functype type,Item *value, tree=new SEL_TREE(); if (!value || !(value->used_tables() & ~param->read_tables)) { - sel_arg=get_mm_leaf(key_part->field,key_part,type,value); + sel_arg=get_mm_leaf(param,key_part->field,key_part,type,value); if (!sel_arg) continue; if (sel_arg->type == SEL_ARG::IMPOSSIBLE) @@ -911,7 +917,7 @@ get_mm_parts(PARAM *param,Field *field, Item_func::Functype type,Item *value, static SEL_ARG * -get_mm_leaf(Field *field,KEY_PART *key_part, +get_mm_leaf(PARAM *param, Field *field, KEY_PART *key_part, Item_func::Functype type,Item *value) { uint maybe_null=(uint) field->real_maybe_null(); @@ -926,7 +932,7 @@ get_mm_leaf(Field *field,KEY_PART *key_part, String tmp(buff1,sizeof(buff1)),*res; uint length,offset,min_length,max_length; - if (!field->optimize_range()) + if (!field->optimize_range((uint) key_part->key)) DBUG_RETURN(0); // Can't optimize this if (!(res= value->val_str(&tmp))) DBUG_RETURN(&null_element); @@ -959,7 +965,7 @@ get_mm_leaf(Field *field,KEY_PART *key_part, field_length=length; } length+=offset; - if (!(min_str= (char*) sql_alloc(length*2))) + if (!(min_str= (char*) alloc_root(param->mem_root, length*2))) DBUG_RETURN(0); max_str=min_str+length; if (maybe_null) @@ -1010,7 +1016,8 @@ get_mm_leaf(Field *field,KEY_PART *key_part, DBUG_RETURN(tree); } - if (!field->optimize_range() && type != Item_func::EQ_FUNC && + if (!field->optimize_range((uint) key_part->key) && + type != Item_func::EQ_FUNC && type != Item_func::EQUAL_FUNC) DBUG_RETURN(0); // Can't optimize this @@ -1023,22 +1030,14 @@ get_mm_leaf(Field *field,KEY_PART *key_part, field->cmp_type() != value->result_type()) DBUG_RETURN(0); - if (value->save_in_field(field)) + if (value->save_in_field(field, 1)) { /* This happens when we try to insert a NULL field in a not null column */ - if (type == Item_func::EQUAL_FUNC) - { - /* convert column_name <=> NULL -> column_name IS NULL */ - char *str= (char*) sql_alloc(1); // Get local copy of key - if (!str) - DBUG_RETURN(0); - *str = 1; - DBUG_RETURN(new SEL_ARG(field,str,str)); - } DBUG_RETURN(&null_element); // cmp with NULL is never true } // Get local copy of key - char *str= (char*) sql_alloc(key_part->part_length+maybe_null); + char *str= (char*) alloc_root(param->mem_root, + key_part->part_length+maybe_null); if (!str) DBUG_RETURN(0); if (maybe_null) @@ -1104,7 +1103,7 @@ static bool like_range(const char *ptr,uint ptr_length,char escape, { if (*ptr == escape && ptr+1 != end) { - ptr++; // Skipp escape + ptr++; // Skip escape *min_str++= *max_str++ = *ptr; continue; } @@ -2239,7 +2238,7 @@ get_quick_select(PARAM *param,uint idx,SEL_ARG *key_tree) else { quick->key_parts=(KEY_PART*) - sql_memdup(param->key[idx], + memdup_root(&quick->alloc,(char*) param->key[idx], sizeof(KEY_PART)* param->table->key_info[param->real_keynr[idx]].key_parts); } @@ -2410,7 +2409,7 @@ QUICK_SELECT *get_quick_select_for_ref(TABLE *table, TABLE_REF *ref) (key_info->flags & HA_NOSAME)) ? EQ_RANGE : 0); if (!(quick->key_parts=key_part=(KEY_PART *) - sql_alloc(sizeof(KEY_PART)*ref->key_parts))) + alloc_root(&quick->alloc,sizeof(KEY_PART)*ref->key_parts))) goto err; for (part=0 ; part < ref->key_parts ;part++,key_part++) @@ -2458,12 +2457,12 @@ int QUICK_SELECT::get_next() DBUG_RETURN(HA_ERR_END_OF_FILE); // All ranges used if (range->flag & NO_MIN_RANGE) // Read first record { - int error; - if ((error=file->index_first(record))) - DBUG_RETURN(error); // Empty table + int local_error; + if ((local_error=file->index_first(record))) + DBUG_RETURN(local_error); // Empty table if (cmp_next(range) == 0) - DBUG_RETURN(0); // No matching records - range=0; // To next range + DBUG_RETURN(0); + range=0; // No matching records; go to next range continue; } if ((result = file->index_read(record,(byte*) range->min_key, @@ -2493,13 +2492,13 @@ int QUICK_SELECT::get_next() /* compare if found key is over max-value */ /* Returns 0 if key <= range->max_key */ -int QUICK_SELECT::cmp_next(QUICK_RANGE *range) +int QUICK_SELECT::cmp_next(QUICK_RANGE *range_arg) { - if (range->flag & NO_MAX_RANGE) - return (0); /* key can't be to large */ + if (range_arg->flag & NO_MAX_RANGE) + return 0; /* key can't be to large */ KEY_PART *key_part=key_parts; - for (char *key=range->max_key, *end=key+range->max_length; + for (char *key=range_arg->max_key, *end=key+range_arg->max_length; key < end; key+= key_part++->part_length) { @@ -2520,9 +2519,240 @@ int QUICK_SELECT::cmp_next(QUICK_RANGE *range) if (cmp > 0) return 1; } - return (range->flag & NEAR_MAX) ? 1 : 0; // Exact match + return (range_arg->flag & NEAR_MAX) ? 1 : 0; // Exact match +} + + +/* + This is a hack: we inherit from QUICK_SELECT so that we can use the + get_next() interface, but we have to hold a pointer to the original + QUICK_SELECT because its data are used all over the place. What + should be done is to factor out the data that is needed into a base + class (QUICK_SELECT), and then have two subclasses (_ASC and _DESC) + which handle the ranges and implement the get_next() function. But + for now, this seems to work right at least. + */ + +QUICK_SELECT_DESC::QUICK_SELECT_DESC(QUICK_SELECT *q, uint used_key_parts) + : QUICK_SELECT(*q), rev_it(rev_ranges) +{ + bool not_read_after_key = file->table_flags() & HA_NOT_READ_AFTER_KEY; + QUICK_RANGE *r; + + it.rewind(); + for (r = it++; r; r = it++) + { + rev_ranges.push_front(r); + if (not_read_after_key && range_reads_after_key(r)) + { + it.rewind(); // Reset range + error = HA_ERR_UNSUPPORTED; + dont_free=1; // Don't free memory from 'q' + return; + } + } + /* Remove EQ_RANGE flag for keys that are not using the full key */ + for (r = rev_it++; r; r = rev_it++) + { + if ((r->flag & EQ_RANGE) && + head->key_info[index].key_length != r->max_length) + r->flag&= ~EQ_RANGE; + } + rev_it.rewind(); + q->dont_free=1; // Don't free shared mem + delete q; } + +int QUICK_SELECT_DESC::get_next() +{ + DBUG_ENTER("QUICK_SELECT_DESC::get_next"); + + /* The max key is handled as follows: + * - if there is NO_MAX_RANGE, start at the end and move backwards + * - if it is an EQ_RANGE, which means that max key covers the entire + * key, go directly to the key and read through it (sorting backwards is + * same as sorting forwards) + * - if it is NEAR_MAX, go to the key or next, step back once, and + * move backwards + * - otherwise (not NEAR_MAX == include the key), go after the key, + * step back once, and move backwards + */ + + for (;;) + { + int result; + if (range) + { // Already read through key + result = ((range->flag & EQ_RANGE) + ? file->index_next_same(record, (byte*) range->min_key, + range->min_length) : + file->index_prev(record)); + if (!result) + { + if (cmp_prev(*rev_it.ref()) == 0) + DBUG_RETURN(0); + } + else if (result != HA_ERR_END_OF_FILE) + DBUG_RETURN(result); + } + + if (!(range=rev_it++)) + DBUG_RETURN(HA_ERR_END_OF_FILE); // All ranges used + + if (range->flag & NO_MAX_RANGE) // Read last record + { + int local_error; + if ((local_error=file->index_last(record))) + DBUG_RETURN(local_error); // Empty table + if (cmp_prev(range) == 0) + DBUG_RETURN(0); + range=0; // No matching records; go to next range + continue; + } + + if (range->flag & EQ_RANGE) + { + result = file->index_read(record, (byte*) range->max_key, + range->max_length, HA_READ_KEY_EXACT); + } + else + { + /* Heikki changed Sept 11, 2002: since InnoDB does not store the cursor + position if READ_KEY_EXACT is used to a primary key with all + key columns specified, we must use below HA_READ_KEY_OR_NEXT, + so that InnoDB stores the cursor position and is able to move + the cursor one step backward after the search. */ + + DBUG_ASSERT(range->flag & NEAR_MAX || range_reads_after_key(range)); + /* Note: even if max_key is only a prefix, HA_READ_AFTER_KEY will + * do the right thing - go past all keys which match the prefix */ + result=file->index_read(record, (byte*) range->max_key, + range->max_length, + ((range->flag & NEAR_MAX) ? + HA_READ_KEY_OR_NEXT : HA_READ_AFTER_KEY)); + result = file->index_prev(record); + } + if (result) + { + if (result != HA_ERR_KEY_NOT_FOUND) + DBUG_RETURN(result); + range=0; // Not found, to next range + continue; + } + if (cmp_prev(range) == 0) + { + if (range->flag == (UNIQUE_RANGE | EQ_RANGE)) + range = 0; // Stop searching + DBUG_RETURN(0); // Found key is in range + } + range = 0; // To next range + } +} + + +/* + Returns 0 if found key is inside range (found key >= range->min_key). +*/ + +int QUICK_SELECT_DESC::cmp_prev(QUICK_RANGE *range_arg) +{ + if (range_arg->flag & NO_MIN_RANGE) + return 0; /* key can't be to small */ + + KEY_PART *key_part = key_parts; + for (char *key = range_arg->min_key, *end = key + range_arg->min_length; + key < end; + key += key_part++->part_length) + { + int cmp; + if (key_part->null_bit) + { + // this key part allows null values; NULL is lower than everything else + if (*key++) + { + // the range is expecting a null value + if (!key_part->field->is_null()) + return 0; // not null -- still inside the range + continue; // null -- exact match, go to next key part + } + else if (key_part->field->is_null()) + return 1; // null -- outside the range + } + if ((cmp = key_part->field->key_cmp((byte*) key, + key_part->part_length)) > 0) + return 0; + if (cmp < 0) + return 1; + } + return (range_arg->flag & NEAR_MIN) ? 1 : 0; // Exact match +} + + +/* + * True if this range will require using HA_READ_AFTER_KEY + See comment in get_next() about this + */ + +bool QUICK_SELECT_DESC::range_reads_after_key(QUICK_RANGE *range_arg) +{ + return ((range_arg->flag & (NO_MAX_RANGE | NEAR_MAX)) || + !(range_arg->flag & EQ_RANGE) || + head->key_info[index].key_length != range_arg->max_length) ? 1 : 0; +} + + +/* True if we are reading over a key that may have a NULL value */ + +#ifdef NOT_USED +bool QUICK_SELECT_DESC::test_if_null_range(QUICK_RANGE *range_arg, + uint used_key_parts) +{ + uint offset,end; + KEY_PART *key_part = key_parts, + *key_part_end= key_part+used_key_parts; + + for (offset= 0, end = min(range_arg->min_length, range_arg->max_length) ; + offset < end && key_part != key_part_end ; + offset += key_part++->part_length) + { + uint null_length=test(key_part->null_bit); + if (!memcmp((char*) range_arg->min_key+offset, + (char*) range_arg->max_key+offset, + key_part->part_length + null_length)) + { + offset+=null_length; + continue; + } + if (null_length && range_arg->min_key[offset]) + return 1; // min_key is null and max_key isn't + // Range doesn't cover NULL. This is ok if there is no more null parts + break; + } + /* + If the next min_range is > NULL, then we can use this, even if + it's a NULL key + Example: SELECT * FROM t1 WHERE a = 2 AND b >0 ORDER BY a DESC,b DESC; + + */ + if (key_part != key_part_end && key_part->null_bit) + { + if (offset >= range_arg->min_length || range_arg->min_key[offset]) + return 1; // Could be null + key_part++; + } + /* + If any of the key parts used in the ORDER BY could be NULL, we can't + use the key to sort the data. + */ + for (; key_part != key_part_end ; key_part++) + if (key_part->null_bit) + return 1; // Covers null part + return 0; +} +#endif + + /***************************************************************************** ** Print a quick range for debugging ** TODO: diff --git a/sql/opt_range.h b/sql/opt_range.h index 247dd260817..6a6b5ae3810 100644 --- a/sql/opt_range.h +++ b/sql/opt_range.h @@ -38,25 +38,34 @@ typedef struct st_key_part { Field *field; } KEY_PART; + class QUICK_RANGE :public Sql_alloc { public: char *min_key,*max_key; uint16 min_length,max_length,flag; +#ifdef HAVE_purify + uint16 dummy; /* Avoid warnings on 'flag' */ +#endif QUICK_RANGE(); /* Full range */ QUICK_RANGE(const char *min_key_arg,uint min_length_arg, const char *max_key_arg,uint max_length_arg, uint flag_arg) : min_key((char*) sql_memdup(min_key_arg,min_length_arg+1)), max_key((char*) sql_memdup(max_key_arg,max_length_arg+1)), - min_length(min_length_arg), - max_length(max_length_arg), - flag(flag_arg) - {} + min_length((uint16) min_length_arg), + max_length((uint16) max_length_arg), + flag((uint16) flag_arg) + { +#ifdef HAVE_purify + dummy=0; +#endif + } }; + class QUICK_SELECT { public: - bool next; + bool next,dont_free; int error; uint index,max_used_key_length; TABLE *head; @@ -74,12 +83,32 @@ public: QUICK_SELECT(TABLE *table,uint index_arg,bool no_alloc=0); virtual ~QUICK_SELECT(); void reset(void) { next=0; it.rewind(); } - virtual int init(); + int init() { return error=file->index_init(index); } virtual int get_next(); + virtual bool reverse_sorted() { return 0; } int cmp_next(QUICK_RANGE *range); bool unique_key_range(); }; + +class QUICK_SELECT_DESC: public QUICK_SELECT +{ +public: + QUICK_SELECT_DESC(QUICK_SELECT *q, uint used_key_parts); + int get_next(); + bool reverse_sorted() { return 1; } +private: + int cmp_prev(QUICK_RANGE *range); + bool range_reads_after_key(QUICK_RANGE *range); +#ifdef NOT_USED + bool test_if_null_range(QUICK_RANGE *range, uint used_key_parts); +#endif + void reset(void) { next=0; rev_it.rewind(); } + List<QUICK_RANGE> rev_ranges; + List_iterator<QUICK_RANGE> rev_it; +}; + + class SQL_SELECT :public Sql_alloc { public: QUICK_SELECT *quick; // If quick-select used diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index 9385e993859..aeaf8beef07 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -1,15 +1,15 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - +/* Copyright (C) 2000-2003 MySQL AB + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -22,28 +22,56 @@ static bool find_range_key(TABLE_REF *ref, Field* field,COND *cond); -/***************************************************************************** -** This function is only called for queries with sum functions and no -** GROUP BY part. -** This substitutes constants for some COUNT(), MIN() and MAX() functions. -** The function returns 1 if all items was resolved and -1 on impossible -** conditions -****************************************************************************/ +/* + Substitutes constants for some COUNT(), MIN() and MAX() functions. + + SYNOPSIS + opt_sum_query() + tables Tables in query + all_fields All fields to be returned + conds WHERE clause + + NOTE: + This function is only called for queries with sum functions and no + GROUP BY part. + + RETURN VALUES + 0 No errors + 1 if all items was resolved + -1 on impossible conditions +*/ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) { - List_iterator<Item> it(all_fields); - int const_result=1; - bool recalc_const_item=0; - table_map removed_tables=0; + List_iterator_fast<Item> it(all_fields); + int const_result= 1; + bool recalc_const_item= 0; + table_map removed_tables= 0, outer_tables= 0, used_tables= 0; + table_map where_tables= 0; Item *item; COND *org_conds= conds; - - /* Add all ON conditions to WHERE condition */ + + if (conds) + where_tables= conds->used_tables(); + + /* Don't replace expression on a table that is part of an outer join */ for (TABLE_LIST *tl=tables; tl ; tl= tl->next) { if (tl->on_expr) - conds= and_expressions(conds, tl->on_expr, &org_conds); + { + outer_tables|= tl->table->map; + + /* + We can't optimise LEFT JOIN in cases where the WHERE condition + restricts the table that is used, like in: + SELECT MAX(t1.a) FROM t1 LEFT JOIN t2 join-condition + WHERE t2.field IS NULL; + */ + if (tl->table->map & where_tables) + return 0; + } + else + used_tables|= tl->table->map; } /* @@ -68,8 +96,8 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) TABLE_LIST *table; for (table=tables; table ; table=table->next) { - if (table->on_expr || (table->table->file->option_flag() & - HA_NOT_EXACT_COUNT)) + if (outer_tables || (table->table->file->table_flags() & + HA_NOT_EXACT_COUNT)) { const_result=0; // Can't optimize left join break; @@ -99,21 +127,35 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) byte key_buff[MAX_KEY_LENGTH]; TABLE_REF ref; ref.key_buff=key_buff; + Item_field *item_field= ((Item_field*) expr); + TABLE *table= item_field->field->table; - if (!find_range_key(&ref, ((Item_field*) expr)->field,conds)) + if ((outer_tables & table->map) || + (!find_range_key(&ref, item_field->field,conds))) { const_result=0; break; } - TABLE *table=((Item_field*) expr)->field->table; - bool error=table->file->index_init((uint) ref.key); + bool error= table->file->index_init((uint) ref.key); + enum ha_rkey_function find_flag= HA_READ_KEY_OR_NEXT; + uint prefix_len= ref.key_length; + /* + If we are doing MIN() on a column with NULL fields + we must read the key after the NULL column + */ + if (item_field->field->null_bit) + { + ref.key_buff[ref.key_length++]=1; + find_flag= HA_READ_AFTER_KEY; + } + if (!ref.key_length) error=table->file->index_first(table->record[0]) !=0; else error=table->file->index_read(table->record[0],key_buff, ref.key_length, - HA_READ_KEY_OR_NEXT) || - key_cmp(table, key_buff, ref.key, ref.key_length); + find_flag) || + key_cmp(table, key_buff, ref.key, prefix_len); if (table->key_read) { table->key_read=0; @@ -121,7 +163,7 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) } table->file->index_end(); if (error) - return -1; // Impossible query + return -1; // No rows matching where removed_tables|= table->map; } else if (!expr->const_item()) // This is VERY seldom false @@ -147,14 +189,15 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) byte key_buff[MAX_KEY_LENGTH]; TABLE_REF ref; ref.key_buff=key_buff; + TABLE *table=((Item_field*) expr)->field->table; - if (!find_range_key(&ref, ((Item_field*) expr)->field,conds)) + if ((outer_tables & table->map) || + !find_range_key(&ref, ((Item_field*) expr)->field,conds)) { const_result=0; break; } - TABLE *table=((Item_field*) expr)->field->table; - if ((table->file->option_flag() & HA_NOT_READ_AFTER_KEY)) + if ((table->file->table_flags() & HA_NOT_READ_AFTER_KEY)) { const_result=0; break; @@ -165,9 +208,9 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) error=table->file->index_last(table->record[0]) !=0; else { - error = table->file->index_read(table->record[0], key_buff, - ref.key_length, - HA_READ_PREFIX_LAST) || + error = table->file->index_read(table->record[0], key_buff, + ref.key_length, + HA_READ_PREFIX_LAST) || key_cmp(table,key_buff,ref.key,ref.key_length); } if (table->key_read) @@ -203,8 +246,15 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) const_result=0; } } - if (conds && (conds->used_tables() & ~ removed_tables)) - const_result=0; + /* + If we have a where clause, we can only ignore searching in the + tables if MIN/MAX optimisation replaced all used tables + This is to not to use replaced values in case of: + SELECT MIN(key) FROM table_1, empty_table + removed_tables is != 0 if we have used MIN() or MAX(). + */ + if (removed_tables && used_tables != removed_tables) + const_result= 0; // We didn't remove all tables return const_result; } @@ -217,7 +267,7 @@ uint count_table_entries(COND *cond,TABLE *table) if (((Item_cond*) cond)->functype() == Item_func::COND_OR_FUNC) return (cond->used_tables() & table->map) ? MAX_REF_PARTS+1 : 0; - List_iterator<Item> li(*((Item_cond*) cond)->argument_list()); + List_iterator_fast<Item> li(*((Item_cond*) cond)->argument_list()); Item *item; uint count=0; while ((item=li++)) @@ -262,7 +312,7 @@ bool part_of_cond(COND *cond,Field *field) if (((Item_cond*) cond)->functype() == Item_func::COND_OR_FUNC) return 0; // Already checked - List_iterator<Item> li(*((Item_cond*) cond)->argument_list()); + List_iterator_fast<Item> li(*((Item_cond*) cond)->argument_list()); Item *item; while ((item=li++)) { @@ -303,21 +353,28 @@ bool part_of_cond(COND *cond,Field *field) static bool find_range_key(TABLE_REF *ref, Field* field, COND *cond) { if (!(field->flags & PART_KEY_FLAG)) - return 0; // Not part of a key. Skipp it + return 0; // Not part of a key. Skip it TABLE *table=field->table; - if (table->file->option_flag() & HA_WRONG_ASCII_ORDER) - return(0); // Can't use key to find last row uint idx=0; /* Check if some key has field as first key part */ - if (field->key_start && (! cond || ! (cond->used_tables() & table->map))) + if ((field->key_start & field->table->keys_in_use_for_query) && + (! cond || ! (cond->used_tables() & table->map))) { - for (key_map key=field->key_start ; !(key & 1) ; idx++) - key>>=1; + for (key_map key=field->key_start ;;) + { + for (; !(key & 1) ; idx++) + key>>=1; + if (!(table->file->index_flags(idx) & HA_WRONG_ASCII_ORDER)) + break; // Key is ok + /* Can't use this key, for looking up min() or max(), end if last one */ + if (key == 1) + return 0; + } ref->key_length=0; ref->key=idx; - if (field->part_of_key & ((table_map) 1 << idx)) + if (field->part_of_key & ((key_map) 1 << idx)) { table->key_read=1; table->file->extra(HA_EXTRA_KEYREAD); @@ -334,6 +391,7 @@ static bool find_range_key(TABLE_REF *ref, Field* field, COND *cond) return 0; KEY *keyinfo,*keyinfo_end; + idx=0; for (keyinfo=table->key_info, keyinfo_end=keyinfo+table->keys ; keyinfo != keyinfo_end; keyinfo++,idx++) @@ -349,7 +407,8 @@ static bool find_range_key(TABLE_REF *ref, Field* field, COND *cond) part++) { if (!part_of_cond(cond,part->field) || - left_length < part->store_length) + left_length < part->store_length || + (table->file->index_flags(idx) & HA_WRONG_ASCII_ORDER)) break; // Save found constant if (part->null_bit) @@ -362,7 +421,7 @@ static bool find_range_key(TABLE_REF *ref, Field* field, COND *cond) { ref->key_length= (uint) (key_ptr-ref->key_buff); ref->key=idx; - if (field->part_of_key & ((table_map) 1 << idx)) + if (field->part_of_key & ((key_map) 1 << idx)) { table->key_read=1; table->file->extra(HA_EXTRA_KEYREAD); diff --git a/sql/password.c b/sql/password.c index 1c88aabcce2..318c8e84db3 100644 --- a/sql/password.c +++ b/sql/password.c @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -34,7 +34,7 @@ This saves a hashed number as a string in the password field. *****************************************************************************/ -#include <global.h> +#include <my_global.h> #include <my_sys.h> #include <m_string.h> #include "mysql.h" @@ -43,7 +43,7 @@ void randominit(struct rand_struct *rand_st,ulong seed1, ulong seed2) { /* For mysql 3.21.# */ #ifdef HAVE_purify - bzero((char*) rand_st,sizeof(*rand_st)); /* Avoid UMC varnings */ + bzero((char*) rand_st,sizeof(*rand_st)); /* Avoid UMC varnings */ #endif rand_st->max_value= 0x3FFFFFFFL; rand_st->max_value_dbl=(double) rand_st->max_value; diff --git a/sql/procedure.cc b/sql/procedure.cc index 526bbe0feab..437bd82d6e5 100644 --- a/sql/procedure.cc +++ b/sql/procedure.cc @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ diff --git a/sql/procedure.h b/sql/procedure.h index 1583f1169ce..349908a8d84 100644 --- a/sql/procedure.h +++ b/sql/procedure.h @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -43,6 +43,7 @@ public: { init_make_field(tmp_field,field_type()); } + unsigned int size_of() { return sizeof(*this);} }; class Item_proc_real :public Item_proc @@ -62,6 +63,7 @@ public: double val() { return value; } longlong val_int() { return (longlong) value; } String *val_str(String *s) { s->set(value,decimals); return s; } + unsigned int size_of() { return sizeof(*this);} }; class Item_proc_int :public Item_proc @@ -79,6 +81,7 @@ public: double val() { return (double) value; } longlong val_int() { return value; } String *val_str(String *s) { s->set(value); return s; } + unsigned int size_of() { return sizeof(*this);} }; @@ -98,6 +101,7 @@ public: { return null_value ? (String*) 0 : (String*) &str_value; } + unsigned int size_of() { return sizeof(*this);} }; /* The procedure class definitions */ diff --git a/sql/records.cc b/sql/records.cc index d436f4f58fe..22c4d54550c 100644 --- a/sql/records.cc +++ b/sql/records.cc @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -45,18 +45,14 @@ void init_read_record(READ_RECORD *info,THD *thd, TABLE *table, info->ref_length=table->file->ref_length; info->select=select; info->print_error=print_error; - table->status=0; /* And it's allways found */ + info->ignore_not_found_rows= 0; + table->status=0; /* And it's always found */ if (select && my_b_inited(&select->file)) tempfile= &select->file; else tempfile= table->io_cache; - if (select && select->quick && (! tempfile || !tempfile->buffer)) - { - DBUG_PRINT("info",("using rr_quick")); - info->read_record=rr_quick; - } - else if (tempfile && my_b_inited(tempfile)) // Test if ref-records was used + if (tempfile && my_b_inited(tempfile)) // Test if ref-records was used { DBUG_PRINT("info",("using rr_from_tempfile")); info->read_record=rr_from_tempfile; @@ -66,7 +62,7 @@ void init_read_record(READ_RECORD *info,THD *thd, TABLE *table, table->file->rnd_init(0); if (! (specialflag & SPECIAL_SAFE_MODE) && - record_rnd_cache_size && + thd->variables.read_rnd_buff_size && !table->file->fast_key_read() && (table->db_stat & HA_READ_ONLY || table->reginfo.lock_type <= TL_READ_NO_INSERT) && @@ -84,8 +80,14 @@ void init_read_record(READ_RECORD *info,THD *thd, TABLE *table, } } } + else if (select && select->quick) + { + DBUG_PRINT("info",("using rr_quick")); + info->read_record=rr_quick; + } else if (table->record_pointers) { + DBUG_PRINT("info",("using record_pointers")); table->file->rnd_init(0); info->cache_pos=table->record_pointers; info->cache_end=info->cache_pos+ table->found_records*info->ref_length; @@ -101,8 +103,9 @@ void init_read_record(READ_RECORD *info,THD *thd, TABLE *table, (int) table->reginfo.lock_type <= (int) TL_READ_HIGH_PRIORITY || !(table->db_options_in_use & HA_OPTION_PACK_RECORD) || (use_record_cache < 0 && - !(table->file->option_flag() & HA_NOT_DELETE_WITH_CACHE))) - VOID(table->file->extra(HA_EXTRA_CACHE)); // Cache reads + !(table->file->table_flags() & HA_NOT_DELETE_WITH_CACHE))) + VOID(table->file->extra_opt(HA_EXTRA_CACHE, + thd->variables.read_buff_size)); } DBUG_VOID_RETURN; } /* init_read_record */ @@ -182,7 +185,8 @@ tryNext: { if (tmp == HA_ERR_END_OF_FILE) tmp= -1; - else if (tmp == HA_ERR_RECORD_DELETED) + else if (tmp == HA_ERR_RECORD_DELETED || + (tmp == HA_ERR_KEY_NOT_FOUND && info->ignore_not_found_rows)) goto tryNext; else { @@ -210,7 +214,8 @@ tryNext: { if (tmp == HA_ERR_END_OF_FILE) tmp= -1; - else if (tmp == HA_ERR_RECORD_DELETED) + else if (tmp == HA_ERR_RECORD_DELETED || + (tmp == HA_ERR_KEY_NOT_FOUND && info->ignore_not_found_rows)) goto tryNext; else { @@ -228,6 +233,8 @@ tryNext: static int init_rr_cache(READ_RECORD *info) { uint rec_cache_size; + THD *thd= current_thd; + DBUG_ENTER("init_rr_cache"); info->struct_length=3+MAX_REFLENGTH; @@ -236,7 +243,7 @@ static int init_rr_cache(READ_RECORD *info) info->reclength=ALIGN_SIZE(info->struct_length); info->error_offset=info->table->reclength; - info->cache_records=record_rnd_cache_size/ + info->cache_records= thd->variables.read_rnd_buff_size / (info->reclength+info->struct_length); rec_cache_size=info->cache_records*info->reclength; info->rec_cache_size=info->cache_records*info->ref_length; diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc new file mode 100644 index 00000000000..e263ca7adeb --- /dev/null +++ b/sql/repl_failsafe.cc @@ -0,0 +1,891 @@ +/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB & Sasha + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +// Sasha Pachev <sasha@mysql.com> is currently in charge of this file + +#include "mysql_priv.h" +#include "repl_failsafe.h" +#include "sql_repl.h" +#include "slave.h" +#include "sql_acl.h" +#include "mini_client.h" +#include "log_event.h" +#include <mysql.h> + +#define SLAVE_LIST_CHUNK 128 +#define SLAVE_ERRMSG_SIZE (FN_REFLEN+64) + + +RPL_STATUS rpl_status=RPL_NULL; +pthread_mutex_t LOCK_rpl_status; +pthread_cond_t COND_rpl_status; +HASH slave_list; +extern const char* any_db; + +const char *rpl_role_type[] = {"MASTER","SLAVE",NullS}; +TYPELIB rpl_role_typelib = {array_elements(rpl_role_type)-1,"", + rpl_role_type}; + +const char* rpl_status_type[]= +{ + "AUTH_MASTER","ACTIVE_SLAVE","IDLE_SLAVE", "LOST_SOLDIER","TROOP_SOLDIER", + "RECOVERY_CAPTAIN","NULL",NullS +}; +TYPELIB rpl_status_typelib= {array_elements(rpl_status_type)-1,"", + rpl_status_type}; + + +static Slave_log_event* find_slave_event(IO_CACHE* log, + const char* log_file_name, + char* errmsg); + + +static int init_failsafe_rpl_thread(THD* thd) +{ + DBUG_ENTER("init_failsafe_rpl_thread"); + thd->system_thread = thd->bootstrap = 1; + thd->host_or_ip= ""; + thd->client_capabilities = 0; + my_net_init(&thd->net, 0); + thd->net.read_timeout = slave_net_timeout; + thd->max_client_packet_length=thd->net.max_packet; + thd->master_access= ~0; + thd->priv_user = 0; + pthread_mutex_lock(&LOCK_thread_count); + thd->thread_id = thread_id++; + pthread_mutex_unlock(&LOCK_thread_count); + + if (init_thr_lock() || thd->store_globals()) + { + close_connection(&thd->net,ER_OUT_OF_RESOURCES); // is this needed? + end_thread(thd,0); + DBUG_RETURN(-1); + } + +#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__) + sigset_t set; + VOID(sigemptyset(&set)); // Get mask in use + VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals)); +#endif + + thd->mem_root.free=thd->mem_root.used=0; + if (thd->variables.max_join_size == HA_POS_ERROR) + thd->options|= OPTION_BIG_SELECTS; + + thd->proc_info="Thread initialized"; + thd->version=refresh_version; + thd->set_time(); + DBUG_RETURN(0); +} + + +void change_rpl_status(RPL_STATUS from_status, RPL_STATUS to_status) +{ + pthread_mutex_lock(&LOCK_rpl_status); + if (rpl_status == from_status || rpl_status == RPL_ANY) + rpl_status = to_status; + pthread_cond_signal(&COND_rpl_status); + pthread_mutex_unlock(&LOCK_rpl_status); +} + + +#define get_object(p, obj) \ +{\ + uint len = (uint)*p++; \ + if (p + len > p_end || len >= sizeof(obj)) \ + goto err; \ + strmake(obj,(char*) p,len); \ + p+= len; \ +}\ + + +static inline int cmp_master_pos(Slave_log_event* sev, LEX_MASTER_INFO* mi) +{ + return cmp_master_pos(sev->master_log, sev->master_pos, mi->log_file_name, + mi->pos); +} + + +void unregister_slave(THD* thd, bool only_mine, bool need_mutex) +{ + if (thd->server_id) + { + if (need_mutex) + pthread_mutex_lock(&LOCK_slave_list); + + SLAVE_INFO* old_si; + if ((old_si = (SLAVE_INFO*)hash_search(&slave_list, + (byte*)&thd->server_id, 4)) && + (!only_mine || old_si->thd == thd)) + hash_delete(&slave_list, (byte*)old_si); + + if (need_mutex) + pthread_mutex_unlock(&LOCK_slave_list); + } +} + + +/* + Register slave in 'slave_list' hash table + + RETURN VALUES + 0 ok + 1 Error. Error message sent to client +*/ + +int register_slave(THD* thd, uchar* packet, uint packet_length) +{ + int res; + SLAVE_INFO *si; + uchar *p= packet, *p_end= packet + packet_length; + + if (check_access(thd, REPL_SLAVE_ACL, any_db)) + return 1; + + if (!(si = (SLAVE_INFO*)my_malloc(sizeof(SLAVE_INFO), MYF(MY_WME)))) + goto err2; + + thd->server_id= si->server_id= uint4korr(p); + p+= 4; + get_object(p,si->host); + get_object(p,si->user); + get_object(p,si->password); + if (p+10 > p_end) + goto err; + si->port= uint2korr(p); + p += 2; + si->rpl_recovery_rank= uint4korr(p); + p += 4; + if (!(si->master_id= uint4korr(p))) + si->master_id= server_id; + si->thd= thd; + + pthread_mutex_lock(&LOCK_slave_list); + unregister_slave(thd,0,0); + res= hash_insert(&slave_list, (byte*) si); + pthread_mutex_unlock(&LOCK_slave_list); + return res; + +err: + my_free((gptr) si, MYF(MY_WME)); + my_message(ER_UNKNOWN_ERROR, "Wrong parameters to function register_slave", + MYF(0)); +err2: + send_error(&thd->net); + return 1; +} + +extern "C" uint32 +*slave_list_key(SLAVE_INFO* si, uint* len, + my_bool not_used __attribute__((unused))) +{ + *len = 4; + return &si->server_id; +} + +extern "C" void slave_info_free(void *s) +{ + my_free((gptr) s, MYF(MY_WME)); +} + +void init_slave_list() +{ + hash_init(&slave_list, SLAVE_LIST_CHUNK, 0, 0, + (hash_get_key) slave_list_key, (hash_free_key) slave_info_free, 0); + pthread_mutex_init(&LOCK_slave_list, MY_MUTEX_INIT_FAST); +} + +void end_slave_list() +{ + /* No protection by a mutex needed as we are only called at shutdown */ + if (hash_inited(&slave_list)) + { + hash_free(&slave_list); + pthread_mutex_destroy(&LOCK_slave_list); + } +} + +static int find_target_pos(LEX_MASTER_INFO *mi, IO_CACHE *log, char *errmsg) +{ + my_off_t log_pos = (my_off_t) mi->pos; + uint32 target_server_id = mi->server_id; + + for (;;) + { + Log_event* ev; + if (!(ev = Log_event::read_log_event(log, (pthread_mutex_t*) 0, 0))) + { + if (log->error > 0) + strmov(errmsg, "Binary log truncated in the middle of event"); + else if (log->error < 0) + strmov(errmsg, "I/O error reading binary log"); + else + strmov(errmsg, "Could not find target event in the binary log"); + return 1; + } + + if (ev->log_pos >= log_pos && ev->server_id == target_server_id) + { + delete ev; + mi->pos = my_b_tell(log); + return 0; + } + delete ev; + } + /* Impossible */ +} + + +int translate_master(THD* thd, LEX_MASTER_INFO* mi, char* errmsg) +{ + LOG_INFO linfo; + char last_log_name[FN_REFLEN]; + IO_CACHE log; + File file = -1, last_file = -1; + pthread_mutex_t *log_lock; + const char* errmsg_p; + Slave_log_event* sev = 0; + my_off_t last_pos = 0; + int error = 1; + int cmp_res; + LINT_INIT(cmp_res); + DBUG_ENTER("translate_master"); + + if (!mysql_bin_log.is_open()) + { + strmov(errmsg,"Binary log is not open"); + DBUG_RETURN(1); + } + + if (!server_id_supplied) + { + strmov(errmsg, "Misconfigured master - server id was not set"); + DBUG_RETURN(1); + } + + if (mysql_bin_log.find_log_pos(&linfo, NullS, 1)) + { + strmov(errmsg,"Could not find first log"); + DBUG_RETURN(1); + } + thd->current_linfo = &linfo; + + bzero((char*) &log,sizeof(log)); + log_lock = mysql_bin_log.get_log_lock(); + pthread_mutex_lock(log_lock); + + for (;;) + { + if ((file=open_binlog(&log, linfo.log_file_name, &errmsg_p)) < 0) + { + strmov(errmsg, errmsg_p); + goto err; + } + + if (!(sev = find_slave_event(&log, linfo.log_file_name, errmsg))) + goto err; + + cmp_res = cmp_master_pos(sev, mi); + delete sev; + + if (!cmp_res) + { + /* Copy basename */ + fn_format(mi->log_file_name, linfo.log_file_name, "","",1); + mi->pos = my_b_tell(&log); + goto mi_inited; + } + else if (cmp_res > 0) + { + if (!last_pos) + { + strmov(errmsg, + "Slave event in first log points past the target position"); + goto err; + } + end_io_cache(&log); + (void) my_close(file, MYF(MY_WME)); + if (init_io_cache(&log, (file = last_file), IO_SIZE, READ_CACHE, 0, 0, + MYF(MY_WME))) + { + errmsg[0] = 0; + goto err; + } + break; + } + + strmov(last_log_name, linfo.log_file_name); + last_pos = my_b_tell(&log); + + switch (mysql_bin_log.find_next_log(&linfo, 1)) { + case LOG_INFO_EOF: + if (last_file >= 0) + (void)my_close(last_file, MYF(MY_WME)); + last_file = -1; + goto found_log; + case 0: + break; + default: + strmov(errmsg, "Error reading log index"); + goto err; + } + + end_io_cache(&log); + if (last_file >= 0) + (void) my_close(last_file, MYF(MY_WME)); + last_file = file; + } + +found_log: + my_b_seek(&log, last_pos); + if (find_target_pos(mi,&log,errmsg)) + goto err; + fn_format(mi->log_file_name, last_log_name, "","",1); /* Copy basename */ + +mi_inited: + error = 0; +err: + pthread_mutex_unlock(log_lock); + end_io_cache(&log); + pthread_mutex_lock(&LOCK_thread_count); + thd->current_linfo = 0; + pthread_mutex_unlock(&LOCK_thread_count); + if (file >= 0) + (void) my_close(file, MYF(MY_WME)); + if (last_file >= 0 && last_file != file) + (void) my_close(last_file, MYF(MY_WME)); + + DBUG_RETURN(error); +} + + +/* + Caller must delete result when done +*/ + +static Slave_log_event* find_slave_event(IO_CACHE* log, + const char* log_file_name, + char* errmsg) +{ + Log_event* ev; + int i; + bool slave_event_found = 0; + LINT_INIT(ev); + + for (i = 0; i < 2; i++) + { + if (!(ev = Log_event::read_log_event(log, (pthread_mutex_t*)0, 0))) + { + my_snprintf(errmsg, SLAVE_ERRMSG_SIZE, + "Error reading event in log '%s'", + (char*)log_file_name); + return 0; + } + if (ev->get_type_code() == SLAVE_EVENT) + { + slave_event_found = 1; + break; + } + delete ev; + } + if (!slave_event_found) + { + my_snprintf(errmsg, SLAVE_ERRMSG_SIZE, + "Could not find slave event in log '%s'", + (char*)log_file_name); + delete ev; + return 0; + } + + return (Slave_log_event*)ev; +} + + +int show_new_master(THD* thd) +{ + DBUG_ENTER("show_new_master"); + List<Item> field_list; + char errmsg[SLAVE_ERRMSG_SIZE]; + LEX_MASTER_INFO* lex_mi = &thd->lex.mi; + + errmsg[0]=0; // Safety + if (translate_master(thd, lex_mi, errmsg)) + { + if (errmsg[0]) + my_error(ER_ERROR_WHEN_EXECUTING_COMMAND, MYF(0), + "SHOW NEW MASTER", errmsg); + DBUG_RETURN(-1); + } + else + { + String* packet = &thd->packet; + field_list.push_back(new Item_empty_string("Log_name", 20)); + field_list.push_back(new Item_empty_string("Log_pos", 20)); + if (send_fields(thd, field_list, 1)) + DBUG_RETURN(-1); + packet->length(0); + net_store_data(packet, lex_mi->log_file_name); + net_store_data(packet, (longlong)lex_mi->pos); + if (my_net_write(&thd->net, packet->ptr(), packet->length())) + DBUG_RETURN(-1); + send_eof(&thd->net); + DBUG_RETURN(0); + } +} + + +int update_slave_list(MYSQL* mysql) +{ + MYSQL_RES* res=0; + MYSQL_ROW row; + const char* error=0; + bool have_auth_info; + int port_ind; + DBUG_ENTER("update_slave_list"); + + if (mc_mysql_query(mysql,"SHOW SLAVE HOSTS",0) || + !(res = mc_mysql_store_result(mysql))) + { + error = "Query error"; + goto err; + } + + switch (mc_mysql_num_fields(res)) { + case 5: + have_auth_info = 0; + port_ind=2; + break; + case 7: + have_auth_info = 1; + port_ind=4; + break; + default: + error = "Invalid number of fields in SHOW SLAVE HOSTS"; + goto err; + } + + pthread_mutex_lock(&LOCK_slave_list); + + while ((row= mc_mysql_fetch_row(res))) + { + uint32 server_id; + SLAVE_INFO* si, *old_si; + server_id = atoi(row[0]); + if ((old_si= (SLAVE_INFO*)hash_search(&slave_list, + (byte*)&server_id,4))) + si = old_si; + else + { + if (!(si = (SLAVE_INFO*)my_malloc(sizeof(SLAVE_INFO), MYF(MY_WME)))) + { + error = "Out of memory"; + pthread_mutex_unlock(&LOCK_slave_list); + goto err; + } + si->server_id = server_id; + hash_insert(&slave_list, (byte*)si); + } + strmake(si->host, row[1], sizeof(si->host)-1); + si->port = atoi(row[port_ind]); + si->rpl_recovery_rank = atoi(row[port_ind+1]); + si->master_id = atoi(row[port_ind+2]); + if (have_auth_info) + { + strmake(si->user, row[2], sizeof(si->user)-1); + strmake(si->password, row[3], sizeof(si->password)-1); + } + } + pthread_mutex_unlock(&LOCK_slave_list); + +err: + if (res) + mc_mysql_free_result(res); + if (error) + { + sql_print_error("Error updating slave list: %s",error); + DBUG_RETURN(1); + } + DBUG_RETURN(0); +} + + +int find_recovery_captain(THD* thd, MYSQL* mysql) +{ + return 0; +} + + +pthread_handler_decl(handle_failsafe_rpl,arg) +{ + DBUG_ENTER("handle_failsafe_rpl"); + THD *thd = new THD; + thd->thread_stack = (char*)&thd; + MYSQL* recovery_captain = 0; + pthread_detach_this_thread(); + if (init_failsafe_rpl_thread(thd) || !(recovery_captain=mc_mysql_init(0))) + { + sql_print_error("Could not initialize failsafe replication thread"); + goto err; + } + pthread_mutex_lock(&LOCK_rpl_status); + while (!thd->killed && !abort_loop) + { + bool break_req_chain = 0; + const char* msg = thd->enter_cond(&COND_rpl_status, + &LOCK_rpl_status, "Waiting for request"); + pthread_cond_wait(&COND_rpl_status, &LOCK_rpl_status); + thd->proc_info="Processing request"; + while (!break_req_chain) + { + switch (rpl_status) { + case RPL_LOST_SOLDIER: + if (find_recovery_captain(thd, recovery_captain)) + rpl_status=RPL_TROOP_SOLDIER; + else + rpl_status=RPL_RECOVERY_CAPTAIN; + break_req_chain=1; /* for now until other states are implemented */ + break; + default: + break_req_chain=1; + break; + } + } + thd->exit_cond(msg); + } + pthread_mutex_unlock(&LOCK_rpl_status); +err: + if (recovery_captain) + mc_mysql_close(recovery_captain); + delete thd; + my_thread_end(); + pthread_exit(0); + DBUG_RETURN(0); +} + + +int show_slave_hosts(THD* thd) +{ + List<Item> field_list; + NET* net = &thd->net; + String* packet = &thd->packet; + DBUG_ENTER("show_slave_hosts"); + + field_list.push_back(new Item_empty_string("Server_id", 20)); + field_list.push_back(new Item_empty_string("Host", 20)); + if (opt_show_slave_auth_info) + { + field_list.push_back(new Item_empty_string("User",20)); + field_list.push_back(new Item_empty_string("Password",20)); + } + field_list.push_back(new Item_empty_string("Port",20)); + field_list.push_back(new Item_empty_string("Rpl_recovery_rank", 20)); + field_list.push_back(new Item_empty_string("Master_id", 20)); + + if (send_fields(thd, field_list, 1)) + DBUG_RETURN(-1); + + pthread_mutex_lock(&LOCK_slave_list); + + for (uint i = 0; i < slave_list.records; ++i) + { + SLAVE_INFO* si = (SLAVE_INFO*) hash_element(&slave_list, i); + packet->length(0); + net_store_data(packet, si->server_id); + net_store_data(packet, si->host); + if (opt_show_slave_auth_info) + { + net_store_data(packet, si->user); + net_store_data(packet, si->password); + } + net_store_data(packet, (uint32) si->port); + net_store_data(packet, si->rpl_recovery_rank); + net_store_data(packet, si->master_id); + if (my_net_write(net, (char*)packet->ptr(), packet->length())) + { + pthread_mutex_unlock(&LOCK_slave_list); + DBUG_RETURN(-1); + } + } + pthread_mutex_unlock(&LOCK_slave_list); + send_eof(net); + DBUG_RETURN(0); +} + + +int connect_to_master(THD *thd, MYSQL* mysql, MASTER_INFO* mi) +{ + DBUG_ENTER("connect_to_master"); + + if (!mi->host || !*mi->host) /* empty host */ + { + strmov(mysql->net.last_error, "Master is not configured"); + DBUG_RETURN(1); + } + if (!mc_mysql_connect(mysql, mi->host, mi->user, mi->password, 0, + mi->port, 0, 0, + slave_net_timeout)) + DBUG_RETURN(1); + DBUG_RETURN(0); +} + + +static inline void cleanup_mysql_results(MYSQL_RES* db_res, + MYSQL_RES** cur, MYSQL_RES** start) +{ + for (; cur >= start; --cur) + { + if (*cur) + mc_mysql_free_result(*cur); + } + mc_mysql_free_result(db_res); +} + + +static int fetch_db_tables(THD *thd, MYSQL *mysql, const char *db, + MYSQL_RES *table_res, MASTER_INFO *mi) +{ + MYSQL_ROW row; + for (row = mc_mysql_fetch_row(table_res); row; + row = mc_mysql_fetch_row(table_res)) + { + TABLE_LIST table; + const char* table_name= row[0]; + int error; + if (table_rules_on) + { + table.next= 0; + table.db= (char*) db; + table.real_name= (char*) table_name; + table.updating= 1; + if (!tables_ok(thd, &table)) + continue; + } + if ((error= fetch_master_table(thd, db, table_name, mi, mysql))) + return error; + } + return 0; +} + +/* + Load all MyISAM tables from master to this slave. + + REQUIREMENTS + - No active transaction (flush_relay_log_info would not work in this case) +*/ + +int load_master_data(THD* thd) +{ + MYSQL mysql; + MYSQL_RES* master_status_res = 0; + int error = 0; + const char* errmsg=0; + int restart_thread_mask; + mc_mysql_init(&mysql); + + /* + We do not want anyone messing with the slave at all for the entire + duration of the data load. + */ + LOCK_ACTIVE_MI; + lock_slave_threads(active_mi); + init_thread_mask(&restart_thread_mask,active_mi,0 /*not inverse*/); + if (restart_thread_mask && + (error=terminate_slave_threads(active_mi,restart_thread_mask, + 1 /*skip lock*/))) + { + send_error(&thd->net,error); + unlock_slave_threads(active_mi); + UNLOCK_ACTIVE_MI; + return 1; + } + + if (connect_to_master(thd, &mysql, active_mi)) + { + net_printf(&thd->net, error= ER_CONNECT_TO_MASTER, + mc_mysql_error(&mysql)); + goto err; + } + + // now that we are connected, get all database and tables in each + { + MYSQL_RES *db_res, **table_res, **table_res_end, **cur_table_res; + uint num_dbs; + + if (mc_mysql_query(&mysql, "show databases", 0) || + !(db_res = mc_mysql_store_result(&mysql))) + { + net_printf(&thd->net, error = ER_QUERY_ON_MASTER, + mc_mysql_error(&mysql)); + goto err; + } + + if (!(num_dbs = (uint) mc_mysql_num_rows(db_res))) + goto err; + /* + In theory, the master could have no databases at all + and run with skip-grant + */ + + if (!(table_res = (MYSQL_RES**)thd->alloc(num_dbs * sizeof(MYSQL_RES*)))) + { + net_printf(&thd->net, error = ER_OUTOFMEMORY); + goto err; + } + + /* + This is a temporary solution until we have online backup + capabilities - to be replaced once online backup is working + we wait to issue FLUSH TABLES WITH READ LOCK for as long as we + can to minimize the lock time. + */ + if (mc_mysql_query(&mysql, "FLUSH TABLES WITH READ LOCK", 0) || + mc_mysql_query(&mysql, "SHOW MASTER STATUS",0) || + !(master_status_res = mc_mysql_store_result(&mysql))) + { + net_printf(&thd->net, error = ER_QUERY_ON_MASTER, + mc_mysql_error(&mysql)); + goto err; + } + + /* + Go through every table in every database, and if the replication + rules allow replicating it, get it + */ + + table_res_end = table_res + num_dbs; + + for (cur_table_res = table_res; cur_table_res < table_res_end; + cur_table_res++) + { + // since we know how many rows we have, this can never be NULL + MYSQL_ROW row = mc_mysql_fetch_row(db_res); + char* db = row[0]; + + /* + Do not replicate databases excluded by rules + also skip mysql database - in most cases the user will + mess up and not exclude mysql database with the rules when + he actually means to - in this case, he is up for a surprise if + his priv tables get dropped and downloaded from master + TODO - add special option, not enabled + by default, to allow inclusion of mysql database into load + data from master + */ + + if (!db_ok(db, replicate_do_db, replicate_ignore_db) || + !strcmp(db,"mysql")) + { + *cur_table_res = 0; + continue; + } + + if (mysql_rm_db(thd, db, 1,1) || + mysql_create_db(thd, db, 0, 1)) + { + send_error(&thd->net, 0, 0); + cleanup_mysql_results(db_res, cur_table_res - 1, table_res); + goto err; + } + + if (mc_mysql_select_db(&mysql, db) || + mc_mysql_query(&mysql, "show tables", 0) || + !(*cur_table_res = mc_mysql_store_result(&mysql))) + { + net_printf(&thd->net, error = ER_QUERY_ON_MASTER, + mc_mysql_error(&mysql)); + cleanup_mysql_results(db_res, cur_table_res - 1, table_res); + goto err; + } + + if ((error = fetch_db_tables(thd,&mysql,db,*cur_table_res,active_mi))) + { + // we do not report the error - fetch_db_tables handles it + cleanup_mysql_results(db_res, cur_table_res, table_res); + goto err; + } + } + + cleanup_mysql_results(db_res, cur_table_res - 1, table_res); + + // adjust position in the master + if (master_status_res) + { + MYSQL_ROW row = mc_mysql_fetch_row(master_status_res); + + /* + We need this check because the master may not be running with + log-bin, but it will still allow us to do all the steps + of LOAD DATA FROM MASTER - no reason to forbid it, really, + although it does not make much sense for the user to do it + */ + if (row[0] && row[1]) + { + strmake(active_mi->master_log_name, row[0], + sizeof(active_mi->master_log_name)); + active_mi->master_log_pos = strtoull(row[1], (char**) 0, 10); + // don't hit the magic number + if (active_mi->master_log_pos < BIN_LOG_HEADER_SIZE) + active_mi->master_log_pos = BIN_LOG_HEADER_SIZE; + active_mi->rli.pending = 0; + flush_master_info(active_mi); + } + mc_mysql_free_result(master_status_res); + } + + if (mc_mysql_query(&mysql, "UNLOCK TABLES", 0)) + { + net_printf(&thd->net, error = ER_QUERY_ON_MASTER, + mc_mysql_error(&mysql)); + goto err; + } + } + thd->proc_info="purging old relay logs"; + if (purge_relay_logs(&active_mi->rli,thd, + 0 /* not only reset, but also reinit */, + &errmsg)) + { + send_error(&thd->net, 0, "Failed purging old relay logs"); + unlock_slave_threads(active_mi); + UNLOCK_ACTIVE_MI; + return 1; + } + pthread_mutex_lock(&active_mi->rli.data_lock); + active_mi->rli.master_log_pos = active_mi->master_log_pos; + strmake(active_mi->rli.master_log_name,active_mi->master_log_name, + sizeof(active_mi->rli.master_log_name)-1); + flush_relay_log_info(&active_mi->rli); + pthread_cond_broadcast(&active_mi->rli.data_cond); + pthread_mutex_unlock(&active_mi->rli.data_lock); + thd->proc_info = "starting slave"; + if (restart_thread_mask) + { + error=start_slave_threads(0 /* mutex not needed */, + 1 /* wait for start */, + active_mi,master_info_file,relay_log_info_file, + restart_thread_mask); + } + +err: + unlock_slave_threads(active_mi); + UNLOCK_ACTIVE_MI; + thd->proc_info = 0; + + mc_mysql_close(&mysql); // safe to call since we always do mc_mysql_init() + if (!error) + send_ok(&thd->net); + + return error; +} diff --git a/sql/repl_failsafe.h b/sql/repl_failsafe.h new file mode 100644 index 00000000000..ef1dc1f8778 --- /dev/null +++ b/sql/repl_failsafe.h @@ -0,0 +1,37 @@ +#ifndef REPL_FAILSAFE_H +#define REPL_FAILSAFE_H + +#include "mysql.h" +#include "my_sys.h" +#include "slave.h" + +typedef enum {RPL_AUTH_MASTER=0,RPL_ACTIVE_SLAVE,RPL_IDLE_SLAVE, + RPL_LOST_SOLDIER,RPL_TROOP_SOLDIER, + RPL_RECOVERY_CAPTAIN,RPL_NULL /* inactive */, + RPL_ANY /* wild card used by change_rpl_status */ } RPL_STATUS; +extern RPL_STATUS rpl_status; + +extern pthread_mutex_t LOCK_rpl_status; +extern pthread_cond_t COND_rpl_status; +extern TYPELIB rpl_role_typelib, rpl_status_typelib; +extern const char* rpl_role_type[], *rpl_status_type[]; + +pthread_handler_decl(handle_failsafe_rpl,arg); +void change_rpl_status(RPL_STATUS from_status, RPL_STATUS to_status); +int find_recovery_captain(THD* thd, MYSQL* mysql); +int update_slave_list(MYSQL* mysql); + +extern HASH slave_list; + +int load_master_data(THD* thd); +int connect_to_master(THD *thd, MYSQL* mysql, MASTER_INFO* mi); + +int show_new_master(THD* thd); +int show_slave_hosts(THD* thd); +int translate_master(THD* thd, LEX_MASTER_INFO* mi, char* errmsg); +void init_slave_list(); +void end_slave_list(); +int register_slave(THD* thd, uchar* packet, uint packet_length); +void unregister_slave(THD* thd, bool only_mine, bool need_mutex); + +#endif diff --git a/sql/set_var.cc b/sql/set_var.cc new file mode 100644 index 00000000000..5cfd027bc58 --- /dev/null +++ b/sql/set_var.cc @@ -0,0 +1,1445 @@ +/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* + Handling of MySQL SQL variables + + To add a new variable, one has to do the following: + + - If the variable is thread specific, add it to 'system_variables' struct. + If not, add it to mysqld.cc and an declaration in 'mysql_priv.h' + - Use one of the 'sys_var... classes from set_var.h or write a specific + one for the variable type. + - Define it in the 'variable definition list' in this file. + - If the variable should be changeable, it should be added to the + 'list of all variables' list in this file. + - If the variable should be changed from the command line, add a definition + of it in the my_option structure list in mysqld.dcc + - If the variable should show up in 'show variables' add it to the + init_vars[] struct in this file + + TODO: + - Add full support for the variable character_set (for 4.1) + + - When updating myisam_delay_key_write, we should do a 'flush tables' + of all MyISAM tables to ensure that they are reopen with the + new attribute. +*/ + +#ifdef __GNUC__ +#pragma implementation // gcc: Class implementation +#endif + +#include "mysql_priv.h" +#include "slave.h" +#include "sql_acl.h" +#include <my_getopt.h> +#include <myisam.h> +#ifdef HAVE_BERKELEY_DB +#include "ha_berkeley.h" +#endif +#ifdef HAVE_INNOBASE_DB +#include "ha_innodb.h" +#endif + +static HASH system_variable_hash; +const char *bool_type_names[]= { "OFF", "ON", NullS }; +TYPELIB bool_typelib= +{ + array_elements(bool_type_names)-1, "", bool_type_names +}; + +const char *delay_key_write_type_names[]= { "OFF", "ON", "ALL", NullS }; +TYPELIB delay_key_write_typelib= +{ + array_elements(delay_key_write_type_names)-1, "", delay_key_write_type_names +}; + +static bool sys_check_charset(THD *thd, set_var *var); +static bool sys_update_charset(THD *thd, set_var *var); +static void sys_set_default_charset(THD *thd, enum_var_type type); +static bool set_option_bit(THD *thd, set_var *var); +static bool set_option_autocommit(THD *thd, set_var *var); +static bool set_log_update(THD *thd, set_var *var); +static void fix_low_priority_updates(THD *thd, enum_var_type type); +static void fix_tx_isolation(THD *thd, enum_var_type type); +static void fix_net_read_timeout(THD *thd, enum_var_type type); +static void fix_net_write_timeout(THD *thd, enum_var_type type); +static void fix_net_retry_count(THD *thd, enum_var_type type); +static void fix_max_join_size(THD *thd, enum_var_type type); +static void fix_query_cache_size(THD *thd, enum_var_type type); +static void fix_key_buffer_size(THD *thd, enum_var_type type); + +/* + Variable definition list + + These are variables that can be set from the command line, in + alphabetic order +*/ + +sys_var_long_ptr sys_binlog_cache_size("binlog_cache_size", + &binlog_cache_size); +sys_var_thd_ulong sys_bulk_insert_buff_size("bulk_insert_buffer_size", + &SV::bulk_insert_buff_size); +sys_var_str sys_charset("character_set", + sys_check_charset, + sys_update_charset, + sys_set_default_charset); +sys_var_thd_conv_charset sys_convert_charset("convert_character_set"); +sys_var_bool_ptr sys_concurrent_insert("concurrent_insert", + &myisam_concurrent_insert); +sys_var_long_ptr sys_connect_timeout("connect_timeout", + &connect_timeout); +sys_var_enum sys_delay_key_write("delay_key_write", + &delay_key_write_options, + &delay_key_write_typelib, + fix_delay_key_write); +sys_var_long_ptr sys_delayed_insert_limit("delayed_insert_limit", + &delayed_insert_limit); +sys_var_long_ptr sys_delayed_insert_timeout("delayed_insert_timeout", + &delayed_insert_timeout); +sys_var_long_ptr sys_delayed_queue_size("delayed_queue_size", + &delayed_queue_size); +sys_var_bool_ptr sys_flush("flush", &myisam_flush); +sys_var_long_ptr sys_flush_time("flush_time", &flush_time); +sys_var_thd_ulong sys_interactive_timeout("interactive_timeout", + &SV::net_interactive_timeout); +sys_var_thd_ulong sys_join_buffer_size("join_buffer_size", + &SV::join_buff_size); +sys_var_ulonglong_ptr sys_key_buffer_size("key_buffer_size", + &keybuff_size, + fix_key_buffer_size); +sys_var_bool_ptr sys_local_infile("local_infile", + &opt_local_infile); +sys_var_thd_bool sys_log_warnings("log_warnings", &SV::log_warnings); +sys_var_thd_ulong sys_long_query_time("long_query_time", + &SV::long_query_time); +sys_var_thd_bool sys_low_priority_updates("low_priority_updates", + &SV::low_priority_updates, + fix_low_priority_updates); +#ifndef TO_BE_DELETED /* Alias for the low_priority_updates */ +sys_var_thd_bool sys_sql_low_priority_updates("sql_low_priority_updates", + &SV::low_priority_updates, + fix_low_priority_updates); +#endif +sys_var_thd_ulong sys_max_allowed_packet("max_allowed_packet", + &SV::max_allowed_packet); +sys_var_long_ptr sys_max_binlog_cache_size("max_binlog_cache_size", + &max_binlog_cache_size); +sys_var_long_ptr sys_max_binlog_size("max_binlog_size", + &max_binlog_size); +sys_var_long_ptr sys_max_connections("max_connections", + &max_connections); +sys_var_long_ptr sys_max_connect_errors("max_connect_errors", + &max_connect_errors); +sys_var_long_ptr sys_max_delayed_threads("max_delayed_threads", + &max_insert_delayed_threads); +sys_var_thd_ulong sys_max_heap_table_size("max_heap_table_size", + &SV::max_heap_table_size); +sys_var_thd_ha_rows sys_max_join_size("max_join_size", + &SV::max_join_size, + fix_max_join_size); +#ifndef TO_BE_DELETED /* Alias for max_join_size */ +sys_var_thd_ha_rows sys_sql_max_join_size("sql_max_join_size", + &SV::max_join_size, + fix_max_join_size); +#endif +sys_var_thd_ulong sys_max_sort_length("max_sort_length", + &SV::max_sort_length); +sys_var_long_ptr sys_max_user_connections("max_user_connections", + &max_user_connections); +sys_var_thd_ulong sys_max_tmp_tables("max_tmp_tables", + &SV::max_tmp_tables); +sys_var_long_ptr sys_max_write_lock_count("max_write_lock_count", + &max_write_lock_count); +sys_var_thd_ulonglong sys_myisam_max_extra_sort_file_size("myisam_max_extra_sort_file_size", &SV::myisam_max_extra_sort_file_size); +sys_var_thd_ulonglong sys_myisam_max_sort_file_size("myisam_max_sort_file_size", &SV::myisam_max_sort_file_size); +sys_var_thd_ulong sys_myisam_sort_buffer_size("myisam_sort_buffer_size", &SV::myisam_sort_buff_size); +sys_var_thd_ulong sys_net_buffer_length("net_buffer_length", + &SV::net_buffer_length); +sys_var_thd_ulong sys_net_read_timeout("net_read_timeout", + &SV::net_read_timeout, + fix_net_read_timeout); +sys_var_thd_ulong sys_net_write_timeout("net_write_timeout", + &SV::net_write_timeout, + fix_net_write_timeout); +sys_var_thd_ulong sys_net_retry_count("net_retry_count", + &SV::net_retry_count, + fix_net_retry_count); +sys_var_thd_ulong sys_read_buff_size("read_buffer_size", + &SV::read_buff_size); +sys_var_thd_ulong sys_read_rnd_buff_size("read_rnd_buffer_size", + &SV::read_rnd_buff_size); +sys_var_long_ptr sys_rpl_recovery_rank("rpl_recovery_rank", + &rpl_recovery_rank); +sys_var_long_ptr sys_query_cache_size("query_cache_size", + &query_cache_size, + fix_query_cache_size); +#ifdef HAVE_QUERY_CACHE +sys_var_long_ptr sys_query_cache_limit("query_cache_limit", + &query_cache.query_cache_limit); +sys_var_thd_enum sys_query_cache_type("query_cache_type", + &SV::query_cache_type, + &query_cache_type_typelib); +#endif /* HAVE_QUERY_CACHE */ +sys_var_long_ptr sys_server_id("server_id",&server_id); +sys_var_bool_ptr sys_slave_compressed_protocol("slave_compressed_protocol", + &opt_slave_compressed_protocol); +sys_var_long_ptr sys_slave_net_timeout("slave_net_timeout", + &slave_net_timeout); +sys_var_long_ptr sys_slow_launch_time("slow_launch_time", + &slow_launch_time); +sys_var_thd_ulong sys_sort_buffer("sort_buffer_size", + &SV::sortbuff_size); +sys_var_thd_enum sys_table_type("table_type", &SV::table_type, + &ha_table_typelib); +sys_var_long_ptr sys_table_cache_size("table_cache", + &table_cache_size); +sys_var_long_ptr sys_thread_cache_size("thread_cache_size", + &thread_cache_size); +sys_var_thd_enum sys_tx_isolation("tx_isolation", + &SV::tx_isolation, + &tx_isolation_typelib, + fix_tx_isolation); +sys_var_thd_ulong sys_tmp_table_size("tmp_table_size", + &SV::tmp_table_size); +sys_var_thd_ulong sys_net_wait_timeout("wait_timeout", + &SV::net_wait_timeout); + + +/* + Variables that are bits in THD +*/ + +static sys_var_thd_bit sys_autocommit("autocommit", + set_option_autocommit, + OPTION_NOT_AUTOCOMMIT, + 1); +static sys_var_thd_bit sys_big_tables("big_tables", + set_option_bit, + OPTION_BIG_TABLES); +#ifndef TO_BE_DELETED /* Alias for big_tables */ +static sys_var_thd_bit sys_sql_big_tables("sql_big_tables", + set_option_bit, + OPTION_BIG_TABLES); +#endif +static sys_var_thd_bit sys_big_selects("sql_big_selects", + set_option_bit, + OPTION_BIG_TABLES); +static sys_var_thd_bit sys_log_off("sql_log_off", + set_option_bit, + OPTION_LOG_OFF); +static sys_var_thd_bit sys_log_update("sql_log_update", + set_log_update, + OPTION_UPDATE_LOG); +static sys_var_thd_bit sys_log_binlog("sql_log_bin", + set_log_update, + OPTION_BIN_LOG); +static sys_var_thd_bit sys_sql_warnings("sql_warnings", + set_option_bit, + OPTION_WARNINGS); +static sys_var_thd_bit sys_auto_is_null("sql_auto_is_null", + set_option_bit, + OPTION_AUTO_IS_NULL); +static sys_var_thd_bit sys_safe_updates("sql_safe_updates", + set_option_bit, + OPTION_SAFE_UPDATES); +static sys_var_thd_bit sys_buffer_results("sql_buffer_result", + set_option_bit, + OPTION_BUFFER_RESULT); +static sys_var_thd_bit sys_quote_show_create("sql_quote_show_create", + set_option_bit, + OPTION_QUOTE_SHOW_CREATE); +static sys_var_thd_bit sys_foreign_key_checks("foreign_key_checks", + set_option_bit, + OPTION_NO_FOREIGN_KEY_CHECKS, + 1); +static sys_var_thd_bit sys_unique_checks("unique_checks", + set_option_bit, + OPTION_RELAXED_UNIQUE_CHECKS, + 1); + + +/* Local state variables */ + +static sys_var_thd_ha_rows sys_select_limit("sql_select_limit", + &SV::select_limit); +static sys_var_timestamp sys_timestamp("timestamp"); +static sys_var_last_insert_id sys_last_insert_id("last_insert_id"); +static sys_var_last_insert_id sys_identity("identity"); +static sys_var_insert_id sys_insert_id("insert_id"); +/* alias for last_insert_id() to be compatible with Sybase */ +static sys_var_slave_skip_counter sys_slave_skip_counter("sql_slave_skip_counter"); +static sys_var_rand_seed1 sys_rand_seed1("rand_seed1"); +static sys_var_rand_seed2 sys_rand_seed2("rand_seed2"); + + +/* + List of all variables for initialisation and storage in hash + This is sorted in alphabetical order to make it easy to add new variables + + If the variable is not in this list, it can't be changed with + SET variable_name= +*/ + +sys_var *sys_variables[]= +{ + &sys_auto_is_null, + &sys_autocommit, + &sys_big_tables, + &sys_big_selects, + &sys_binlog_cache_size, + &sys_buffer_results, + &sys_bulk_insert_buff_size, + &sys_concurrent_insert, + &sys_connect_timeout, + &sys_convert_charset, + &sys_delay_key_write, + &sys_delayed_insert_limit, + &sys_delayed_insert_timeout, + &sys_delayed_queue_size, + &sys_flush, + &sys_flush_time, + &sys_foreign_key_checks, + &sys_identity, + &sys_insert_id, + &sys_interactive_timeout, + &sys_join_buffer_size, + &sys_key_buffer_size, + &sys_last_insert_id, + &sys_local_infile, + &sys_log_binlog, + &sys_log_off, + &sys_log_update, + &sys_log_warnings, + &sys_long_query_time, + &sys_low_priority_updates, + &sys_max_allowed_packet, + &sys_max_binlog_cache_size, + &sys_max_binlog_size, + &sys_max_connect_errors, + &sys_max_connections, + &sys_max_delayed_threads, + &sys_max_heap_table_size, + &sys_max_join_size, + &sys_max_sort_length, + &sys_max_tmp_tables, + &sys_max_user_connections, + &sys_max_write_lock_count, + &sys_myisam_max_extra_sort_file_size, + &sys_myisam_max_sort_file_size, + &sys_myisam_sort_buffer_size, + &sys_net_buffer_length, + &sys_net_read_timeout, + &sys_net_retry_count, + &sys_net_wait_timeout, + &sys_net_write_timeout, + &sys_query_cache_size, +#ifdef HAVE_QUERY_CACHE + &sys_query_cache_limit, + &sys_query_cache_type, +#endif /* HAVE_QUERY_CACHE */ + &sys_quote_show_create, + &sys_rand_seed1, + &sys_rand_seed2, + &sys_read_buff_size, + &sys_read_rnd_buff_size, + &sys_rpl_recovery_rank, + &sys_safe_updates, + &sys_select_limit, + &sys_server_id, + &sys_slave_compressed_protocol, + &sys_slave_net_timeout, + &sys_slave_skip_counter, + &sys_slow_launch_time, + &sys_sort_buffer, + &sys_sql_big_tables, + &sys_sql_low_priority_updates, + &sys_sql_max_join_size, + &sys_sql_warnings, + &sys_table_cache_size, + &sys_table_type, + &sys_thread_cache_size, + &sys_timestamp, + &sys_tmp_table_size, + &sys_tx_isolation, + &sys_unique_checks +}; + + +/* + Variables shown by SHOW variables in alphabetical order +*/ + +struct show_var_st init_vars[]= { + {"back_log", (char*) &back_log, SHOW_LONG}, + {"basedir", mysql_home, SHOW_CHAR}, +#ifdef HAVE_BERKELEY_DB + {"bdb_cache_size", (char*) &berkeley_cache_size, SHOW_LONG}, + {"bdb_log_buffer_size", (char*) &berkeley_log_buffer_size, SHOW_LONG}, + {"bdb_home", (char*) &berkeley_home, SHOW_CHAR_PTR}, + {"bdb_max_lock", (char*) &berkeley_max_lock, SHOW_LONG}, + {"bdb_logdir", (char*) &berkeley_logdir, SHOW_CHAR_PTR}, + {"bdb_shared_data", (char*) &berkeley_shared_data, SHOW_BOOL}, + {"bdb_tmpdir", (char*) &berkeley_tmpdir, SHOW_CHAR_PTR}, + {"bdb_version", (char*) DB_VERSION_STRING, SHOW_CHAR}, +#endif + {sys_binlog_cache_size.name,(char*) &sys_binlog_cache_size, SHOW_SYS}, + {sys_bulk_insert_buff_size.name,(char*) &sys_bulk_insert_buff_size,SHOW_SYS}, + {sys_charset.name, (char*) &sys_charset, SHOW_SYS}, + {"character_sets", (char*) &charsets_list, SHOW_CHAR_PTR}, + {sys_concurrent_insert.name,(char*) &sys_concurrent_insert, SHOW_SYS}, + {sys_connect_timeout.name, (char*) &sys_connect_timeout, SHOW_SYS}, + {sys_convert_charset.name, (char*) &sys_convert_charset, SHOW_SYS}, + {"datadir", mysql_real_data_home, SHOW_CHAR}, + {sys_delay_key_write.name, (char*) &sys_delay_key_write, SHOW_SYS}, + {sys_delayed_insert_limit.name, (char*) &sys_delayed_insert_limit,SHOW_SYS}, + {sys_delayed_insert_timeout.name, (char*) &sys_delayed_insert_timeout, SHOW_SYS}, + {sys_delayed_queue_size.name,(char*) &sys_delayed_queue_size, SHOW_SYS}, + {sys_flush.name, (char*) &sys_flush, SHOW_SYS}, + {sys_flush_time.name, (char*) &sys_flush_time, SHOW_SYS}, + {"ft_boolean_syntax", (char*) ft_boolean_syntax, SHOW_CHAR}, + {"ft_min_word_len", (char*) &ft_min_word_len, SHOW_LONG}, + {"ft_max_word_len", (char*) &ft_max_word_len, SHOW_LONG}, + {"ft_max_word_len_for_sort",(char*) &ft_max_word_len_for_sort, SHOW_LONG}, + {"ft_stopword_file", (char*) &ft_stopword_file, SHOW_CHAR_PTR}, + {"have_bdb", (char*) &have_berkeley_db, SHOW_HAVE}, + {"have_crypt", (char*) &have_crypt, SHOW_HAVE}, + {"have_innodb", (char*) &have_innodb, SHOW_HAVE}, + {"have_isam", (char*) &have_isam, SHOW_HAVE}, + {"have_raid", (char*) &have_raid, SHOW_HAVE}, + {"have_symlink", (char*) &have_symlink, SHOW_HAVE}, + {"have_openssl", (char*) &have_openssl, SHOW_HAVE}, + {"have_query_cache", (char*) &have_query_cache, SHOW_HAVE}, + {"init_file", (char*) &opt_init_file, SHOW_CHAR_PTR}, +#ifdef HAVE_INNOBASE_DB + {"innodb_additional_mem_pool_size", (char*) &innobase_additional_mem_pool_size, SHOW_LONG }, + {"innodb_buffer_pool_size", (char*) &innobase_buffer_pool_size, SHOW_LONG }, + {"innodb_data_file_path", (char*) &innobase_data_file_path, SHOW_CHAR_PTR}, + {"innodb_data_home_dir", (char*) &innobase_data_home_dir, SHOW_CHAR_PTR}, + {"innodb_file_io_threads", (char*) &innobase_file_io_threads, SHOW_LONG }, + {"innodb_force_recovery", (char*) &innobase_force_recovery, SHOW_LONG }, + {"innodb_thread_concurrency", (char*) &innobase_thread_concurrency, SHOW_LONG }, + {"innodb_flush_log_at_trx_commit", (char*) &innobase_flush_log_at_trx_commit, SHOW_INT}, + {"innodb_fast_shutdown", (char*) &innobase_fast_shutdown, SHOW_MY_BOOL}, + {"innodb_flush_method", (char*) &innobase_unix_file_flush_method, SHOW_CHAR_PTR}, + {"innodb_lock_wait_timeout", (char*) &innobase_lock_wait_timeout, SHOW_LONG }, + {"innodb_log_arch_dir", (char*) &innobase_log_arch_dir, SHOW_CHAR_PTR}, + {"innodb_log_archive", (char*) &innobase_log_archive, SHOW_MY_BOOL}, + {"innodb_log_buffer_size", (char*) &innobase_log_buffer_size, SHOW_LONG }, + {"innodb_log_file_size", (char*) &innobase_log_file_size, SHOW_LONG}, + {"innodb_log_files_in_group", (char*) &innobase_log_files_in_group, SHOW_LONG}, + {"innodb_log_group_home_dir", (char*) &innobase_log_group_home_dir, SHOW_CHAR_PTR}, + {"innodb_mirrored_log_groups", (char*) &innobase_mirrored_log_groups, SHOW_LONG}, +#endif + {sys_interactive_timeout.name,(char*) &sys_interactive_timeout, SHOW_SYS}, + {sys_join_buffer_size.name, (char*) &sys_join_buffer_size, SHOW_SYS}, + {sys_key_buffer_size.name, (char*) &sys_key_buffer_size, SHOW_SYS}, + {"language", language, SHOW_CHAR}, + {"large_files_support", (char*) &opt_large_files, SHOW_BOOL}, + {sys_local_infile.name, (char*) &sys_local_infile, SHOW_SYS}, +#ifdef HAVE_MLOCKALL + {"locked_in_memory", (char*) &locked_in_memory, SHOW_BOOL}, +#endif + {"log", (char*) &opt_log, SHOW_BOOL}, + {"log_update", (char*) &opt_update_log, SHOW_BOOL}, + {"log_bin", (char*) &opt_bin_log, SHOW_BOOL}, + {"log_slave_updates", (char*) &opt_log_slave_updates, SHOW_MY_BOOL}, + {"log_slow_queries", (char*) &opt_slow_log, SHOW_BOOL}, + {sys_log_warnings.name, (char*) &sys_log_warnings, SHOW_SYS}, + {sys_long_query_time.name, (char*) &sys_long_query_time, SHOW_SYS}, + {sys_low_priority_updates.name, (char*) &sys_low_priority_updates, SHOW_SYS}, + {"lower_case_table_names", (char*) &lower_case_table_names, SHOW_MY_BOOL}, + {sys_max_allowed_packet.name,(char*) &sys_max_allowed_packet, SHOW_SYS}, + {sys_max_binlog_cache_size.name,(char*) &sys_max_binlog_cache_size, SHOW_SYS}, + {sys_max_binlog_size.name, (char*) &sys_max_binlog_size, SHOW_SYS}, + {sys_max_connections.name, (char*) &sys_max_connections, SHOW_SYS}, + {sys_max_connect_errors.name, (char*) &sys_max_connect_errors, SHOW_SYS}, + {sys_max_delayed_threads.name,(char*) &sys_max_delayed_threads, SHOW_SYS}, + {sys_max_heap_table_size.name,(char*) &sys_max_heap_table_size, SHOW_SYS}, + {sys_max_join_size.name, (char*) &sys_max_join_size, SHOW_SYS}, + {sys_max_sort_length.name, (char*) &sys_max_sort_length, SHOW_SYS}, + {sys_max_user_connections.name,(char*) &sys_max_user_connections, SHOW_SYS}, + {sys_max_tmp_tables.name, (char*) &sys_max_tmp_tables, SHOW_SYS}, + {sys_max_write_lock_count.name, (char*) &sys_max_write_lock_count,SHOW_SYS}, + {sys_myisam_max_extra_sort_file_size.name, + (char*) &sys_myisam_max_extra_sort_file_size, + SHOW_SYS}, + {sys_myisam_max_sort_file_size.name, + (char*) &sys_myisam_max_sort_file_size, + SHOW_SYS}, + {"myisam_recover_options", (char*) &myisam_recover_options_str, SHOW_CHAR_PTR}, + {sys_myisam_sort_buffer_size.name, (char*) &sys_myisam_sort_buffer_size, SHOW_SYS}, +#ifdef __NT__ + {"named_pipe", (char*) &opt_enable_named_pipe, SHOW_MY_BOOL}, +#endif + {sys_net_buffer_length.name,(char*) &sys_net_buffer_length, SHOW_SYS}, + {sys_net_read_timeout.name, (char*) &sys_net_read_timeout, SHOW_SYS}, + {sys_net_retry_count.name, (char*) &sys_net_retry_count, SHOW_SYS}, + {sys_net_write_timeout.name,(char*) &sys_net_write_timeout, SHOW_SYS}, + {"open_files_limit", (char*) &open_files_limit, SHOW_LONG}, + {"pid_file", (char*) pidfile_name, SHOW_CHAR}, + {"log_error", (char*) log_error_file, SHOW_CHAR}, + {"port", (char*) &mysql_port, SHOW_INT}, + {"protocol_version", (char*) &protocol_version, SHOW_INT}, + {sys_read_buff_size.name, (char*) &sys_read_buff_size, SHOW_SYS}, + {sys_read_rnd_buff_size.name,(char*) &sys_read_rnd_buff_size, SHOW_SYS}, + {sys_rpl_recovery_rank.name,(char*) &sys_rpl_recovery_rank, SHOW_SYS}, +#ifdef HAVE_QUERY_CACHE + {sys_query_cache_limit.name,(char*) &sys_query_cache_limit, SHOW_SYS}, + {sys_query_cache_size.name, (char*) &sys_query_cache_size, SHOW_SYS}, + {sys_query_cache_type.name, (char*) &sys_query_cache_type, SHOW_SYS}, +#endif /* HAVE_QUERY_CACHE */ + {sys_server_id.name, (char*) &sys_server_id, SHOW_SYS}, + {sys_slave_net_timeout.name,(char*) &sys_slave_net_timeout, SHOW_SYS}, + {"skip_external_locking", (char*) &my_disable_locking, SHOW_MY_BOOL}, + {"skip_networking", (char*) &opt_disable_networking, SHOW_BOOL}, + {"skip_show_database", (char*) &opt_skip_show_db, SHOW_BOOL}, + {sys_slow_launch_time.name, (char*) &sys_slow_launch_time, SHOW_SYS}, +#ifdef HAVE_SYS_UN_H + {"socket", (char*) &mysql_unix_port, SHOW_CHAR_PTR}, +#endif + {sys_sort_buffer.name, (char*) &sys_sort_buffer, SHOW_SYS}, + {"sql_mode", (char*) &opt_sql_mode, SHOW_LONG}, + {"table_cache", (char*) &table_cache_size, SHOW_LONG}, + {sys_table_type.name, (char*) &sys_table_type, SHOW_SYS}, + {sys_thread_cache_size.name,(char*) &sys_thread_cache_size, SHOW_SYS}, +#ifdef HAVE_THR_SETCONCURRENCY + {"thread_concurrency", (char*) &concurrency, SHOW_LONG}, +#endif + {"thread_stack", (char*) &thread_stack, SHOW_LONG}, + {sys_tx_isolation.name, (char*) &sys_tx_isolation, SHOW_SYS}, +#ifdef HAVE_TZNAME + {"timezone", time_zone, SHOW_CHAR}, +#endif + {sys_tmp_table_size.name, (char*) &sys_tmp_table_size, SHOW_SYS}, + {"tmpdir", (char*) &mysql_tmpdir, SHOW_CHAR_PTR}, + {"version", server_version, SHOW_CHAR}, + {sys_net_wait_timeout.name, (char*) &sys_net_wait_timeout, SHOW_SYS}, + {NullS, NullS, SHOW_LONG} +}; + +/* + Functions to check and update variables +*/ + +/* + The following 3 functions need to be changed in 4.1 when we allow + one to change character sets +*/ + +static bool sys_check_charset(THD *thd, set_var *var) +{ + return 0; +} + + +static bool sys_update_charset(THD *thd, set_var *var) +{ + return 0; +} + + +static void sys_set_default_charset(THD *thd, enum_var_type type) +{ +} + + +/* + If one sets the LOW_PRIORIY UPDATES flag, we also must change the + used lock type +*/ + +static void fix_low_priority_updates(THD *thd, enum_var_type type) +{ + if (type != OPT_GLOBAL) + thd->update_lock_default= (thd->variables.low_priority_updates ? + TL_WRITE_LOW_PRIORITY : TL_WRITE); +} + + +/* + Set the OPTION_BIG_SELECTS flag if max_join_size == HA_POS_ERROR +*/ + +static void fix_max_join_size(THD *thd, enum_var_type type) +{ + if (type != OPT_GLOBAL) + { + if (thd->variables.max_join_size == HA_POS_ERROR) + thd->options|= OPTION_BIG_SELECTS; + else + thd->options&= ~OPTION_BIG_SELECTS; + } +} + + +/* + If one doesn't use the SESSION modifier, the isolation level + is only active for the next command +*/ + +static void fix_tx_isolation(THD *thd, enum_var_type type) +{ + if (type == OPT_SESSION) + thd->session_tx_isolation= ((enum_tx_isolation) + thd->variables.tx_isolation); +} + + +/* + If we are changing the thread variable, we have to copy it to NET too +*/ + +static void fix_net_read_timeout(THD *thd, enum_var_type type) +{ + if (type != OPT_GLOBAL) + thd->net.read_timeout=thd->variables.net_read_timeout; +} + + +static void fix_net_write_timeout(THD *thd, enum_var_type type) +{ + if (type != OPT_GLOBAL) + thd->net.write_timeout=thd->variables.net_write_timeout; +} + +static void fix_net_retry_count(THD *thd, enum_var_type type) +{ + if (type != OPT_GLOBAL) + thd->net.retry_count=thd->variables.net_retry_count; +} + + +static void fix_query_cache_size(THD *thd, enum_var_type type) +{ +#ifdef HAVE_QUERY_CACHE + query_cache.resize(query_cache_size); +#endif +} + + +static void fix_key_buffer_size(THD *thd, enum_var_type type) +{ + ha_resize_key_cache(); +} + + +void fix_delay_key_write(THD *thd, enum_var_type type) +{ + switch ((enum_delay_key_write) delay_key_write_options) { + case DELAY_KEY_WRITE_NONE: + myisam_delay_key_write=0; + break; + case DELAY_KEY_WRITE_ON: + myisam_delay_key_write=1; + break; + case DELAY_KEY_WRITE_ALL: + myisam_delay_key_write=1; + ha_open_options|= HA_OPEN_DELAY_KEY_WRITE; + break; + } +} + + +bool sys_var_long_ptr::update(THD *thd, set_var *var) +{ + ulonglong tmp= var->value->val_int(); + if (option_limits) + *value= (ulong) getopt_ull_limit_value(tmp, option_limits); + else + *value= (ulong) tmp; + return 0; +} + + +void sys_var_long_ptr::set_default(THD *thd, enum_var_type type) +{ + *value= (ulong) option_limits->def_value; +} + + +bool sys_var_ulonglong_ptr::update(THD *thd, set_var *var) +{ + ulonglong tmp= var->value->val_int(); + if (option_limits) + *value= (ulonglong) getopt_ull_limit_value(tmp, option_limits); + else + *value= (ulonglong) tmp; + return 0; +} + + +void sys_var_ulonglong_ptr::set_default(THD *thd, enum_var_type type) +{ + *value= (ulonglong) option_limits->def_value; +} + + +bool sys_var_bool_ptr::update(THD *thd, set_var *var) +{ + *value= (my_bool) var->save_result.ulong_value; + return 0; +} + + +void sys_var_bool_ptr::set_default(THD *thd, enum_var_type type) +{ + *value= (my_bool) option_limits->def_value; +} + + +bool sys_var_enum::update(THD *thd, set_var *var) +{ + *value= (uint) var->save_result.ulong_value; + return 0; +} + + +byte *sys_var_enum::value_ptr(THD *thd, enum_var_type type) +{ + return (byte*) enum_names->type_names[*value]; +} + + +bool sys_var_thd_ulong::update(THD *thd, set_var *var) +{ + ulonglong tmp= var->value->val_int(); + + /* Don't use bigger value than given with --maximum-variable-name=.. */ + if ((ulong) tmp > max_system_variables.*offset) + tmp= max_system_variables.*offset; + + if (option_limits) + tmp= (ulong) getopt_ull_limit_value(tmp, option_limits); + if (var->type == OPT_GLOBAL) + global_system_variables.*offset= (ulong) tmp; + else + thd->variables.*offset= (ulong) tmp; + return 0; +} + + +void sys_var_thd_ulong::set_default(THD *thd, enum_var_type type) +{ + if (type == OPT_GLOBAL) + { + /* We will not come here if option_limits is not set */ + global_system_variables.*offset= (ulong) option_limits->def_value; + } + else + thd->variables.*offset= global_system_variables.*offset; +} + + +byte *sys_var_thd_ulong::value_ptr(THD *thd, enum_var_type type) +{ + if (type == OPT_GLOBAL) + return (byte*) &(global_system_variables.*offset); + return (byte*) &(thd->variables.*offset); +} + + +bool sys_var_thd_ha_rows::update(THD *thd, set_var *var) +{ + ulonglong tmp= var->value->val_int(); + + /* Don't use bigger value than given with --maximum-variable-name=.. */ + if ((ha_rows) tmp > max_system_variables.*offset) + tmp= max_system_variables.*offset; + + if (option_limits) + tmp= (ha_rows) getopt_ull_limit_value(tmp, option_limits); + if (var->type == OPT_GLOBAL) + { + /* Lock is needed to make things safe on 32 bit systems */ + pthread_mutex_lock(&LOCK_global_system_variables); + global_system_variables.*offset= (ha_rows) tmp; + pthread_mutex_unlock(&LOCK_global_system_variables); + } + else + thd->variables.*offset= (ha_rows) tmp; + return 0; +} + + +void sys_var_thd_ha_rows::set_default(THD *thd, enum_var_type type) +{ + if (type == OPT_GLOBAL) + { + /* We will not come here if option_limits is not set */ + pthread_mutex_lock(&LOCK_global_system_variables); + global_system_variables.*offset= (ha_rows) option_limits->def_value; + pthread_mutex_unlock(&LOCK_global_system_variables); + } + else + thd->variables.*offset= global_system_variables.*offset; +} + + +byte *sys_var_thd_ha_rows::value_ptr(THD *thd, enum_var_type type) +{ + if (type == OPT_GLOBAL) + return (byte*) &(global_system_variables.*offset); + return (byte*) &(thd->variables.*offset); +} + + +bool sys_var_thd_ulonglong::update(THD *thd, set_var *var) +{ + if (var->type == OPT_GLOBAL) + { + /* Lock is needed to make things safe on 32 bit systems */ + pthread_mutex_lock(&LOCK_global_system_variables); + global_system_variables.*offset= var->value->val_int(); + pthread_mutex_unlock(&LOCK_global_system_variables); + } + else + thd->variables.*offset= var->value->val_int(); + return 0; +} + + +void sys_var_thd_ulonglong::set_default(THD *thd, enum_var_type type) +{ + if (type == OPT_GLOBAL) + { + pthread_mutex_lock(&LOCK_global_system_variables); + global_system_variables.*offset= (ulong) option_limits->def_value; + pthread_mutex_unlock(&LOCK_global_system_variables); + } + else + thd->variables.*offset= global_system_variables.*offset; +} + + +byte *sys_var_thd_ulonglong::value_ptr(THD *thd, enum_var_type type) +{ + if (type == OPT_GLOBAL) + return (byte*) &(global_system_variables.*offset); + return (byte*) &(thd->variables.*offset); +} + + +bool sys_var_thd_bool::update(THD *thd, set_var *var) +{ + if (var->type == OPT_GLOBAL) + global_system_variables.*offset= (my_bool) var->save_result.ulong_value; + else + thd->variables.*offset= (my_bool) var->save_result.ulong_value; + return 0; +} + + +void sys_var_thd_bool::set_default(THD *thd, enum_var_type type) +{ + if (type == OPT_GLOBAL) + global_system_variables.*offset= (my_bool) option_limits->def_value; + else + thd->variables.*offset= global_system_variables.*offset; +} + + +byte *sys_var_thd_bool::value_ptr(THD *thd, enum_var_type type) +{ + if (type == OPT_GLOBAL) + return (byte*) &(global_system_variables.*offset); + return (byte*) &(thd->variables.*offset); +} + + +bool sys_var::check_enum(THD *thd, set_var *var, TYPELIB *enum_names) +{ + char buff[80], *value; + String str(buff,sizeof(buff)), *res; + + if (var->value->result_type() == STRING_RESULT) + { + if (!(res=var->value->val_str(&str)) || + ((long) (var->save_result.ulong_value= + (ulong) find_type(res->c_ptr(), enum_names, 3)-1)) + < 0) + { + value=res->c_ptr(); + goto err; + } + } + else + { + ulonglong tmp=var->value->val_int(); + if (tmp >= enum_names->count) + { + llstr(tmp,buff); + value=buff; // Wrong value is here + goto err; + } + var->save_result.ulong_value= (ulong) tmp; // Save for update + } + return 0; + +err: + my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name, value); + return 1; +} + +/* + Return an Item for a variable. Used with @@[global.]variable_name + + If type is not given, return local value if exists, else global + + We have to use netprintf() instead of my_error() here as this is + called on the parsing stage. +*/ + +Item *sys_var::item(THD *thd, enum_var_type var_type) +{ + if (check_type(var_type)) + { + if (var_type != OPT_DEFAULT) + { + net_printf(&thd->net, + var_type == OPT_GLOBAL ? ER_LOCAL_VARIABLE : + ER_GLOBAL_VARIABLE, name); + return 0; + } + /* As there was no local variable, return the global value */ + var_type= OPT_GLOBAL; + } + switch (type()) { + case SHOW_LONG: + return new Item_uint((int32) *(ulong*) value_ptr(thd, var_type)); + case SHOW_LONGLONG: + return new Item_int(*(longlong*) value_ptr(thd, var_type)); + case SHOW_HA_ROWS: + return new Item_int((longlong) *(ha_rows*) value_ptr(thd, var_type)); + case SHOW_MY_BOOL: + return new Item_int((int32) *(my_bool*) value_ptr(thd, var_type),1); + case SHOW_CHAR: + { + char *str= (char*) value_ptr(thd, var_type); + return new Item_string(str,strlen(str)); + } + default: + net_printf(&thd->net, ER_VAR_CANT_BE_READ, name); + } + return 0; +} + + +bool sys_var_thd_enum::update(THD *thd, set_var *var) +{ + if (var->type == OPT_GLOBAL) + global_system_variables.*offset= var->save_result.ulong_value; + else + thd->variables.*offset= var->save_result.ulong_value; + return 0; +} + + +void sys_var_thd_enum::set_default(THD *thd, enum_var_type type) +{ + if (type == OPT_GLOBAL) + global_system_variables.*offset= (ulong) option_limits->def_value; + else + thd->variables.*offset= global_system_variables.*offset; +} + + +byte *sys_var_thd_enum::value_ptr(THD *thd, enum_var_type type) +{ + ulong tmp= ((type == OPT_GLOBAL) ? + global_system_variables.*offset : + thd->variables.*offset); + return (byte*) enum_names->type_names[tmp]; +} + + +bool sys_var_thd_bit::update(THD *thd, set_var *var) +{ + int res= (*update_func)(thd, var); + thd->lex.select_lex.options=thd->options; + return res; +} + + +byte *sys_var_thd_bit::value_ptr(THD *thd, enum_var_type type) +{ + /* + If reverse is 0 (default) return 1 if bit is set. + If reverse is 1, return 0 if bit is set + */ + thd->sys_var_tmp.my_bool_value= ((thd->options & bit_flag) ? + !reverse : reverse); + return (byte*) &thd->sys_var_tmp.my_bool_value; +} + + +bool sys_var_thd_conv_charset::check(THD *thd, set_var *var) +{ + CONVERT *tmp; + char buff[80]; + String str(buff,sizeof(buff)), *res; + + if (!var->value) // Default value + { + var->save_result.convert= (var->type != OPT_GLOBAL ? + global_system_variables.convert_set + : (CONVERT*) 0); + return 0; + } + if (!(res=var->value->val_str(&str))) + res= &empty_string; + + if (!(tmp=get_convert_set(res->c_ptr()))) + { + my_error(ER_UNKNOWN_CHARACTER_SET, MYF(0), res->c_ptr()); + return 1; + } + var->save_result.convert=tmp; // Save for update + return 0; +} + + +bool sys_var_thd_conv_charset::update(THD *thd, set_var *var) +{ + if (var->type == OPT_GLOBAL) + global_system_variables.convert_set= var->save_result.convert; + else + thd->lex.convert_set= thd->variables.convert_set= + var->save_result.convert; + return 0; +} + + +byte *sys_var_thd_conv_charset::value_ptr(THD *thd, enum_var_type type) +{ + CONVERT *conv= ((type == OPT_GLOBAL) ? + global_system_variables.convert_set : + thd->variables.convert_set); + return conv ? (byte*) conv->name : (byte*) ""; +} + + + +bool sys_var_timestamp::update(THD *thd, set_var *var) +{ + thd->set_time((time_t) var->value->val_int()); + return 0; +} + + +void sys_var_timestamp::set_default(THD *thd, enum_var_type type) +{ + thd->user_time=0; +} + + +byte *sys_var_timestamp::value_ptr(THD *thd, enum_var_type type) +{ + thd->sys_var_tmp.long_value= (long) thd->start_time; + return (byte*) &thd->sys_var_tmp.long_value; +} + + +bool sys_var_last_insert_id::update(THD *thd, set_var *var) +{ + thd->insert_id(var->value->val_int()); + return 0; +} + + +byte *sys_var_last_insert_id::value_ptr(THD *thd, enum_var_type type) +{ + thd->sys_var_tmp.long_value= (long) thd->insert_id(); + return (byte*) &thd->last_insert_id; +} + + +bool sys_var_insert_id::update(THD *thd, set_var *var) +{ + thd->next_insert_id=var->value->val_int(); + return 0; +} + + +byte *sys_var_insert_id::value_ptr(THD *thd, enum_var_type type) +{ + return (byte*) &thd->current_insert_id; +} + + +bool sys_var_slave_skip_counter::check(THD *thd, set_var *var) +{ + int result= 0; + LOCK_ACTIVE_MI; + pthread_mutex_lock(&active_mi->rli.run_lock); + if (active_mi->rli.slave_running) + { + my_error(ER_SLAVE_MUST_STOP, MYF(0)); + result=1; + } + pthread_mutex_unlock(&active_mi->rli.run_lock); + UNLOCK_ACTIVE_MI; + return result; +} + + +bool sys_var_slave_skip_counter::update(THD *thd, set_var *var) +{ + LOCK_ACTIVE_MI; + pthread_mutex_lock(&active_mi->rli.run_lock); + /* + The following test should normally never be true as we test this + in the check function; To be safe against multiple + SQL_SLAVE_SKIP_COUNTER request, we do the check anyway + */ + if (!active_mi->rli.slave_running) + { + pthread_mutex_lock(&active_mi->rli.data_lock); + active_mi->rli.slave_skip_counter= (ulong) var->value->val_int(); + pthread_mutex_unlock(&active_mi->rli.data_lock); + } + pthread_mutex_unlock(&active_mi->rli.run_lock); + UNLOCK_ACTIVE_MI; + return 0; +} + + +bool sys_var_rand_seed1::update(THD *thd, set_var *var) +{ + thd->rand.seed1= (ulong) var->value->val_int(); + return 0; +} + +bool sys_var_rand_seed2::update(THD *thd, set_var *var) +{ + thd->rand.seed2= (ulong) var->value->val_int(); + return 0; +} + + +/* + Functions to update thd->options bits +*/ + +static bool set_option_bit(THD *thd, set_var *var) +{ + sys_var_thd_bit *sys_var= ((sys_var_thd_bit*) var->var); + if ((var->save_result.ulong_value != 0) == sys_var->reverse) + thd->options&= ~sys_var->bit_flag; + else + thd->options|= sys_var->bit_flag; + return 0; +} + + +static bool set_option_autocommit(THD *thd, set_var *var) +{ + /* The test is negative as the flag we use is NOT autocommit */ + + ulong org_options=thd->options; + + if (var->save_result.ulong_value != 0) + thd->options&= ~((sys_var_thd_bit*) var->var)->bit_flag; + else + thd->options|= ((sys_var_thd_bit*) var->var)->bit_flag; + + if ((org_options ^ thd->options) & OPTION_NOT_AUTOCOMMIT) + { + if ((org_options & OPTION_NOT_AUTOCOMMIT)) + { + /* We changed to auto_commit mode */ + thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE); + thd->server_status|= SERVER_STATUS_AUTOCOMMIT; + if (ha_commit(thd)) + return 1; + } + else + { + thd->options&= ~(ulong) (OPTION_STATUS_NO_TRANS_UPDATE); + thd->server_status&= ~SERVER_STATUS_AUTOCOMMIT; + } + } + return 0; +} + + +static bool set_log_update(THD *thd, set_var *var) +{ + if (opt_sql_bin_update) + ((sys_var_thd_bit*) var->var)->bit_flag|= (OPTION_BIN_LOG | + OPTION_UPDATE_LOG); + set_option_bit(thd, var); + return 0; +} + + +/**************************************************************************** + Main handling of variables: + - Initialisation + - Searching during parsing + - Update loop +****************************************************************************/ + +/* + Find variable name in option my_getopt structure used for command line args + + SYNOPSIS + find_option() + opt option structure array to search in + name variable name + + RETURN VALUES + 0 Error + ptr pointer to option structure +*/ + +static struct my_option *find_option(struct my_option *opt, const char *name) +{ + uint length=strlen(name); + for (; opt->name; opt++) + { + if (!getopt_compare_strings(opt->name, name, length) && + !opt->name[length]) + { + /* + Only accept the option if one can set values through it. + If not, there is no default value or limits in the option. + */ + return (opt->value) ? opt : 0; + } + } + return 0; +} + + +/* + Return variable name and length for hashing of variables +*/ + +static byte *get_sys_var_length(const sys_var *var, uint *length, + my_bool first) +{ + *length= var->name_length; + return (byte*) var->name; +} + + +/* + Initialises sys variables and put them in system_variable_hash +*/ + + +void set_var_init() +{ + extern struct my_option my_long_options[]; // From mysqld + + hash_init(&system_variable_hash,array_elements(sys_variables),0,0, + (hash_get_key) get_sys_var_length,0, HASH_CASE_INSENSITIVE); + sys_var **var, **end; + for (var= sys_variables, end= sys_variables+array_elements(sys_variables) ; + var < end; + var++) + { + (*var)->name_length= strlen((*var)->name); + (*var)->option_limits= find_option(my_long_options, (*var)->name); + hash_insert(&system_variable_hash, (byte*) *var); + } + + /* + Special cases + Needed because MySQL can't find the limits for a variable it it has + a different name than the command line option. + As these variables are deprecated, this code will disappear soon... + */ + sys_sql_max_join_size.option_limits= sys_max_join_size.option_limits; +} + + +void set_var_free() +{ + hash_free(&system_variable_hash); +} + + +/* + Find a user set-table variable + + SYNOPSIS + find_sys_var() + str Name of system variable to find + length Length of variable. zero means that we should use strlen() + on the variable + + NOTE + We have to use net_printf() as this is called during the parsing stage + + RETURN VALUES + pointer pointer to variable definitions + 0 Unknown variable (error message is given) +*/ + +sys_var *find_sys_var(const char *str, uint length) +{ + sys_var *var= (sys_var*) hash_search(&system_variable_hash, + (byte*) str, + length ? length : + strlen(str)); + if (!var) + net_printf(¤t_thd->net, ER_UNKNOWN_SYSTEM_VARIABLE, (char*) str); + return var; +} + + +/* + Execute update of all variables + + SYNOPSIS + + sql_set + THD Thread id + set_var List of variables to update + + DESCRIPTION + First run a check of all variables that all updates will go ok. + If yes, then execute all updates, returning an error if any one failed. + + This should ensure that in all normal cases none all or variables are + updated + + RETURN VALUE + 0 ok + 1 ERROR, message sent (normally no variables was updated) + -1 ERROR, message not sent +*/ + +int sql_set_variables(THD *thd, List<set_var_base> *var_list) +{ + int error= 0; + List_iterator<set_var_base> it(*var_list); + + set_var_base *var; + while ((var=it++)) + { + if ((error=var->check(thd))) + return error; + } + it.rewind(); + while ((var=it++)) + error|= var->update(thd); // Returns 0, -1 or 1 + return error; +} + + +/***************************************************************************** + Functions to handle SET mysql_internal_variable=const_expr +*****************************************************************************/ + +int set_var::check(THD *thd) +{ + if (var->check_type(type)) + { + my_error(type == OPT_GLOBAL ? ER_LOCAL_VARIABLE : ER_GLOBAL_VARIABLE, + MYF(0), + var->name); + return -1; + } + if ((type == OPT_GLOBAL && check_global_access(thd, SUPER_ACL))) + return 1; + + /* value is a NULL pointer if we are using SET ... = DEFAULT */ + if (!value) + { + if (var->check_default(type)) + { + my_error(ER_NO_DEFAULT, MYF(0), var->name); + return -1; + } + return 0; + } + + if (value->fix_fields(thd,0)) + return -1; + if (var->check_update_type(value->result_type())) + { + my_error(ER_WRONG_TYPE_FOR_VAR, MYF(0), var->name); + return -1; + } + return var->check(thd, this) ? -1 : 0; +} + + +int set_var::update(THD *thd) +{ + if (!value) + var->set_default(thd, type); + else if (var->update(thd, this)) + return -1; // should never happen + if (var->after_update) + (*var->after_update)(thd, type); + return 0; +} + + +/***************************************************************************** + Functions to handle SET @user_variable=const_expr +*****************************************************************************/ + +int set_var_user::check(THD *thd) +{ + return user_var_item->fix_fields(thd,0) ? -1 : 0; +} + + +int set_var_user::update(THD *thd) +{ + if (user_var_item->update()) + { + /* Give an error if it's not given already */ + my_error(ER_SET_CONSTANTS_ONLY, MYF(0)); + return -1; + } + return 0; +} + + +/***************************************************************************** + Functions to handle SET PASSWORD +*****************************************************************************/ + +int set_var_password::check(THD *thd) +{ + if (!user->host.str) + user->host.str= (char*) thd->host_or_ip; + /* Returns 1 as the function sends error to client */ + return check_change_password(thd, user->host.str, user->user.str) ? 1 : 0; +} + +int set_var_password::update(THD *thd) +{ + /* Returns 1 as the function sends error to client */ + return (change_password(thd, user->host.str, user->user.str, password) ? + 1 : 0); +} + +/**************************************************************************** + Used templates +****************************************************************************/ + +#ifdef __GNUC__ +template class List<set_var_base>; +template class List_iterator<set_var_base>; +#endif diff --git a/sql/set_var.h b/sql/set_var.h new file mode 100644 index 00000000000..c74f1e827bd --- /dev/null +++ b/sql/set_var.h @@ -0,0 +1,491 @@ +/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* Classes to support the SET command */ + +#ifdef __GNUC__ +#pragma interface /* gcc class implementation */ +#endif + +/**************************************************************************** + Variables that are changable runtime are declared using the + following classes +****************************************************************************/ + +class sys_var; +class set_var; +typedef struct system_variables SV; +extern TYPELIB bool_typelib, delay_key_write_typelib; + +enum enum_var_type +{ + OPT_DEFAULT, OPT_SESSION, OPT_GLOBAL +}; + +typedef bool (*sys_check_func)(THD *, set_var *); +typedef bool (*sys_update_func)(THD *, set_var *); +typedef void (*sys_after_update_func)(THD *,enum_var_type); +typedef void (*sys_set_default_func)(THD *, enum_var_type); + +class sys_var +{ +public: + struct my_option *option_limits; /* Updated by by set_var_init() */ + uint name_length; /* Updated by by set_var_init() */ + const char *name; + sys_after_update_func after_update; + sys_var(const char *name_arg) :name(name_arg),after_update(0) + {} + sys_var(const char *name_arg,sys_after_update_func func) + :name(name_arg),after_update(func) + {} + virtual ~sys_var() {} + virtual bool check(THD *thd, set_var *var) { return 0; } + bool check_enum(THD *thd, set_var *var, TYPELIB *enum_names); + virtual bool update(THD *thd, set_var *var)=0; + virtual void set_default(THD *thd, enum_var_type type) {} + virtual SHOW_TYPE type() { return SHOW_UNDEF; } + virtual byte *value_ptr(THD *thd, enum_var_type type) { return 0; } + virtual bool check_type(enum_var_type type) + { return type != OPT_GLOBAL; } /* Error if not GLOBAL */ + virtual bool check_update_type(Item_result type) + { return type != INT_RESULT; } /* Assume INT */ + virtual bool check_default(enum_var_type type) + { return option_limits == 0; } + Item *item(THD *thd, enum_var_type type); +}; + + +class sys_var_long_ptr :public sys_var +{ +public: + ulong *value; + sys_var_long_ptr(const char *name_arg, ulong *value_ptr) + :sys_var(name_arg),value(value_ptr) {} + sys_var_long_ptr(const char *name_arg, ulong *value_ptr, + sys_after_update_func func) + :sys_var(name_arg,func), value(value_ptr) {} + bool update(THD *thd, set_var *var); + void set_default(THD *thd, enum_var_type type); + SHOW_TYPE type() { return SHOW_LONG; } + byte *value_ptr(THD *thd, enum_var_type type) { return (byte*) value; } +}; + + +class sys_var_ulonglong_ptr :public sys_var +{ +public: + ulonglong *value; + sys_var_ulonglong_ptr(const char *name_arg, ulonglong *value_ptr) + :sys_var(name_arg),value(value_ptr) {} + sys_var_ulonglong_ptr(const char *name_arg, ulonglong *value_ptr, + sys_after_update_func func) + :sys_var(name_arg,func), value(value_ptr) {} + bool update(THD *thd, set_var *var); + void set_default(THD *thd, enum_var_type type); + SHOW_TYPE type() { return SHOW_LONGLONG; } + byte *value_ptr(THD *thd, enum_var_type type) { return (byte*) value; } +}; + + +class sys_var_bool_ptr :public sys_var +{ +public: + my_bool *value; + sys_var_bool_ptr(const char *name_arg, my_bool *value_arg) + :sys_var(name_arg),value(value_arg) + {} + bool check(THD *thd, set_var *var) + { + return check_enum(thd, var, &bool_typelib); + } + bool update(THD *thd, set_var *var); + void set_default(THD *thd, enum_var_type type); + SHOW_TYPE type() { return SHOW_MY_BOOL; } + byte *value_ptr(THD *thd, enum_var_type type) { return (byte*) value; } + bool check_update_type(Item_result type) { return 0; } +}; + + +class sys_var_str :public sys_var +{ +public: + char *value; // Pointer to allocated string + sys_check_func check_func; + sys_update_func update_func; + sys_set_default_func set_default_func; + sys_var_str(const char *name_arg, + sys_check_func check_func_arg, + sys_update_func update_func_arg, + sys_set_default_func set_default_func_arg) + :sys_var(name_arg), check_func(check_func_arg), + update_func(update_func_arg),set_default_func(set_default_func_arg) + {} + bool check(THD *thd, set_var *var) + { + return check_func ? (*check_func)(thd, var) : 0; + } + bool update(THD *thd, set_var *var) + { + return (*update_func)(thd, var); + } + void set_default(THD *thd, enum_var_type type) + { + (*set_default_func)(thd, type); + } + SHOW_TYPE type() { return SHOW_CHAR; } + byte *value_ptr(THD *thd, enum_var_type type) { return (byte*) value; } + bool check_update_type(Item_result type) + { + return type != STRING_RESULT; /* Only accept strings */ + } + bool check_default(enum_var_type type) { return 0; } +}; + + +class sys_var_enum :public sys_var +{ + uint *value; + TYPELIB *enum_names; +public: + sys_var_enum(const char *name_arg, uint *value_arg, + TYPELIB *typelib, sys_after_update_func func) + :sys_var(name_arg,func), value(value_arg), enum_names(typelib) + {} + bool check(THD *thd, set_var *var) + { + return check_enum(thd, var, enum_names); + } + bool update(THD *thd, set_var *var); + SHOW_TYPE type() { return SHOW_CHAR; } + byte *value_ptr(THD *thd, enum_var_type type); + bool check_update_type(Item_result type) { return 0; } +}; + + +class sys_var_thd :public sys_var +{ +public: + sys_var_thd(const char *name_arg) + :sys_var(name_arg) + {} + sys_var_thd(const char *name_arg, sys_after_update_func func) + :sys_var(name_arg,func) + {} + bool check_type(enum_var_type type) { return 0; } + bool check_default(enum_var_type type) + { + return type == OPT_GLOBAL && !option_limits; + } +}; + + +class sys_var_thd_ulong :public sys_var_thd +{ +public: + ulong SV::*offset; + sys_var_thd_ulong(const char *name_arg, ulong SV::*offset_arg) + :sys_var_thd(name_arg), offset(offset_arg) + {} + sys_var_thd_ulong(const char *name_arg, ulong SV::*offset_arg, + sys_after_update_func func) + :sys_var_thd(name_arg,func), offset(offset_arg) + {} + bool update(THD *thd, set_var *var); + void set_default(THD *thd, enum_var_type type); + SHOW_TYPE type() { return SHOW_LONG; } + byte *value_ptr(THD *thd, enum_var_type type); +}; + + +class sys_var_thd_ha_rows :public sys_var_thd +{ +public: + ha_rows SV::*offset; + sys_var_thd_ha_rows(const char *name_arg, ha_rows SV::*offset_arg) + :sys_var_thd(name_arg), offset(offset_arg) + {} + sys_var_thd_ha_rows(const char *name_arg, ha_rows SV::*offset_arg, + sys_after_update_func func) + :sys_var_thd(name_arg,func), offset(offset_arg) + {} + bool update(THD *thd, set_var *var); + void set_default(THD *thd, enum_var_type type); + SHOW_TYPE type() { return SHOW_HA_ROWS; } + byte *value_ptr(THD *thd, enum_var_type type); +}; + + +class sys_var_thd_ulonglong :public sys_var_thd +{ +public: + ulonglong SV::*offset; + sys_var_thd_ulonglong(const char *name_arg, ulonglong SV::*offset_arg) + :sys_var_thd(name_arg), offset(offset_arg) + {} + bool update(THD *thd, set_var *var); + void set_default(THD *thd, enum_var_type type); + SHOW_TYPE type() { return SHOW_LONGLONG; } + byte *value_ptr(THD *thd, enum_var_type type); +}; + + +class sys_var_thd_bool :public sys_var_thd +{ +public: + my_bool SV::*offset; + sys_var_thd_bool(const char *name_arg, my_bool SV::*offset_arg) + :sys_var_thd(name_arg), offset(offset_arg) + {} + sys_var_thd_bool(const char *name_arg, my_bool SV::*offset_arg, + sys_after_update_func func) + :sys_var_thd(name_arg,func), offset(offset_arg) + {} + bool update(THD *thd, set_var *var); + void set_default(THD *thd, enum_var_type type); + SHOW_TYPE type() { return SHOW_MY_BOOL; } + byte *value_ptr(THD *thd, enum_var_type type); + bool check(THD *thd, set_var *var) + { + return check_enum(thd, var, &bool_typelib); + } + bool check_update_type(Item_result type) { return 0; } +}; + + +class sys_var_thd_enum :public sys_var_thd +{ + ulong SV::*offset; + TYPELIB *enum_names; +public: + sys_var_thd_enum(const char *name_arg, ulong SV::*offset_arg, + TYPELIB *typelib) + :sys_var_thd(name_arg), offset(offset_arg), enum_names(typelib) + {} + sys_var_thd_enum(const char *name_arg, ulong SV::*offset_arg, + TYPELIB *typelib, + sys_after_update_func func) + :sys_var_thd(name_arg,func), offset(offset_arg), enum_names(typelib) + {} + bool check(THD *thd, set_var *var) + { + return check_enum(thd, var, enum_names); + } + bool update(THD *thd, set_var *var); + void set_default(THD *thd, enum_var_type type); + SHOW_TYPE type() { return SHOW_CHAR; } + byte *value_ptr(THD *thd, enum_var_type type); + bool check_update_type(Item_result type) { return 0; } +}; + + +class sys_var_thd_bit :public sys_var_thd +{ + sys_update_func update_func; +public: + ulong bit_flag; + bool reverse; + sys_var_thd_bit(const char *name_arg, sys_update_func func, ulong bit, + bool reverse_arg=0) + :sys_var_thd(name_arg), update_func(func), bit_flag(bit), + reverse(reverse_arg) + {} + bool check(THD *thd, set_var *var) + { + return check_enum(thd, var, &bool_typelib); + } + bool update(THD *thd, set_var *var); + bool check_update_type(Item_result type) { return 0; } + bool check_type(enum_var_type type) { return type == OPT_GLOBAL; } + SHOW_TYPE type() { return SHOW_MY_BOOL; } + byte *value_ptr(THD *thd, enum_var_type type); +}; + + +/* some variables that require special handling */ + +class sys_var_timestamp :public sys_var +{ +public: + sys_var_timestamp(const char *name_arg) :sys_var(name_arg) {} + bool update(THD *thd, set_var *var); + void set_default(THD *thd, enum_var_type type); + bool check_type(enum_var_type type) { return type == OPT_GLOBAL; } + bool check_default(enum_var_type type) { return 0; } + SHOW_TYPE type() { return SHOW_LONG; } + byte *value_ptr(THD *thd, enum_var_type type); +}; + + +class sys_var_last_insert_id :public sys_var +{ +public: + sys_var_last_insert_id(const char *name_arg) :sys_var(name_arg) {} + bool update(THD *thd, set_var *var); + bool check_type(enum_var_type type) { return type == OPT_GLOBAL; } + SHOW_TYPE type() { return SHOW_LONGLONG; } + byte *value_ptr(THD *thd, enum_var_type type); +}; + + +class sys_var_insert_id :public sys_var +{ +public: + sys_var_insert_id(const char *name_arg) :sys_var(name_arg) {} + bool update(THD *thd, set_var *var); + bool check_type(enum_var_type type) { return type == OPT_GLOBAL; } + SHOW_TYPE type() { return SHOW_LONGLONG; } + byte *value_ptr(THD *thd, enum_var_type type); +}; + + +class sys_var_slave_skip_counter :public sys_var +{ +public: + sys_var_slave_skip_counter(const char *name_arg) :sys_var(name_arg) {} + bool check(THD *thd, set_var *var); + bool update(THD *thd, set_var *var); + bool check_type(enum_var_type type) { return type != OPT_GLOBAL; } + /* + We can't retrieve the value of this, so we don't have to define + type() or value_ptr() + */ +}; + + +class sys_var_rand_seed1 :public sys_var +{ +public: + sys_var_rand_seed1(const char *name_arg) :sys_var(name_arg) {} + bool update(THD *thd, set_var *var); + bool check_type(enum_var_type type) { return type == OPT_GLOBAL; } +}; + +class sys_var_rand_seed2 :public sys_var +{ +public: + sys_var_rand_seed2(const char *name_arg) :sys_var(name_arg) {} + bool update(THD *thd, set_var *var); + bool check_type(enum_var_type type) { return type == OPT_GLOBAL; } +}; + + +class sys_var_thd_conv_charset :public sys_var_thd +{ +public: + sys_var_thd_conv_charset(const char *name_arg) + :sys_var_thd(name_arg) + {} + bool check(THD *thd, set_var *var); + bool update(THD *thd, set_var *var); + SHOW_TYPE type() { return SHOW_CHAR; } + byte *value_ptr(THD *thd, enum_var_type type); + bool check_update_type(Item_result type) + { + return type != STRING_RESULT; /* Only accept strings */ + } + bool check_default(enum_var_type type) { return 0; } +}; + + +/**************************************************************************** + Classes for parsing of the SET command +****************************************************************************/ + +class set_var_base :public Sql_alloc +{ +public: + set_var_base() {} + virtual ~set_var_base() {} + virtual int check(THD *thd)=0; /* To check privileges etc. */ + virtual int update(THD *thd)=0; /* To set the value */ +}; + + +/* MySQL internal variables, like query_cache_size */ + +class set_var :public set_var_base +{ +public: + sys_var *var; + Item *value; + enum_var_type type; + union + { + CONVERT *convert; + ulong ulong_value; + } save_result; + + set_var(enum_var_type type_arg, sys_var *var_arg, Item *value_arg) + :var(var_arg), type(type_arg) + { + /* + If the set value is a field, change it to a string to allow things like + SET table_type=MYISAM; + */ + if (value_arg && value_arg->type() == Item::FIELD_ITEM) + { + Item_field *item= (Item_field*) value_arg; + if (!(value=new Item_string(item->field_name, strlen(item->field_name)))) + value=value_arg; /* Give error message later */ + } + else + value=value_arg; + } + int check(THD *thd); + int update(THD *thd); +}; + + +/* User variables like @my_own_variable */ + +class set_var_user: public set_var_base +{ + Item_func_set_user_var *user_var_item; +public: + set_var_user(Item_func_set_user_var *item) + :user_var_item(item) + {} + int check(THD *thd); + int update(THD *thd); +}; + +/* For SET PASSWORD */ + +class set_var_password: public set_var_base +{ + LEX_USER *user; + char *password; +public: + set_var_password(LEX_USER *user_arg,char *password_arg) + :user(user_arg), password(password_arg) + {} + int check(THD *thd); + int update(THD *thd); +}; + + +/* + Prototypes for helper functions +*/ + +void set_var_init(); +void set_var_free(); +sys_var *find_sys_var(const char *str, uint length=0); +int sql_set_variables(THD *thd, List<set_var_base> *var_list); +void fix_delay_key_write(THD *thd, enum_var_type type); + +extern sys_var_str sys_charset; diff --git a/sql/share/Makefile.am b/sql/share/Makefile.am index b72f7493e20..c70ac9ccf57 100644 --- a/sql/share/Makefile.am +++ b/sql/share/Makefile.am @@ -28,5 +28,11 @@ install-data-local: $(INSTALL_DATA) $(srcdir)/charsets/Index $(DESTDIR)$(pkgdatadir)/charsets/Index $(INSTALL_DATA) $(srcdir)/charsets/*.conf $(DESTDIR)$(pkgdatadir)/charsets +fix_errors: + for lang in @AVAILABLE_LANGUAGES@; \ + do \ + ../../extra/comp_err $(srcdir)/$$lang/errmsg.txt $(srcdir)/$$lang/errmsg.sys; \ + done + # Don't update the files from bitkeeper %::SCCS/s.% diff --git a/sql/share/charsets/Index b/sql/share/charsets/Index index b91e27e7c02..5cf30682cc0 100644 --- a/sql/share/charsets/Index +++ b/sql/share/charsets/Index @@ -1,6 +1,7 @@ # sql/share/charsets/Index # -# This file lists all of the available character sets. +# This file lists all of the available character sets. Please keep this +# file sorted by character set number. big5 1 @@ -34,3 +35,4 @@ croat 27 gbk 28 cp1257 29 latin5 30 +latin1_de 31 diff --git a/sql/share/czech/errmsg.txt b/sql/share/czech/errmsg.txt index 27825baa031..f169d6f85f1 100644 --- a/sql/share/czech/errmsg.txt +++ b/sql/share/czech/errmsg.txt @@ -156,7 +156,7 @@ "%-.16s p-Bøíkaz nepøístupný pro u¾ivatele: '%-.32s@%-.64s' pro sloupec '%-.64s' v tabulce '%-.64s'", "Neplatn-Bý pøíkaz GRANT/REVOKE. Prosím, pøeètìte si v manuálu, jaká privilegia je mo¾né pou¾ít.", "Argument p-Bøíkazu GRANT u¾ivatel nebo stroj je pøíli¹ dlouhý", -"Tabulka '%-64s.%s' neexistuje", +"Tabulka '%-.64s.%s' neexistuje", "Neexistuje odpov-Bídající grant pro u¾ivatele '%-.32s' na stroji '%-.64s' pro tabulku '%-.64s'", "Pou-B¾itý pøíkaz není v této verzi MySQL povolen", "Va-B¹e syntaxe je nìjaká divná", @@ -188,7 +188,7 @@ "Update tabulky bez WHERE s kl-Bíèem není v módu bezpeèných update dovoleno", "Kl-Bíè '%-.64s' v tabulce '%-.64s' neexistuje", "Nemohu otev-Bøít tabulku", -"Handler tabulky nepodporuje check/repair", +"Handler tabulky nepodporuje %s", "Proveden-Bí tohoto pøíkazu není v transakci dovoleno", "Chyba %d p-Bøi COMMIT", "Chyba %d p-Bøi ROLLBACK", @@ -228,3 +228,22 @@ "Cannot add foreign key constraint", "Cannot add a child row: a foreign key constraint fails", "Cannot delete a parent row: a foreign key constraint fails", +"Error connecting to master: %-.128s", +"Error running query on master: %-.128s", +"Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", +"Can't execute the query because you have a conflicting read lock", +"Mixing of transactional and non-transactional tables is disabled", +"Option '%s' used twice in statement", +"User '%-.64s' has exceeded the '%s' resource (current value: %ld)", +"Access denied. You need the %-.128s privilege for this operation", +"Variable '%-.64s' is a LOCAL variable and can't be used with SET GLOBAL", +"Variable '%-.64s' is a GLOBAL variable and should be set with SET GLOBAL", +"Variable '%-.64s' doesn't have a default value", +"Variable '%-.64s' can't be set to the value of '%-.64s'", +"Wrong argument type to variable '%-.64s'", +"Variable '%-.64s' can only be set, not read", +"Wrong usage/placement of '%s'", +"This version of MySQL doesn't yet support '%s'", +"Got fatal error %d: '%-.128s' from master when reading data from binary log", diff --git a/sql/share/danish/errmsg.txt b/sql/share/danish/errmsg.txt index fcf39f9b3ce..06c63e47a73 100644 --- a/sql/share/danish/errmsg.txt +++ b/sql/share/danish/errmsg.txt @@ -182,7 +182,7 @@ "Du bruger sikker opdaterings modus ('safe update mode') og du forsøgte at opdatere en tabel uden en WHERE klausul, der gør brug af et KEY felt", "Nøglen '%-.64s' eksisterer ikke i tabellen '%-.64s'", "Kan ikke åbne tabellen", -"Denne tabeltype understøtter ikke CHECK/REPAIR", +"Denne tabeltype understøtter ikke %s", "Du må ikke bruge denne kommando i en transaktion", "Modtog fejl %d mens kommandoen COMMIT blev udført", "Modtog fejl %d mens kommandoen ROLLBACK blev udført", @@ -222,3 +222,22 @@ "Cannot add foreign key constraint", "Cannot add a child row: a foreign key constraint fails", "Cannot delete a parent row: a foreign key constraint fails", +"Error connecting to master: %-.128s", +"Error running query on master: %-.128s", +"Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", +"Can't execute the query because you have a conflicting read lock", +"Mixing of transactional and non-transactional tables is disabled", +"Option '%s' used twice in statement", +"User '%-.64s' has exceeded the '%s' resource (current value: %ld)", +"Access denied. You need the %-.128s privilege for this operation", +"Variable '%-.64s' is a LOCAL variable and can't be used with SET GLOBAL", +"Variable '%-.64s' is a GLOBAL variable and should be set with SET GLOBAL", +"Variable '%-.64s' doesn't have a default value", +"Variable '%-.64s' can't be set to the value of '%-.64s'", +"Wrong argument type to variable '%-.64s'", +"Variable '%-.64s' can only be set, not read", +"Wrong usage/placement of '%s'", +"This version of MySQL doesn't yet support '%s'", +"Got fatal error %d: '%-.128s' from master when reading data from binary log", diff --git a/sql/share/dutch/errmsg.txt b/sql/share/dutch/errmsg.txt index 79768450cae..452a330b61b 100644 --- a/sql/share/dutch/errmsg.txt +++ b/sql/share/dutch/errmsg.txt @@ -2,9 +2,14 @@ This file is public domain and comes with NO WARRANTY of any kind Dutch error messages (share/dutch/errmsg.txt) - Arjen G. Lentz (agl@bitbike.com) + 2001-08-02 - Arjen Lentz (agl@bitbike.com) Completed earlier partial translation; worked on consistency and spelling. - Version: 02-08-2001 + 2002-01-29 - Arjen Lentz (arjen@mysql.com) + 2002-04-11 - Arjen Lentz (arjen@mysql.com) + 2002-06-13 - Arjen Lentz (arjen@mysql.com) + 2002-08-08 - Arjen Lentz (arjen@mysql.com) + 2002-08-22 - Arjen Lentz (arjen@mysql.com) + Translated new error messages. */ "hashchk", @@ -16,9 +21,9 @@ "Kan database '%-.64s' niet aanmaken (Errcode: %d)", "Kan database '%-.64s' niet aanmaken. Database bestaat reeds", "Kan database '%-.64s' niet verwijderen. Database bestaat niet", -"Error verwijderen database (kan '%-.64s' niet verwijderen, Errcode: %d)", -"Error verwijderen database (kan rmdir '%-.64s' niet uitvoeren, Errcode: %d)", -"Error bij het verwijderen van '%-.64s' (Errcode: %d)", +"Fout bij verwijderen database (kan '%-.64s' niet verwijderen, Errcode: %d)", +"Fout bij verwijderen database (kan rmdir '%-.64s' niet uitvoeren, Errcode: %d)", +"Fout bij het verwijderen van '%-.64s' (Errcode: %d)", "Kan record niet lezen in de systeem tabel", "Kan de status niet krijgen van '%-.64s' (Errcode: %d)", "Kan de werkdirectory niet krijgen (Errcode: %d)", @@ -153,7 +158,7 @@ "%-.16s commando geweigerd voor gebruiker: '%-.32s@%-.64s' voor kolom '%-.64s' in tabel '%-.64s'", "Foutief GRANT/REVOKE commando. Raadpleeg de handleiding welke priveleges gebruikt kunnen worden.", "De host of gebruiker parameter voor GRANT is te lang", -"Tabel '%-64s.%s' bestaat niet", +"Tabel '%-.64s.%s' bestaat niet", "Deze toegang (GRANT) is niet toegekend voor gebruiker '%-.32s' op host '%-.64s' op tabel '%-.64s'", "Het used commando is niet toegestaan in deze MySQL versie", "Er is iets fout in de gebruikte syntax", @@ -167,8 +172,8 @@ "Communicatiepakket kon niet worden gedecomprimeerd", "Fout bij het lezen van communicatiepakketten", "Timeout bij het lezen van communicatiepakketten", -"Got an error writing communication packets", -"Got timeout writing communication packets", +"Fout bij het schrijven van communicatiepakketten", +"Timeout bij het schrijven van communicatiepakketten", "Resultaat string is langer dan max_allowed_packet", "Het gebruikte tabel type ondersteunt geen BLOB/TEXT kolommen", "Het gebruikte tabel type ondersteunt geen AUTO_INCREMENT kolommen", @@ -185,7 +190,7 @@ "U gebruikt 'safe update mode' en u probeerde een tabel te updaten zonder een WHERE met een KEY kolom", "Zoeksleutel '%-.64s' bestaat niet in tabel '%-.64s'", "Kan tabel niet openen", -"De 'handler' voor de tabel ondersteund geen check/repair", +"De 'handler' voor de tabel ondersteund geen %s", "Het is u niet toegestaan dit commando uit te voeren binnen een transactie", "Kreeg fout %d tijdens COMMIT", "Kreeg fout %d tijdens ROLLBACK", @@ -218,10 +223,29 @@ "DROP DATABASE niet toegestaan terwijl thread een globale 'read lock' bezit", "CREATE DATABASE niet toegestaan terwijl thread een globale 'read lock' bezit", "Foutieve parameters voor %s", -"%-.32s@%-.64s is not allowed to create new users", -"Incorrect table definition; All MERGE tables must be in the same database", -"Deadlock found when trying to get lock; Try restarting transaction", -"The used table type doesn't support FULLTEXT indexes", -"Cannot add foreign key constraint", -"Cannot add a child row: a foreign key constraint fails", -"Cannot delete a parent row: a foreign key constraint fails", +"%-.32s@%-.64s mag geen nieuwe gebruikers creeren", +"Incorrecte tabel definitie; Alle MERGE tabellen moeten tot dezelfde database behoren", +"Deadlock gevonden tijdens lock-aanvraag poging; Probeer herstart van de transactie", +"Het gebruikte tabel type ondersteund geen FULLTEXT indexen", +"Kan foreign key beperking niet toevoegen", +"Kan onderliggende rij niet toevoegen: foreign key beperking gefaald", +"Kan bovenliggende rij nite verwijderen: foreign key beperking gefaald", +"Fout bij opbouwen verbinding naar master: %-.128s", +"Fout bij uitvoeren query op master: %-.128s", +"Fout tijdens uitvoeren van commando %s: %-.128s", +"Foutief gebruik van %s en %s", +"De gebruikte SELECT commando's hebben een verschillend aantal kolommen", +"Kan de query niet uitvoeren vanwege een conflicterende read lock", +"Het combineren van transactionele en niet-transactionele tabellen is uitgeschakeld.", +"Optie '%s' tweemaal gebruikt in opdracht", +"Gebruiker '%-.64s' heeft het maximale gebruik van de '%s' faciliteit overschreden (huidige waarde: %ld)", +"Toegang geweigerd. U moet het %-.128s privilege hebben voor deze operatie", +"Variabele '%-.64s' is LOCAL en kan niet worden gebruikt met SET GLOBAL", +"Variabele '%-.64s' is GLOBAL en dient te worden gewijzigd met SET GLOBAL", +"Variabele '%-.64s' heeft geen standaard waarde", +"Variabele '%-.64s' kan niet worden gewijzigd naar de waarde '%-.64s'", +"Foutief argumenttype voor variabele '%-.64s'", +"Variabele '%-.64s' kan alleen worden gewijzigd, niet gelezen", +"Foutieve toepassing/plaatsing van '%s'", +"Deze versie van MySQL ondersteunt nog geen '%s'", +"Kreeg fatale fout %d: '%-.128s' van master tijdens lezen van data uit binaire log", diff --git a/sql/share/english/errmsg.txt b/sql/share/english/errmsg.txt index b30ac87b5b6..023cd7bc73e 100644 --- a/sql/share/english/errmsg.txt +++ b/sql/share/english/errmsg.txt @@ -77,7 +77,7 @@ "BLOB column '%-.64s' can't be used in key specification with the used table type", "Too big column length for column '%-.64s' (max = %d). Use BLOB instead", "Incorrect table definition; There can only be one auto column and it must be defined as a key", -"%s: ready for connections\n", +"%s: ready for connections.\nVersion: '%s' socket: '%s' port: %d\n", "%s: Normal shutdown\n", "%s: Got signal %d. Aborting!\n", "%s: Shutdown Complete\n", @@ -150,7 +150,7 @@ "Table '%-.64s.%-.64s' doesn't exist", "There is no such grant defined for user '%-.32s' on host '%-.64s' on table '%-.64s'", "The used command is not allowed with this MySQL version", -"You have an error in your SQL syntax", +"You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use", "Delayed insert thread couldn't get requested lock for table %-.64s", "Too many delayed threads in use", "Aborted connection %ld to db: '%-.64s' user: '%-.32s' (%-.64s)", @@ -179,7 +179,7 @@ "You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column", "Key '%-.64s' doesn't exist in table '%-.64s'", "Can't open table", -"The handler for the table doesn't support check/repair", +"The handler for the table doesn't support %s", "You are not allowed to execute this command in a transaction", "Got error %d during COMMIT", "Got error %d during ROLLBACK", @@ -217,5 +217,24 @@ "Deadlock found when trying to get lock; Try restarting transaction", "The used table type doesn't support FULLTEXT indexes", "Cannot add foreign key constraint", -"Cannot add a child row: a foreign key constraint fails", -"Cannot delete a parent row: a foreign key constraint fails", +"Cannot add or update a child row: a foreign key constraint fails", +"Cannot delete or update a parent row: a foreign key constraint fails", +"Error connecting to master: %-.128s", +"Error running query on master: %-.128s", +"Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", +"Can't execute the query because you have a conflicting read lock", +"Mixing of transactional and non-transactional tables is disabled", +"Option '%s' used twice in statement", +"User '%-.64s' has exceeded the '%s' resource (current value: %ld)", +"Access denied. You need the %-.128s privilege for this operation", +"Variable '%-.64s' is a LOCAL variable and can't be used with SET GLOBAL", +"Variable '%-.64s' is a GLOBAL variable and should be set with SET GLOBAL", +"Variable '%-.64s' doesn't have a default value", +"Variable '%-.64s' can't be set to the value of '%-.64s'", +"Wrong argument type to variable '%-.64s'", +"Variable '%-.64s' can only be set, not read", +"Wrong usage/placement of '%s'", +"This version of MySQL doesn't yet support '%s'", +"Got fatal error %d: '%-.128s' from master when reading data from binary log", diff --git a/sql/share/estonian/errmsg.txt b/sql/share/estonian/errmsg.txt index 88cded9e19b..d0a30b2f434 100644 --- a/sql/share/estonian/errmsg.txt +++ b/sql/share/estonian/errmsg.txt @@ -1,225 +1,245 @@ -/* Copyright Abandoned 1997 TCX DataKonsult AB & Monty Program KB & Detron HB - This file is public domain and comes with NO WARRANTY of any kind +/* + Copyright Abandoned 1997 MySQL AB + This file is public domain and comes with NO WARRANTY of any kind + Esialgne tõlge: Tõnu Samuel (tonu@spam.ee) + Parandanud ja täiendanud: Indrek Siitan (tfr@mysql.com) - Translated into estonian language by Tonu Samuel - email: tonu@spam.ee */ "hashchk", "isamchk", "EI", "JAH", -"Ei saa luua tabelit '%-.64s' (vea kood: %d)", -"Ei saa luua tabelit '%-.64s' (vea kood: %d)", -"Ei saa luua andmebaasi '%-.64s'. (vea kood: %d)", -"Ei saa luua andmebaasi '%-.64s'. Andmebaas on juba olemas", -"Ei saa kustutada andmebaasi '%-.64s'. Andmebaasi ei eksisteeri", -"Ei saa kustutada andmebaasi (Ei saa kustutada faili '%-.64s', vea kood: %d)", -"Ei saa kustutada andmebaasi (Ei saa kustutada kataloogi '%-.64s', vea kood: %d)", -"Viga '%-.64s' kustutamisel (vea kood: %d)", -"Ei saa lugeda kirjet in süsteemsest tabelist", -"Ei saa lugeda '%-.64s' olekut (vea kood: %d)", -"Ei saa teada jooksva kataloogi nime (vea kood: %d)", -"Ei saa avada lukustusfaili (vea kood: %d)", -"Ei saa avada faili: '%-.64s'. (vea kood: %d)", -"Ei leia faili: '%-.64s' (vea kood: %d)", -"Ei saa lugeda kataloogi '%-.64s' (vea kood: %d)", -"Ei saa siseneda kataloogi '%-.64s' (vea kood: %d)", -"Kirje on muutunud võrreldes eelmise lugemisega tabelis '%-.64s'", -"Ketas on täis (%s). Ootame kuni tekib vaba ruumi....", -"Ei saa kirjutada, Korduv võti tabelis '%-.64s'", -"Viga faili '%-.64s' sulgemisel (vea kood: %d)", -"Viga faili '%-.64s' lugemisel (vea kood: %d)", -"Viga faili '%-.64s' ringi nimetamisel '%-.64s'-ks (vea kood: %d)", -"Viga faili '%-.64s' kirjutamisel (vea kood: %d)", +"Ei suuda luua faili '%-.64s' (veakood: %d)", +"Ei suuda luua tabelit '%-.64s' (veakood: %d)", +"Ei suuda luua andmebaasi '%-.64s'. (veakood: %d)", +"Ei suuda luua andmebaasi '%-.64s': andmebaas juba eksisteerib", +"Ei suuda kustutada andmebaasi '%-.64s': andmebaasi ei eksisteeri", +"Viga andmebaasi kustutamisel (ei suuda kustutada faili '%-.64s', veakood: %d)", +"Viga andmebaasi kustutamisel (ei suuda kustutada kataloogi '%-.64s', veakood: %d)", +"Viga '%-.64s' kustutamisel (veakood: %d)", +"Ei suuda lugeda kirjet süsteemsest tabelist", +"Ei suuda lugeda '%-.64s' olekut (veakood: %d)", +"Ei suuda identifitseerida jooksvat kataloogi (veakood: %d)", +"Ei suuda lukustada faili (veakood: %d)", +"Ei suuda avada faili '%-.64s'. (veakood: %d)", +"Ei suuda leida faili '%-.64s' (veakood: %d)", +"Ei suuda lugeda kataloogi '%-.64s' (veakood: %d)", +"Ei suuda siseneda kataloogi '%-.64s' (veakood: %d)", +"Kirje tabelis '%-.64s' on muutunud viimasest lugemisest saadik", +"Ketas täis (%s). Ootame kuni tekib vaba ruumi...", +"Ei saa kirjutada, korduv võti tabelis '%-.64s'", +"Viga faili '%-.64s' sulgemisel (veakood: %d)", +"Viga faili '%-.64s' lugemisel (veakood: %d)", +"Viga faili '%-.64s' ümbernimetamisel '%-.64s'-ks (veakood: %d)", +"Viga faili '%-.64s' kirjutamisel (veakood: %d)", "'%-.64s' on lukustatud muudatuste vastu", "Sorteerimine katkestatud", -"Vaade '%-.64s' puudub '%-.64s' jaoks", -"Viga %d tabelitöötluses", -"Table handler for '%-.64s' doesn't have this option", +"Vaade '%-.64s' ei eksisteeri '%-.64s' jaoks", +"Tabeli handler tagastas vea %d", +"Tabeli '%-.64s' handler ei toeta antud operatsiooni", "Ei suuda leida kirjet '%-.64s'-s", -"Väär informatsiion failis '%-.64s'", -"Vigastatud võtmefail tabelile '%-.64s'", -"Vana võtmefail tabelile '%-.64s'. Proovi teda parandada", -"Tabel '%-.64s' on ainult lugemise õigusega", -"Mälu sai otsa. Proovi MySQL uuesti käivitada (Puudu jäi %d baiti)", -"Mälu sai sorteerimie ajal otsa. Suurenda MySQL-i sorteerimispuhvrit", -"Ootamatu faili lõpp leitud faili '%-.64s' lugemisel (vea kood: %d)", +"Vigane informatsioon failis '%-.64s'", +"Tabeli '%-.64s' võtmefail on vigane; Proovi seda parandada", +"Tabeli '%-.64s' võtmefail on aegunud; Paranda see!", +"Tabel '%-.64s' on ainult lugemiseks", +"Mälu sai otsa. Proovi MySQL uuesti käivitada (puudu jäi %d baiti)", +"Mälu sai sorteerimisel otsa. Suurenda MySQL-i sorteerimispuhvrit", +"Ootamatu faililõpumärgend faili '%-.64s' lugemisel (veakood: %d)", "Liiga palju samaaegseid ühendusi", -"Mälu sai otsa. Võimalik, et aitab swap-i lisamine või käsu 'ulimit' abil MySQL-le rohkema mälu kasutamise lubamine.", +"Mälu sai otsa. Võimalik, et aitab swap-i lisamine või käsu 'ulimit' abil MySQL-le rohkema mälu kasutamise lubamine", "Ei suuda lahendada IP aadressi masina nimeks", "Väär handshake", -"Ligipääs piiratud kasutajale: '%-.32s@%-.64s' andmebaasi '%-.64s'", -"Ligipääs piiratud kasutajale: '%-.32s@%-.64s' (Kasutab parooli: %s)", -"Andmebaas pole valitud", +"Ligipääs keelatud kasutajale '%-.32s@%-.64s' andmebaasile '%-.64s'", +"Ligipääs keelatud kasutajale '%-.32s@%-.64s' (kasutab parooli: %s)", +"Andmebaasi ei ole valitud", "Tundmatu käsk", -"Tulp '%-.64s' ei saa olla null", +"Tulp '%-.64s' ei saa omada nullväärtust", "Tundmatu andmebaas '%-.64s'", -"Tabel '%-.64s' on juba olemas", +"Tabel '%-.64s' juba eksisteerib", "Tundmatu tabel '%-.64s'", -"Tulp: '%-.64s' in %-.64s on väär", +"Väli '%-.64s' %-.64s-s ei ole ühene", "Serveri seiskamine käib", -"Tundmatu tulp '%-.64s' in '%-.64s'", -"'%-.64s' puudub GROUP BY-s", +"Tundmatu tulp '%-.64s' '%-.64s'-s", +"'%-.64s' puudub GROUP BY klauslis", "Ei saa grupeerida '%-.64s' järgi", -"Lauses on korraga nii tulbad kui summad", -"Tuplade arv tabelis erineb antud väärtuste arvust", +"Lauses on korraga nii tulbad kui summeerimisfunktsioonid", +"Tulpade arv erineb väärtuste arvust", "Identifikaatori '%-.100s' nimi on liiga pikk", "Kattuv tulba nimi '%-.64s'", "Kattuv võtme nimi '%-.64s'", -"Kattuv nimi '%-.64s' võtmele %d", -"Väär tulba kirjeldus tulbale '%-.64s'", -"%s '%-.80s' ligidal reas %d", +"Kattuv väärtus '%-.64s' võtmele %d", +"Vigane tulba kirjeldus tulbale '%-.64s'", +"%s '%-.80s' ligidal real %d", "Tühi päring", -"Pole unikaalne tabel/alias '%-.64s'", -"Vale vaikeväärtus '%-.64s'", -"Mitut põhivõtit (PRIMARY KEY) ei saa olla", -"Liiga palju võtmeid määratletud. Maksimaalselt võib olla %d võtit", +"Ei ole unikaalne tabel/alias '%-.64s'", +"Vigane vaikeväärtus '%-.64s' jaoks", +"Mitut primaarset võtit ei saa olla", +"Liiga palju võtmeid. Maksimaalselt võib olla %d võtit", "Võti koosneb liiga paljudest osadest. Maksimaalselt võib olla %d osa", -"Määratletud võti sai liiga pikk. Maksimaalne lubatud pikkus on %d", -"Võtme tulp '%-.64s' puudub antud tabelis", -"BLOB tulpa '%-.64s' ei saa kasutada võtmena", -"Tulba '%-.64s' pikkus on liiga pikk (maksimaalne = %d).", -"Tabeli kohta saab olla ainult üks auto_increment tulp ja see peab olema samas ka võtmena", +"Võti on liiga pikk. Maksimaalne võtmepikkus on %d", +"Võtme tulp '%-.64s' puudub tabelis", +"BLOB-tüüpi tulpa '%-.64s' ei saa kasutada võtmena", +"Tulba '%-.64s' pikkus on liiga pikk (maksimaalne pikkus: %d). Kasuta BLOB väljatüüpi", +"Vigane tabelikirjeldus; Tabelis tohib olla üks auto_increment tüüpi tulp ning see peab olema defineeritud võtmena", "%s: ootab ühendusi\n", "%s: MySQL lõpetas\n", -"%s: Sain signaali %d. Lõpetan!\n", +"%s: sain signaali %d. Lõpetan!\n", "%s: Lõpp\n", -"%s: Sulgen jõuga threadi %ld kasutaja: '%-.64s'\n", -"Ei saa luua IP pesa", +"%s: Sulgen jõuga lõime %ld kasutaja: '%-.32s'\n", +"Ei suuda luua IP socketit", "Tabelil '%-.64s' puuduvad võtmed. Loo tabel uuesti", -"Väljade eraldaja on väär. Vaata kasutamisjuhendisse", -"BLOB väljadega ei saa kasutada fikseeritud väljapikkust. Seetõttu on vajalik lisaklausel 'fields terminated by'.", -"Fail '%-.64s' peab asuma andmebaasi kataloogis ning olema loetav", -"Fail '%-.64s' on juba olemas", -"Kirjed: %ld Kustutatud: %ld Vahele jäetud: %ld Hoiatusi: %ld", -"Kirjed: %ld Topelt: %ld", -"Väär võtme osa. Kasutatud võtme osa ei ole string või on pikkus pikem kui võtme osa", -"ALTER TABLE abil ei saa koiki tulpasid kustutada. DROP TABLE kustutab terve tabeli", -"Ei saa kustutada '%-.64s'. On selline tulp või võti üldse olemas?", -"Kirjed: %ld Topelt: %ld Hoiatusi: %ld", -"INSERT TABLE '%-.64s' pole lubatud FROM tabelite nimekirjas", -"Tundmatu threadi id: %lu", -"Pole threadi %lu omanik", -"Pole kasutatud tabeleid", -"Liiga palju stringe tulbale %-.64s ja tüübile SET", -"Ei saa luua ainulaadset failinime %-.64s.(1-999)\n", -"Tabel '%-.64s' on lukustatud ainult lugemiseks ja sinna kirjutada ei saa", -"Tabel '%-.64s' pole lukustatud käsuga LOCK TABLES", -"BLOB tüüpi tulbal '%-.64s' ei saa olla vaikeväärtust", -"Väär andmebaasi nimi '%-.100s'", -"Väär tabeli nimi '%-.100s'", -"SELECT lause peab läbi vaatama suure hulga kirjeid ja võtaks tõenäoliselt liiga kaua aega. Tasub kontrollide WHERE klauslit ja vajadusel kasutada käsku SET OPTION SQL_BIG_SELECTS=1", +"Väljade eraldaja erineb oodatust. Tutvu kasutajajuhendiga", +"BLOB-tüüpi väljade olemasolul ei saa kasutada fikseeritud väljapikkust. Vajalik 'fields terminated by' määrang.", +"Fail '%-.64s' peab asuma andmebaasi kataloogis või olema kõigile loetav", +"Fail '%-.80s' juba eksisteerib", +"Kirjeid: %ld Kustutatud: %ld Vahele jäetud: %ld Hoiatusi: %ld", +"Kirjeid: %ld Kattuvaid: %ld", +"Vigane võtme osa. Kasutatud võtmeosa ei ole string tüüpi, määratud pikkus on pikem kui võtmeosa või tabelihandler ei toeta seda tüüpi võtmeid", +"ALTER TABLE kasutades ei saa kustutada kõiki tulpasid. Kustuta tabel DROP TABLE abil", +"Ei suuda kustutada '%-.64s'. Kontrolli kas tulp/võti eksisteerib", +"Kirjeid: %ld Kattuvaid: %ld Hoiatusi: %ld", +"INSERT TABLE '%-.64s' ei ole lubatud FROM tabelite nimekirjas", +"Tundmatu lõim: %lu", +"Ei ole lõime %lu omanik", +"Ühtegi tabelit pole kasutusel", +"Liiga palju string tulbale %-.64s tüübile SET", +"Ei suuda luua unikaalset logifaili nime %-.64s.(1-999)\n", +"Tabel '%-.64s' on lukustatud READ lukuga ning ei ole muudetav", +"Tabel '%-.64s' ei ole lukustatud käsuga LOCK TABLES", +"BLOB-tüüpi tulp '%-.64s' ei saa omada vaikeväärtust", +"Vigane andmebaasi nimi '%-.100s'", +"Vigane tabeli nimi '%-.100s'", +"SELECT lause peab läbi vaatama suure hulga kirjeid ja võtaks tõenäoliselt liiga kaua aega. Tasub kontrollida WHERE klauslit ja vajadusel kasutada käsku SET OPTION SQL_BIG_SELECTS=1", "Tundmatu viga", "Tundmatu protseduur '%-.64s'", -"Väär parameetrite hulk protseduurile '%-.64s'", -"Valed parameetrid protseduurile '%-.64s'", -"Tundmatu tabel '%-.64s' %s-s", +"Vale parameetrite hulk protseduurile '%-.64s'", +"Vigased parameetrid protseduurile '%-.64s'", +"Tundmatu tabel '%-.64s' %-.32s-s", "Tulp '%-.64s' on määratletud topelt", -"GROUP BY funktsiooni väärkasutamine", -"Tabel '%-.64s' kasutab laiendit, mis on tundmatu sellele MySQL versioonile", -"Tabelil peab olema vähemalt üks tulp", +"Vigane grupeerimisfunktsiooni kasutus", +"Tabel '%-.64s' kasutab laiendust, mis ei eksisteeri antud MySQL versioonis", +"Tabelis peab olema vähemalt üks tulp", "Tabel '%-.64s' on täis", -"Tundmatu kooditabel: '%-.64s'", -"Liiga palju tabeleid. MySQL oskab kasutada kuni %d tabelit JOINi puhul", +"Vigane kooditabel '%-.64s'", +"Liiga palju tabeleid. MySQL suudab JOINiga ühendada kuni %d tabelit", "Liiga palju tulpasid", -"Liiga pikk kirje. Maksimaalne kirje pikkus arvestamata BLOB tüüpi on %d. Võib-olla aitab mõnede väljade muutmine BLOB tüübiks", -"Threadi stack overrun: Used: %ld of a %ld stack. Use 'mysqld -O thlugeda_stack=#' to specify a bigger stack if needed", -"Ristsõltuvus OUTER JOIN-s. ON tingimused tuleks üle kontrollida", -"Tulp '%-.64s' on kasutused indeksis kui pole defineeritud tüübiga NOT NULL", -"Ei saa avada funktsiooni '%-.64s'", -"Ei saa algväärtustada funktsiooni '%-.64s'; %-.80s", +"Liiga pikk kirje. Kirje maksimumpikkus arvestamata BLOB-tüüpi välju on %d. Muuda mõned väljad BLOB-tüüpi väljadeks", +"Thread stack overrun: Used: %ld of a %ld stack. Use 'mysqld -O thread_stack=#' to specify a bigger stack if needed", +"Ristsõltuvus OUTER JOIN klauslis. Kontrolli oma ON tingimusi", +"Tulp '%-.64s' on kasutusel indeksina, kuid ei ole määratletud kui NOT NULL", +"Ei suuda avada funktsiooni '%-.64s'", +"Ei suuda algväärtustada funktsiooni '%-.64s'; %-.80s", "Teegi nimes ei tohi olla kataloogi", -"Funktsioon '%-.64s' on juba olemas", -"Ei saa avada teeki '%-.64s' (vea kood: %d %s)", -"Ei leia funktsiooni '%-.64s' selles teegis'", -"Funktsiooni '%-.64s' pole defineeritud", -"Masin '%-.64s' blokeeritud hulgaliste ühendusvigade pärast. Blokeeringu saab eemaldada käsuga 'mysqladmin flush-hosts'", -"Masinale '%-.64s' pole lubatud ligipääsu sellele MySQL serverile", +"Funktsioon '%-.64s' juba eksisteerib", +"Ei suuda avada jagatud teeki '%-.64s' (veakood: %d %-.64s)", +"Ei leia funktsiooni '%-.64s' antud teegis", +"Funktsioon '%-.64s' ei ole defineeritud", +"Masin '%-.64s' on blokeeritud hulgaliste ühendusvigade tõttu. Blokeeringu saab tühistada 'mysqladmin flush-hosts' käsuga", +"Masinal '%-.64s' puudub ligipääs sellele MySQL serverile", "Te kasutate MySQL-i anonüümse kasutajana, kelledel pole parooli muutmise õigust", -"Teil peab olema tabelite muutmise õigus muutmaks teiste paroole", -"Ei leia kirjet kasutajate tabelis", -"Sobinud kirjed: %ld Muudetud: %ld Hoiatusi: %ld", -"Ei saa luua threadi (vea kood %d). Kui mälu pole otsas, tasub operatsioonisüsteemi spetsiifilist viga", -"Tulpade arv ei vasta väärtuste hulgale reas %ld", -"Ei saa avada tabelit: '%-.64s", +"Teiste paroolide muutmiseks on nõutav tabelite muutmisõigus 'mysql' andmebaasis", +"Ei leia vastavat kirjet kasutajate tabelis", +"Sobinud kirjeid: %ld Muudetud: %ld Hoiatusi: %ld", +"Ei suuda luua uut lõime (veakood %d). Kui mälu ei ole otsas, on tõenäoliselt tegemist operatsioonisüsteemispetsiifilise veaga", +"Tulpade hulk erineb väärtuste hulgast real %ld", +"Ei suuda taasavada tabelit '%-.64s'", "NULL väärtuse väärkasutus", -"Viga '%-.64s' regexp-i käest", -"GROUP tulpade segamine (MIN(),MAX(),COUNT()...) on väär kui ei kasutata GROUP BY klauslit", -"Sellist õigust ei ole kasutajale '%-.32s' masinast '%-.64s'", -"%-.16s käsk pole lubatud kasutajale '%-.32s@%-.64s' tabelile '%-.64s'", -"%-.16s käsk pole lubatud kasutajale '%-.32s@%-.64s' tulbale '%-.64s' tabelis '%-.64s'", -"Väär GRANT/REVOKE kasutus", -"Masina või kasutaja nimi on liiga pikk GRANT lauses", -"Tabelit '%-64s.%s' ei leitud", -"Sellist õigust pole kasutajale '%-.32s' masinast '%-.64s' tabelile '%-.64s'", -"Antud käsk pole lubatud selle MySQL-i versiooniga", +"regexp tagastas vea '%-.64s'", +"GROUP tulpade (MIN(),MAX(),COUNT()...) kooskasutamine tavaliste tulpadega ilma GROUP BY klauslita ei ole lubatud", +"Sellist õigust ei ole defineeritud kasutajale '%-.32s' masinast '%-.64s'", +"%-.16s käsk ei ole lubatud kasutajale '%-.32s@%-.64s' tabelis '%-.64s'", +"%-.16s käsk ei ole lubatud kasutajale '%-.32s@%-.64s' tulbale '%-.64s' tabelis '%-.64s'", +"Vigane GRANT/REVOKE käsk. Tutvu kasutajajuhendiga", +"Masina või kasutaja nimi GRANT lauses on liiga pikk", +"Tabelit '%-.64s.%-.64s' ei eksisteeri", +"Sellist õigust ei ole defineeritud kasutajale '%-.32s' masinast '%-.64s' tabelile '%-.64s'", +"Antud käsk ei ole lubatud käesolevas MySQL versioonis", "Viga SQL süntaksis", -"INSERT DELAYED thread ei saanud nõutavat lukku tabelile %-.64s", -"Liiga palju DELAYED threade on kasutusel", -"Ühendus katkestatud %ld andmebaasile '%-.64s' kasutaja '%-.64s' (%s)", -"Sain lubatust suurema paketi (max_allowed_packet)", -"Got a read error from the connection pipe", -"Got an error from fcntl()", -"Got packets out of order", -"Ei suuda ühendust lahti pakkida", -"Viga ühenduse lugemisel", -"Aeg sai otsa ühenduse lugemisel", -"Viga ühenduse kirjutamisel", -"Aeg sai otsa ühenduse kirjutamisel", -"Tulemuseks saadud string on pikem kui max_allowed_packet väärtus", -"Kasutatud tabeli tüüp ei toeta BLOB/TEXT tulpasid", -"Kasutatud tabeli tüüp ei toeta AUTO_INCREMENT tulpasid", -"INSERT DELAYED käsku ei saa kasutada tabeliga '%-.64s', kuna see on lukus käsuga LOCK TABLES", -"Väär tulba nimi '%-.100s'", -"Kasutusel olev tabelite haldur ei oska indekseerida tulpa '%-.64s'", -"All tables in the MERGE table are not identically defined", -"Can't write, because of unique constraint, to table '%-.64s'", -"BLOB column '%-.64s' used in key specification without a key length", -"All parts of a PRIMARY KEY must be NOT NULL; If you need NULL in a key, use UNIQUE instead", -"Tulemis on rohkem kui üks kirje", -"This table type requires a primary key", -"Antud MySQL ei ole kompileeritud RAID-i toega", -"You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column", -"Key '%-.64s' doesn't exist in table '%-.64s'", -"Ei suuda tabelit avada", -"See tabelitüüp ei toeta käske CHECK/REPAIR", -"Puudub õigus selle transaktsioonikäsu andmiseks", -"Sain vea %d COMMIT käsu täitmisel", -"Sain vea %d ROLLBACK käsu täitmisel", -"Sain vea %d FLUSH_LOGS käsu täitmisel", -"Sain vea %d CHECKPOINT käsu täitmisel", -"Ühendus %ld katkestatud andmebaas: '%-.64s' kasutaja: '%-.32s' masin: `%-.64s' (%-.64s)", +"INSERT DELAYED lõim ei suutnud saada soovitud lukku tabelile %-.64s", +"Liiga palju DELAYED lõimesid kasutusel", +"Ühendus katkestatud %ld andmebaasile: '%-.64s' kasutajale: '%-.32s' (%-.64s)", +"Saabus suurem pakett kui lubatud 'max_allowed_packet' muutujaga", +"Viga ühendustoru lugemisel", +"fcntl() tagastas vea", +"Paketid saabusid vales järjekorras", +"Viga andmepaketi lahtipakkimisel", +"Viga andmepaketi lugemisel", +"Kontrollaja ületamine andmepakettide lugemisel", +"Viga andmepaketi kirjutamisel", +"Kontrollaja ületamine andmepakettide kirjutamisel", +"Tulemus on pikem kui lubatud 'max_allowed_packet' muutujaga", +"Valitud tabelitüüp ei toeta BLOB/TEXT tüüpi välju", +"Valitud tabelitüüp ei toeta AUTO_INCREMENT tüüpi välju", +"INSERT DELAYED ei saa kasutada tabeli '%-.64s' peal, kuna see on lukustatud LOCK TABLES käsuga", +"Vigane tulba nimi '%-.100s'", +"Tabelihandler ei oska indekseerida tulpa '%-.64s'", +"Kõik tabelid MERGE tabeli määratluses ei ole identsed", +"Ei suuda kirjutada tabelisse '%-.64s', kuna see rikub ühesuse kitsendust", +"BLOB-tüüpi tulp '%-.64s' on kasutusel võtmes ilma pikkust määratlemata", +"Kõik PRIMARY KEY peavad olema määratletud NOT NULL piiranguga; vajadusel kasuta UNIQUE tüüpi võtit", +"Tulemis oli rohkem kui üks kirje", +"Antud tabelitüüp nõuab primaarset võtit", +"Antud MySQL versioon on kompileeritud ilma RAID toeta", +"Katse muuta tabelit turvalises rezhiimis ilma WHERE klauslita", +"Võti '%-.64s' ei eksisteeri tabelis '%-.64s'", +"Ei suuda avada tabelit", +"Antud tabelitüüp ei toeta %s käske", +"Seda käsku ei saa kasutada transaktsiooni sees", +"Viga %d käsu COMMIT täitmisel", +"Viga %d käsu ROLLBACK täitmisel", +"Viga %d käsu FLUSH_LOGS täitmisel", +"Viga %d käsu CHECKPOINT täitmisel", +"Ühendus katkestatud %ld andmebaas: '%-.64s' kasutaja: '%-.32s' masin: `%-.64s' (%-.64s)", "The handler for the table does not support binary table dump", "Binlog closed while trying to FLUSH MASTER", "Failed rebuilding the index of dumped table '%-.64s'", "Error from master: '%-.64s'", "Net error reading from master", "Net error writing to master", -"Can't find FULLTEXT index matching the column list", -"Can't execute the given command because you have active locked tables or an active transaction", -"Tundmatu süsteemne muutja '%-.64s'", +"Ei suutnud leida FULLTEXT indeksit, mis kattuks kasutatud tulpadega", +"Ei suuda täita antud käsku kuna on aktiivseid lukke või käimasolev transaktsioon", +"Tundmatu süsteemne muutuja '%-.64s'", "Tabel '%-.64s' on märgitud vigaseks ja tuleb parandada", -"Tabel '%-.64s' on märgitud vigaseks ja viimane (automaatne?) parandamiskatse ebaõnnestus", -"Warning: Some non-transactional changed tables couldn't be rolled back", -"Multi-statement transaction required more than 'max_binlog_cache_size' bytes of storage. Increase this mysqld variable and try again", +"Tabel '%-.64s' on märgitud vigaseks ja viimane (automaatne?) parandus ebaõnnestus", +"Hoiatus: mõnesid transaktsioone mittetoetavaid tabeleid ei suudetud tagasi kerida", +"Mitme lausendiga transaktsioon nõudis rohkem ruumi kui lubatud 'max_binlog_cache_size' muutujaga. Suurenda muutuja väärtust ja proovi uuesti", "This operation cannot be performed with a running slave, run SLAVE STOP first", "This operation requires a running slave, configure slave and do SLAVE START", "The server is not configured as slave, fix in config file or with CHANGE MASTER TO", "Could not initialize master info structure, check permisions on master.info", "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", -"DROP DATABASE not allowed while thread is holding global read lock", -"CREATE DATABASE not allowed while thread is holding global read lock", -"Wrong arguments to %s", -"%-.32s@%-.64s is not allowed to create new users", -"Incorrect table definition; All MERGE tables must be in the same database", -"Deadlock found when trying to get lock; Try restarting transaction", -"The used table type doesn't support FULLTEXT indexes", +"Kasutajal %-.64s on juba rohkem ühendusi kui lubatud 'max_user_connections' muutujaga", +"Ainult konstantsed suurused on lubatud SET klauslis", +"Kontrollaeg ületatud luku järel ootamisel; Proovi transaktsiooni otsast alata", +"Lukkude koguarv ületab lukutabeli suuruse", +"Uuenduslukke ei saa kasutada READ UNCOMMITTED transaktsiooni käigus", +"DROP DATABASE ei ole lubatud kui lõim omab globaalset READ lukku", +"CREATE DATABASE ei ole lubatud kui lõim omab globaalset READ lukku", +"Vigased parameetrid %s-le", +"Kasutajal %-.32s@%-.64s ei ole lubatud luua uusi kasutajaid", +"Vigane tabelimääratlus; kõik MERGE tabeli liikmed peavad asuma samas andmebaasis", +"Lukustamisel tekkis tupik (deadlock); alusta transaktsiooni otsast", +"Antud tabelitüüp ei toeta FULLTEXT indekseid", "Cannot add foreign key constraint", "Cannot add a child row: a foreign key constraint fails", "Cannot delete a parent row: a foreign key constraint fails", +"Error connecting to master: %-.128s", +"Error running query on master: %-.128s", +"Viga käsu %s täitmisel: %-.128s", +"Vigane %s ja %s kasutus", +"Tulpade arv kasutatud SELECT lausetes ei kattu", +"Ei suuda täita päringut konfliktse luku tõttu", +"Transaktsioone toetavate ning mittetoetavate tabelite kooskasutamine ei ole lubatud", +"Määrangut '%s' on lauses kasutatud topelt", +"User '%-.64s' has exceeded the '%s' resource (current value: %ld)", +"Access denied. You need the %-.128s privilege for this operation", +"Variable '%-.64s' is a LOCAL variable and can't be used with SET GLOBAL", +"Variable '%-.64s' is a GLOBAL variable and should be set with SET GLOBAL", +"Variable '%-.64s' doesn't have a default value", +"Variable '%-.64s' can't be set to the value of '%-.64s'", +"Wrong argument type to variable '%-.64s'", +"Variable '%-.64s' can only be set, not read", +"Wrong usage/placement of '%s'", +"This version of MySQL doesn't yet support '%s'", +"Got fatal error %d: '%-.128s' from master when reading data from binary log", diff --git a/sql/share/french/errmsg.txt b/sql/share/french/errmsg.txt index 594fcf900b4..08aa5760d6c 100644 --- a/sql/share/french/errmsg.txt +++ b/sql/share/french/errmsg.txt @@ -147,7 +147,7 @@ "La commande '%-.16s' est interdite à l'utilisateur: '%-.32s@%-.64s' sur la colonne '%-.64s' de la table '%-.64s'", "Commande GRANT/REVOKE incorrecte. Consultez le manuel.", "L'hôte ou l'utilisateur donné en argument à GRANT est trop long", -"La table '%-64s.%s' n'existe pas", +"La table '%-.64s.%s' n'existe pas", "Un tel droit n'est pas défini pour l'utilisateur '%-.32s' sur l'hôte '%-.64s' sur la table '%-.64s'", "Cette commande n'existe pas dans cette version de MySQL", "Erreur de syntaxe", @@ -179,7 +179,7 @@ "Vous êtes en mode 'safe update' et vous essayez de faire un UPDATE sans clause WHERE utilisant un index", "L'index '%-.64s' n'existe pas sur la table '%-.64s'", "Impossible d'ouvrir la table", -"Ce type de table ne supporte pas les check/repair", +"Ce type de table ne supporte pas les %s", "Vous n'êtes pas autorisé à exécute cette commande dans une transaction", "Erreur %d lors du COMMIT", "Erreur %d lors du ROLLBACK", @@ -219,3 +219,22 @@ "Impossible d'ajouter des contraintes d'index externe", "Impossible d'ajouter un enregistrement fils : une constrainte externe l'empèche", "Impossible de supprimer un enregistrement père : une constrainte externe l'empèche", +"Error connecting to master: %-.128s", +"Error running query on master: %-.128s", +"Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", +"Can't execute the query because you have a conflicting read lock", +"Mixing of transactional and non-transactional tables is disabled", +"Option '%s' used twice in statement", +"User '%-.64s' has exceeded the '%s' resource (current value: %ld)", +"Access denied. You need the %-.128s privilege for this operation", +"Variable '%-.64s' is a LOCAL variable and can't be used with SET GLOBAL", +"Variable '%-.64s' is a GLOBAL variable and should be set with SET GLOBAL", +"Variable '%-.64s' doesn't have a default value", +"Variable '%-.64s' can't be set to the value of '%-.64s'", +"Wrong argument type to variable '%-.64s'", +"Variable '%-.64s' can only be set, not read", +"Wrong usage/placement of '%s'", +"This version of MySQL doesn't yet support '%s'", +"Got fatal error %d: '%-.128s' from master when reading data from binary log", diff --git a/sql/share/german/errmsg.txt b/sql/share/german/errmsg.txt index 4171b9b476e..42e8c6f069b 100644 --- a/sql/share/german/errmsg.txt +++ b/sql/share/german/errmsg.txt @@ -182,7 +182,7 @@ "Unter Verwendung des Sicheren Updatemodes wurde versucht eine Tabelle zu updaten ohne eine KEY-Spalte in der WHERE-Klausel", "Schlüssel '%-.64s' existiert nicht in der Tabelle '%-.64s'", "Kann Tabelle nicht öffnen", -"Der Tabellen-Handler für diese Tabelle unterstützt kein check/repair", +"Der Tabellen-Handler für diese Tabelle unterstützt kein %s", "Keine Berechtigung dieses Kommando in einer Transaktion auszuführen", "Fehler %d wärend COMMIT", "Fehler %d wärend ROLLBACK", @@ -222,3 +222,22 @@ "Cannot add foreign key constraint", "Cannot add a child row: a foreign key constraint fails", "Cannot delete a parent row: a foreign key constraint fails", +"Error connecting to master: %-.128s", +"Error running query on master: %-.128s", +"Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", +"Can't execute the query because you have a conflicting read lock", +"Mixing of transactional and non-transactional tables is disabled", +"Option '%s' used twice in statement", +"User '%-.64s' has exceeded the '%s' resource (current value: %ld)", +"Access denied. You need the %-.128s privilege for this operation", +"Variable '%-.64s' is a LOCAL variable and can't be used with SET GLOBAL", +"Variable '%-.64s' is a GLOBAL variable and should be set with SET GLOBAL", +"Variable '%-.64s' doesn't have a default value", +"Variable '%-.64s' can't be set to the value of '%-.64s'", +"Wrong argument type to variable '%-.64s'", +"Variable '%-.64s' can only be set, not read", +"Wrong usage/placement of '%s'", +"This version of MySQL doesn't yet support '%s'", +"Got fatal error %d: '%-.128s' from master when reading data from binary log", diff --git a/sql/share/greek/errmsg.txt b/sql/share/greek/errmsg.txt index bc9e42c7690..c212c5b5703 100644 --- a/sql/share/greek/errmsg.txt +++ b/sql/share/greek/errmsg.txt @@ -179,7 +179,7 @@ "You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column", "Key '%-.64s' doesn't exist in table '%-.64s'", "Can't open table", -"The handler for the table doesn't support check/repair", +"The handler for the table doesn't support %s", "You are not allowed to execute this command in a transaction", "Got error %d during COMMIT", "Got error %d during ROLLBACK", @@ -219,3 +219,22 @@ "Cannot add foreign key constraint", "Cannot add a child row: a foreign key constraint fails", "Cannot delete a parent row: a foreign key constraint fails", +"Error connecting to master: %-.128s", +"Error running query on master: %-.128s", +"Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", +"Can't execute the query because you have a conflicting read lock", +"Mixing of transactional and non-transactional tables is disabled", +"Option '%s' used twice in statement", +"User '%-.64s' has exceeded the '%s' resource (current value: %ld)", +"Access denied. You need the %-.128s privilege for this operation", +"Variable '%-.64s' is a LOCAL variable and can't be used with SET GLOBAL", +"Variable '%-.64s' is a GLOBAL variable and should be set with SET GLOBAL", +"Variable '%-.64s' doesn't have a default value", +"Variable '%-.64s' can't be set to the value of '%-.64s'", +"Wrong argument type to variable '%-.64s'", +"Variable '%-.64s' can only be set, not read", +"Wrong usage/placement of '%s'", +"This version of MySQL doesn't yet support '%s'", +"Got fatal error %d: '%-.128s' from master when reading data from binary log", diff --git a/sql/share/hungarian/errmsg.txt b/sql/share/hungarian/errmsg.txt index 29b75030d22..5e8affe32b7 100644 --- a/sql/share/hungarian/errmsg.txt +++ b/sql/share/hungarian/errmsg.txt @@ -149,7 +149,7 @@ "%-.16s parancs a '%-.32s@%-.64s' felhasznalo szamara nem engedelyezett a '%-.64s' mezo eseten a '%-.64s' tablaban", "Ervenytelen GRANT/REVOKE parancs. Kerem, nezze meg a kezikonyvben, milyen jogok lehetsegesek", "A host vagy felhasznalo argumentuma tul hosszu a GRANT parancsban", -"A '%-64s.%s' tabla nem letezik", +"A '%-.64s.%s' tabla nem letezik", "A '%-.32s' felhasznalo szamara a '%-.64s' host '%-.64s' tablajaban ez a parancs nem engedelyezett", "A hasznalt parancs nem engedelyezett ebben a MySQL verzioban", "Szintaktikai hiba", @@ -181,7 +181,7 @@ "On a biztonsagos update modot hasznalja, es WHERE that uses a KEY column", "A '%-.64s' kulcs nem letezik a '%-.64s' tablaban", "Nem tudom megnyitni a tablat", -"A tabla kezeloje (handler) nem tamogatja az ellenorzest/helyreallitast", +"A tabla kezeloje (handler) nem tamogatja az %s", "Az On szamara nem engedelyezett a parancs vegrehajtasa a tranzakcioban", "%d hiba a COMMIT vegrehajtasa soran", "%d hiba a ROLLBACK vegrehajtasa soran", @@ -221,3 +221,22 @@ "Cannot add foreign key constraint", "Cannot add a child row: a foreign key constraint fails", "Cannot delete a parent row: a foreign key constraint fails", +"Error connecting to master: %-.128s", +"Error running query on master: %-.128s", +"Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", +"Can't execute the query because you have a conflicting read lock", +"Mixing of transactional and non-transactional tables is disabled", +"Option '%s' used twice in statement", +"User '%-.64s' has exceeded the '%s' resource (current value: %ld)", +"Access denied. You need the %-.128s privilege for this operation", +"Variable '%-.64s' is a LOCAL variable and can't be used with SET GLOBAL", +"Variable '%-.64s' is a GLOBAL variable and should be set with SET GLOBAL", +"Variable '%-.64s' doesn't have a default value", +"Variable '%-.64s' can't be set to the value of '%-.64s'", +"Wrong argument type to variable '%-.64s'", +"Variable '%-.64s' can only be set, not read", +"Wrong usage/placement of '%s'", +"This version of MySQL doesn't yet support '%s'", +"Got fatal error %d: '%-.128s' from master when reading data from binary log", diff --git a/sql/share/italian/errmsg.txt b/sql/share/italian/errmsg.txt index 6d0c7bcdc73..3fdea588bf3 100644 --- a/sql/share/italian/errmsg.txt +++ b/sql/share/italian/errmsg.txt @@ -147,7 +147,7 @@ "Comando %-.16s negato per l'utente: '%-.32s@%-.64s' sulla colonna '%-.64s' della tabella '%-.64s'", "Comando GRANT/REVOKE illegale. Prego consultare il manuale per sapere quali privilegi possono essere usati.", "L'argomento host o utente per la GRANT e` troppo lungo", -"La tabella '%-64s.%s' non esiste", +"La tabella '%-.64s.%s' non esiste", "GRANT non definita per l'utente '%-.32s' dalla macchina '%-.64s' sulla tabella '%-.64s'", "Il comando utilizzato non e` supportato in questa versione di MySQL", "Errore di sintassi nella query SQL", @@ -179,7 +179,7 @@ "In modalita` 'safe update' si e` cercato di aggiornare una tabella senza clausola WHERE su una chiave", "La chiave '%-.64s' non esiste nella tabella '%-.64s'", "Impossibile aprire la tabella", -"Il gestore per la tabella non supporta il controllo/riparazione", +"Il gestore per la tabella non supporta il %s", "Non puoi eseguire questo comando in una transazione", "Rilevato l'errore %d durante il COMMIT", "Rilevato l'errore %d durante il ROLLBACK", @@ -219,3 +219,22 @@ "Impossibile aggiungere il vincolo di integrita' referenziale (foreign key constraint)", "Impossibile aggiungere la riga: un vincolo d'integrita' referenziale non e' soddisfatto", "Impossibile cancellare la riga: un vincolo d'integrita' referenziale non e' soddisfatto", +"Errore durante la connessione al master: %-.128s", +"Errore eseguendo una query sul master: %-.128s", +"Errore durante l'esecuzione del comando %s: %-.128s", +"Uso errato di %s e %s", +"La SELECT utilizzata ha un numero di colonne differente", +"Impossibile eseguire la query perche' c'e' un conflitto con in lock di lettura", +"E' disabilitata la possibilita' di mischiare tabelle transazionali e non-transazionali", +"L'opzione '%s' e' stata usata due volte nel comando", +"L'utente '%-.64s' ha ecceduto la risorsa '%s' (valore corrente: %ld)", +"Accesso non consentito. Serve il privilegio %-.128s per questa operazione", +"La variabile '%-.64s' e' una variabile locale ( LOCAL ) e non puo' essere cambiata usando SET GLOBAL", +"La variabile '%-.64s' e' una variabile globale ( GLOBAL ) e deve essere cambiata usando SET GLOBAL", +"La variabile '%-.64s' non ha un valore di default", +"Alla variabile '%-.64s' non puo' essere assegato il valore '%-.64s'", +"Tipo di valore errato per la variabile '%-.64s'", +"Alla variabile '%-.64s' e' di sola scrittura quindi puo' essere solo assegnato un valore, non letto", +"Uso/posizione di '%s' sbagliato", +"Questa versione di MySQL non supporta ancora '%s'", +"Errore fatale %d: '%-.128s' dal master leggendo i dati dal log binario", diff --git a/sql/share/japanese/errmsg.txt b/sql/share/japanese/errmsg.txt index 4b091bc20ba..f9c1645419d 100644 --- a/sql/share/japanese/errmsg.txt +++ b/sql/share/japanese/errmsg.txt @@ -149,7 +149,7 @@ "¥³¥Þ¥ó¥É %-.16s ¤Ï ¥æ¡¼¥¶¡¼ '%-.32s@%-.64s'\n ¥«¥é¥à '%-.64s' ¥Æ¡¼¥Ö¥ë '%-.64s' ¤ËÂФ·¤Æµö²Ä¤µ¤ì¤Æ¤¤¤Þ¤»¤ó", "Illegal GRANT/REVOKE command. Please consult the manual which privleges can be used.", "The host or user argument to GRANT is too long", -"Table '%-64s.%s' doesn't exist", +"Table '%-.64s.%s' doesn't exist", "There is no such grant defined for user '%-.32s' on host '%-.64s' on table '%-.64s'", "The used command is not allowed with this MySQL version", "Something is wrong in your syntax", @@ -181,7 +181,7 @@ "You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column", "Key '%-.64s' doesn't exist in table '%-.64s'", "Can't open table", -"The handler for the table doesn't support check/repair", +"The handler for the table doesn't support %s", "You are not allowed to execute this command in a transaction", "Got error %d during COMMIT", "Got error %d during ROLLBACK", @@ -221,3 +221,22 @@ "Cannot add foreign key constraint", "Cannot add a child row: a foreign key constraint fails", "Cannot delete a parent row: a foreign key constraint fails", +"Error connecting to master: %-.128s", +"Error running query on master: %-.128s", +"Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", +"Can't execute the query because you have a conflicting read lock", +"Mixing of transactional and non-transactional tables is disabled", +"Option '%s' used twice in statement", +"User '%-.64s' has exceeded the '%s' resource (current value: %ld)", +"Access denied. You need the %-.128s privilege for this operation", +"Variable '%-.64s' is a LOCAL variable and can't be used with SET GLOBAL", +"Variable '%-.64s' is a GLOBAL variable and should be set with SET GLOBAL", +"Variable '%-.64s' doesn't have a default value", +"Variable '%-.64s' can't be set to the value of '%-.64s'", +"Wrong argument type to variable '%-.64s'", +"Variable '%-.64s' can only be set, not read", +"Wrong usage/placement of '%s'", +"This version of MySQL doesn't yet support '%s'", +"Got fatal error %d: '%-.128s' from master when reading data from binary log", diff --git a/sql/share/korean/errmsg.txt b/sql/share/korean/errmsg.txt index 871d67ff21e..82d5a5ecfbe 100644 --- a/sql/share/korean/errmsg.txt +++ b/sql/share/korean/errmsg.txt @@ -147,7 +147,7 @@ "'%-.16s' ¸í·ÉÀº ´ÙÀ½ »ç¿ëÀÚ¿¡°Ô °ÅºÎµÇ¾ú½À´Ï´Ù. : '%-.32s@%-.64s' for Ä®·³ '%-.64s' in Å×À̺í '%-.64s'", "À߸øµÈ GRANT/REVOKE ¸í·É. ¾î¶² ±Ç¸®¿Í ½ÂÀÎÀÌ »ç¿ëµÇ¾î Áú ¼ö ÀÖ´ÂÁö ¸Þ´º¾óÀ» º¸½Ã¿À.", "½ÂÀÎ(GRANT)À» À§ÇÏ¿© »ç¿ëÇÑ »ç¿ëÀÚ³ª È£½ºÆ®ÀÇ °ªµéÀÌ ³Ê¹« ±é´Ï´Ù.", -"Å×À̺í '%-64s.%s' ´Â Á¸ÀçÇÏÁö ¾Ê½À´Ï´Ù.", +"Å×À̺í '%-.64s.%s' ´Â Á¸ÀçÇÏÁö ¾Ê½À´Ï´Ù.", "»ç¿ëÀÚ '%-.32s'(È£½ºÆ® '%-.64s')´Â Å×À̺í '%-.64s'¸¦ »ç¿ëÇϱâ À§ÇÏ¿© Á¤ÀÇµÈ ½ÂÀÎÀº ¾ø½À´Ï´Ù. ", "»ç¿ëµÈ ¸í·ÉÀº ÇöÀçÀÇ MySQL ¹öÁ¯¿¡¼´Â ÀÌ¿ëµÇÁö ¾Ê½À´Ï´Ù.", "SQL ±¸¹®¿¡ ¿À·ù°¡ ÀÖ½À´Ï´Ù.", @@ -179,7 +179,7 @@ "You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column", "Key '%-.64s' doesn't exist in table '%-.64s'", "Can't open table", -"The handler for the table doesn't support check/repair", +"The handler for the table doesn't support %s", "You are not allowed to execute this command in a transaction", "Got error %d during COMMIT", "Got error %d during ROLLBACK", @@ -219,3 +219,22 @@ "Cannot add foreign key constraint", "Cannot add a child row: a foreign key constraint fails", "Cannot delete a parent row: a foreign key constraint fails", +"Error connecting to master: %-.128s", +"Error running query on master: %-.128s", +"Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", +"Can't execute the query because you have a conflicting read lock", +"Mixing of transactional and non-transactional tables is disabled", +"Option '%s' used twice in statement", +"User '%-.64s' has exceeded the '%s' resource (current value: %ld)", +"Access denied. You need the %-.128s privilege for this operation", +"Variable '%-.64s' is a LOCAL variable and can't be used with SET GLOBAL", +"Variable '%-.64s' is a GLOBAL variable and should be set with SET GLOBAL", +"Variable '%-.64s' doesn't have a default value", +"Variable '%-.64s' can't be set to the value of '%-.64s'", +"Wrong argument type to variable '%-.64s'", +"Variable '%-.64s' can only be set, not read", +"Wrong usage/placement of '%s'", +"This version of MySQL doesn't yet support '%s'", +"Got fatal error %d: '%-.128s' from master when reading data from binary log", diff --git a/sql/share/norwegian-ny/errmsg.txt b/sql/share/norwegian-ny/errmsg.txt index 6b42418c917..a218d5873b0 100644 --- a/sql/share/norwegian-ny/errmsg.txt +++ b/sql/share/norwegian-ny/errmsg.txt @@ -149,7 +149,7 @@ "%-.16s command denied to user: '%-.32s@%-.64s' for column '%-.64s' in table '%-.64s'", "Illegal GRANT/REVOKE command. Please consult the manual which privleges can be used.", "The host or user argument to GRANT is too long", -"Table '%-64s.%s' doesn't exist", +"Table '%-.64s.%s' doesn't exist", "There is no such grant defined for user '%-.32s' on host '%-.64s' on table '%-.64s'", "The used command is not allowed with this MySQL version", "Something is wrong in your syntax", @@ -181,7 +181,7 @@ "You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column", "Key '%-.64s' doesn't exist in table '%-.64s'", "Can't open table", -"The handler for the table doesn't support check/repair", +"The handler for the table doesn't support %s", "You are not allowed to execute this command in a transaction", "Got error %d during COMMIT", "Got error %d during ROLLBACK", @@ -221,3 +221,22 @@ "Cannot add foreign key constraint", "Cannot add a child row: a foreign key constraint fails", "Cannot delete a parent row: a foreign key constraint fails", +"Error connecting to master: %-.128s", +"Error running query on master: %-.128s", +"Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", +"Can't execute the query because you have a conflicting read lock", +"Mixing of transactional and non-transactional tables is disabled", +"Option '%s' used twice in statement", +"User '%-.64s' has exceeded the '%s' resource (current value: %ld)", +"Access denied. You need the %-.128s privilege for this operation", +"Variable '%-.64s' is a LOCAL variable and can't be used with SET GLOBAL", +"Variable '%-.64s' is a GLOBAL variable and should be set with SET GLOBAL", +"Variable '%-.64s' doesn't have a default value", +"Variable '%-.64s' can't be set to the value of '%-.64s'", +"Wrong argument type to variable '%-.64s'", +"Variable '%-.64s' can only be set, not read", +"Wrong usage/placement of '%s'", +"This version of MySQL doesn't yet support '%s'", +"Got fatal error %d: '%-.128s' from master when reading data from binary log", diff --git a/sql/share/norwegian/errmsg.txt b/sql/share/norwegian/errmsg.txt index 0e9ead25986..9ed50b4a00b 100644 --- a/sql/share/norwegian/errmsg.txt +++ b/sql/share/norwegian/errmsg.txt @@ -149,7 +149,7 @@ "%-.16s command denied to user: '%-.32s@%-.64s' for column '%-.64s' in table '%-.64s'", "Illegal GRANT/REVOKE command. Please consult the manual which privleges can be used.", "The host or user argument to GRANT is too long", -"Table '%-64s.%s' doesn't exist", +"Table '%-.64s.%s' doesn't exist", "There is no such grant defined for user '%-.32s' on host '%-.64s' on table '%-.64s'", "The used command is not allowed with this MySQL version", "Something is wrong in your syntax", @@ -181,7 +181,7 @@ "You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column", "Key '%-.64s' doesn't exist in table '%-.64s'", "Can't open table", -"The handler for the table doesn't support check/repair", +"The handler for the table doesn't support %s", "You are not allowed to execute this command in a transaction", "Got error %d during COMMIT", "Got error %d during ROLLBACK", @@ -221,3 +221,22 @@ "Cannot add foreign key constraint", "Cannot add a child row: a foreign key constraint fails", "Cannot delete a parent row: a foreign key constraint fails", +"Error connecting to master: %-.128s", +"Error running query on master: %-.128s", +"Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", +"Can't execute the query because you have a conflicting read lock", +"Mixing of transactional and non-transactional tables is disabled", +"Option '%s' used twice in statement", +"User '%-.64s' has exceeded the '%s' resource (current value: %ld)", +"Access denied. You need the %-.128s privilege for this operation", +"Variable '%-.64s' is a LOCAL variable and can't be used with SET GLOBAL", +"Variable '%-.64s' is a GLOBAL variable and should be set with SET GLOBAL", +"Variable '%-.64s' doesn't have a default value", +"Variable '%-.64s' can't be set to the value of '%-.64s'", +"Wrong argument type to variable '%-.64s'", +"Variable '%-.64s' can only be set, not read", +"Wrong usage/placement of '%s'", +"This version of MySQL doesn't yet support '%s'", +"Got fatal error %d: '%-.128s' from master when reading data from binary log", diff --git a/sql/share/polish/errmsg.txt b/sql/share/polish/errmsg.txt index 7bb05ba175f..e5bede3b48c 100644 --- a/sql/share/polish/errmsg.txt +++ b/sql/share/polish/errmsg.txt @@ -151,7 +151,7 @@ "%-.16s command denied to user: '%-.32s@%-.64s' for column '%-.64s' in table '%-.64s'", "Illegal GRANT/REVOKE command. Please consult the manual which privleges can be used.", "The host or user argument to GRANT is too long", -"Table '%-64s.%s' doesn't exist", +"Table '%-.64s.%s' doesn't exist", "There is no such grant defined for user '%-.32s' on host '%-.64s' on table '%-.64s'", "The used command is not allowed with this MySQL version", "Something is wrong in your syntax", @@ -183,7 +183,7 @@ "You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column", "Key '%-.64s' doesn't exist in table '%-.64s'", "Can't open table", -"The handler for the table doesn't support check/repair", +"The handler for the table doesn't support %s", "You are not allowed to execute this command in a transaction", "Got error %d during COMMIT", "Got error %d during ROLLBACK", @@ -223,3 +223,22 @@ "Cannot add foreign key constraint", "Cannot add a child row: a foreign key constraint fails", "Cannot delete a parent row: a foreign key constraint fails", +"Error connecting to master: %-.128s", +"Error running query on master: %-.128s", +"Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", +"Can't execute the query because you have a conflicting read lock", +"Mixing of transactional and non-transactional tables is disabled", +"Option '%s' used twice in statement", +"User '%-.64s' has exceeded the '%s' resource (current value: %ld)", +"Access denied. You need the %-.128s privilege for this operation", +"Variable '%-.64s' is a LOCAL variable and can't be used with SET GLOBAL", +"Variable '%-.64s' is a GLOBAL variable and should be set with SET GLOBAL", +"Variable '%-.64s' doesn't have a default value", +"Variable '%-.64s' can't be set to the value of '%-.64s'", +"Wrong argument type to variable '%-.64s'", +"Variable '%-.64s' can only be set, not read", +"Wrong usage/placement of '%s'", +"This version of MySQL doesn't yet support '%s'", +"Got fatal error %d: '%-.128s' from master when reading data from binary log", diff --git a/sql/share/portuguese/errmsg.txt b/sql/share/portuguese/errmsg.txt index f3861bc945e..0badf76c13d 100644 --- a/sql/share/portuguese/errmsg.txt +++ b/sql/share/portuguese/errmsg.txt @@ -179,7 +179,7 @@ "Você está usando modo de atualização seguro e tentou atualizar uma tabela sem uma cláusula WHERE que use uma coluna chave", "Chave '%-.64s' não existe na tabela '%-.64s'", "Não pode abrir a tabela", -"O manipulador de tabela não suporta checagem/reparação (check/repair)", +"O manipulador de tabela não suporta %s", "Não lhe é permitido executar este comando em uma transação", "Obteve erro %d durante COMMIT", "Obteve erro %d durante ROLLBACK", @@ -219,3 +219,22 @@ "Não pode acrescentar uma restrição de chave estrangeira", "Não pode acrescentar uma linha filha: uma restrição de chave estrangeira falhou", "Não pode apagar uma linha pai: uma restrição de chave estrangeira falhou", +"Error connecting to master: %-.128s", +"Error running query on master: %-.128s", +"Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", +"Can't execute the query because you have a conflicting read lock", +"Mixing of transactional and non-transactional tables is disabled", +"Option '%s' used twice in statement", +"User '%-.64s' has exceeded the '%s' resource (current value: %ld)", +"Access denied. You need the %-.128s privilege for this operation", +"Variable '%-.64s' is a LOCAL variable and can't be used with SET GLOBAL", +"Variable '%-.64s' is a GLOBAL variable and should be set with SET GLOBAL", +"Variable '%-.64s' doesn't have a default value", +"Variable '%-.64s' can't be set to the value of '%-.64s'", +"Wrong argument type to variable '%-.64s'", +"Variable '%-.64s' can only be set, not read", +"Wrong usage/placement of '%s'", +"This version of MySQL doesn't yet support '%s'", +"Got fatal error %d: '%-.128s' from master when reading data from binary log", diff --git a/sql/share/romanian/errmsg.txt b/sql/share/romanian/errmsg.txt index 9ddb06a2c24..1b036481538 100644 --- a/sql/share/romanian/errmsg.txt +++ b/sql/share/romanian/errmsg.txt @@ -183,7 +183,7 @@ "You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column", "Key '%-.64s' doesn't exist in table '%-.64s'", "Can't open table", -"The handler for the table doesn't support check/repair", +"The handler for the table doesn't support %s", "You are not allowed to execute this command in a transaction", "Got error %d during COMMIT", "Got error %d during ROLLBACK", @@ -223,3 +223,22 @@ "Cannot add foreign key constraint", "Cannot add a child row: a foreign key constraint fails", "Cannot delete a parent row: a foreign key constraint fails", +"Error connecting to master: %-.128s", +"Error running query on master: %-.128s", +"Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", +"Can't execute the query because you have a conflicting read lock", +"Mixing of transactional and non-transactional tables is disabled", +"Option '%s' used twice in statement", +"User '%-.64s' has exceeded the '%s' resource (current value: %ld)", +"Access denied. You need the %-.128s privilege for this operation", +"Variable '%-.64s' is a LOCAL variable and can't be used with SET GLOBAL", +"Variable '%-.64s' is a GLOBAL variable and should be set with SET GLOBAL", +"Variable '%-.64s' doesn't have a default value", +"Variable '%-.64s' can't be set to the value of '%-.64s'", +"Wrong argument type to variable '%-.64s'", +"Variable '%-.64s' can only be set, not read", +"Wrong usage/placement of '%s'", +"This version of MySQL doesn't yet support '%s'", +"Got fatal error %d: '%-.128s' from master when reading data from binary log", diff --git a/sql/share/russian/errmsg.txt b/sql/share/russian/errmsg.txt index d6d878fef8b..ef3fb4e87e5 100644 --- a/sql/share/russian/errmsg.txt +++ b/sql/share/russian/errmsg.txt @@ -182,7 +182,7 @@ "MySQL ÒÁÂÏÔÁÅÔ × ÒÅÖÉÍÅ ÚÁÝÉÔÙ ÏÔ ÄÕÒÁËÏ× (safe_mode) - ÎÅ ÍÏÇÕ UPDATE ÂÅÚ WHERE Ó ËÁËÉÍ-ÎÅÂÕÄØ KEY", "éÎÄÅËÓ '%-.64s' ÎÅ ÎÁÊÄÅÎ × ÔÁÂÌÉÃÅ '%-.64s'", "îÅ ÍÏÇÕ ÏÔËÒÙÔØ ÔÁÂÌÉÃÕ", -"äÁÎÎÙÊ ÔÉÐ ÔÁÂÌÉà ÎÅ ÐÏÄÄÅÒÖÉ×ÁÅÔ check/repair", +"äÁÎÎÙÊ ÔÉÐ ÔÁÂÌÉà ÎÅ ÐÏÄÄÅÒÖÉ×ÁÅÔ ËÏÍÁÎÄÕ %s", "üÔÁ ËÏÍÁÎÄÁ ×ÎÕÔÒÉ ÔÒÁÎÚÁËÃÉÉ ÚÁÐÒÅÝÅÎÁ", "ïÛÉÂËÁ %d ×Ï ×ÒÅÍÑ COMMIT", "ïÛÉÂËÁ %d ×Ï ×ÒÅÍÑ ROLLBACK", @@ -222,3 +222,22 @@ "Cannot add foreign key constraint", "Cannot add a child row: a foreign key constraint fails", "Cannot delete a parent row: a foreign key constraint fails", +"ïÛÉÂËÁ ÓÏÅÄÉÎÅÎÉÑ Ó master: %-.128s", +"ïÛÉÂËÁ ×Ù×ÏÌÎÅÎÉÑ ÚÁÐÒÏÓÁ ÎÁ master: %-.128s", +"ïÛÉÂËÁ ×ÙÐÏÌÎÅÎÉÑ ËÏÍÁÎÄÙ %s: %-.128s", +"îÅÐÒÁ×ÉÌØÎÏÅ ÉÓÐÏÌØÚÏ×ÁÎÉÅ %s É %s", +"éÓÐÏÌØÚÕÅÍÙÅ SELECT-×ÙÒÁÖÅÎÉÑ ÉÍÅÀÔ ÒÁÚÎÙÅ ËÏÌÉÞÅÓÔ×Á ÓÔÏÌÂÃÏ×", +"îÅ×ÏÚÍÏÖÎÏ ×ÙÐÏÌÎÉÔØ ÚÁÐÒÏÓ ÉÚ-ÚÁ ËÏÎÆÌÉËÔÎÏÊ ÂÌÏËÉÒÏ×ËÉ ÞÔÅÎÉÑ", +"ïÄÎÏ×ÒÅÍÅÎÎÏÅ ÉÓÐÏÌØÚÏ×ÁÎÉÅ transactional É non-transactional ÔÁÂÌÉà ÏÔËÌÀÞÅÎÏ", +"ïÐÃÉÑ '%s' ÉÓÐÏÌØÚÏ×ÁÎÁ Ä×ÁÖÄÙ", +"User '%-.64s' has exceeded the '%s' resource (current value: %ld)", +"Access denied. You need the %-.128s privilege for this operation", +"Variable '%-.64s' is a LOCAL variable and can't be used with SET GLOBAL", +"Variable '%-.64s' is a GLOBAL variable and should be set with SET GLOBAL", +"Variable '%-.64s' doesn't have a default value", +"Variable '%-.64s' can't be set to the value of '%-.64s'", +"Wrong argument type to variable '%-.64s'", +"Variable '%-.64s' can only be set, not read", +"Wrong usage/placement of '%s'", +"This version of MySQL doesn't yet support '%s'", +"Got fatal error %d: '%-.128s' from master when reading data from binary log", diff --git a/sql/share/slovak/errmsg.txt b/sql/share/slovak/errmsg.txt index 8b06ec7e98f..6424dcec6ee 100644 --- a/sql/share/slovak/errmsg.txt +++ b/sql/share/slovak/errmsg.txt @@ -155,7 +155,7 @@ "%-.16s command denied to user: '%-.32s@%-.64s' for column '%-.64s' in table '%-.64s'", "Illegal GRANT/REVOKE command. Please consult the manual which privleges can be used.", "The host or user argument to GRANT is too long", -"Table '%-64s.%s' doesn't exist", +"Table '%-.64s.%s' doesn't exist", "There is no such grant defined for user '%-.32s' on host '%-.64s' on table '%-.64s'", "The used command is not allowed with this MySQL version", "Something is wrong in your syntax", @@ -187,7 +187,7 @@ "You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column", "Key '%-.64s' doesn't exist in table '%-.64s'", "Can't open table", -"The handler for the table doesn't support check/repair", +"The handler for the table doesn't support %s", "You are not allowed to execute this command in a transaction", "Got error %d during COMMIT", "Got error %d during ROLLBACK", @@ -227,3 +227,22 @@ "Cannot add foreign key constraint", "Cannot add a child row: a foreign key constraint fails", "Cannot delete a parent row: a foreign key constraint fails", +"Error connecting to master: %-.128s", +"Error running query on master: %-.128s", +"Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", +"Can't execute the query because you have a conflicting read lock", +"Mixing of transactional and non-transactional tables is disabled", +"Option '%s' used twice in statement", +"User '%-.64s' has exceeded the '%s' resource (current value: %ld)", +"Access denied. You need the %-.128s privilege for this operation", +"Variable '%-.64s' is a LOCAL variable and can't be used with SET GLOBAL", +"Variable '%-.64s' is a GLOBAL variable and should be set with SET GLOBAL", +"Variable '%-.64s' doesn't have a default value", +"Variable '%-.64s' can't be set to the value of '%-.64s'", +"Wrong argument type to variable '%-.64s'", +"Variable '%-.64s' can only be set, not read", +"Wrong usage/placement of '%s'", +"This version of MySQL doesn't yet support '%s'", +"Got fatal error %d: '%-.128s' from master when reading data from binary log", diff --git a/sql/share/spanish/errmsg.txt b/sql/share/spanish/errmsg.txt index b9c88873106..df95a02a7aa 100644 --- a/sql/share/spanish/errmsg.txt +++ b/sql/share/spanish/errmsg.txt @@ -148,7 +148,7 @@ "%-.16s comando negado para usuario: '%-.32s@%-.64s' para columna '%-.64s' en la tabla '%-.64s'", "Ilegal comando GRANT/REVOKE. Por favor consulte el manual para cuales permisos pueden ser usados.", "El argumento para servidor o usuario para GRANT es demasiado grande", -"Tabla '%-64s.%s' no existe", +"Tabla '%-.64s.%s' no existe", "No existe tal permiso definido para usuario '%-.32s' en el servidor '%-.64s' en la tabla '%-.64s'", "El comando usado no es permitido con esta versión de MySQL", "Algo está equivocado en su sintax", @@ -180,7 +180,7 @@ "Tu estás usando modo de actualización segura y tentado actualizar una tabla sin un WHERE que usa una KEY columna", "Clave '%-.64s' no existe en la tabla '%-.64s'", "No puedo abrir tabla", -"El manipulador de la tabla no permite soporte para check/repair", +"El manipulador de la tabla no permite soporte para %s", "No tienes el permiso para ejecutar este comando en una transición", "Obtenido error %d durante COMMIT", "Obtenido error %d durante ROLLBACK", @@ -220,3 +220,22 @@ "Cannot add foreign key constraint", "Cannot add a child row: a foreign key constraint fails", "Cannot delete a parent row: a foreign key constraint fails", +"Error de coneccion a master: %-128s", +"Error executando el query en master: %-128%", +"Error de %s: %-128%", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", +"Can't execute the query because you have a conflicting read lock", +"Mixing of transactional and non-transactional tables is disabled", +"Option '%s' used twice in statement", +"User '%-.64s' has exceeded the '%s' resource (current value: %ld)", +"Access denied. You need the %-.128s privilege for this operation", +"Variable '%-.64s' is a LOCAL variable and can't be used with SET GLOBAL", +"Variable '%-.64s' is a GLOBAL variable and should be set with SET GLOBAL", +"Variable '%-.64s' doesn't have a default value", +"Variable '%-.64s' can't be set to the value of '%-.64s'", +"Wrong argument type to variable '%-.64s'", +"Variable '%-.64s' can only be set, not read", +"Wrong usage/placement of '%s'", +"This version of MySQL doesn't yet support '%s'", +"Got fatal error %d: '%-.128s' from master when reading data from binary log", diff --git a/sql/share/swedish/errmsg.OLD b/sql/share/swedish/errmsg.OLD index cc54e051e63..3dd14c8b613 100644 --- a/sql/share/swedish/errmsg.OLD +++ b/sql/share/swedish/errmsg.OLD @@ -205,11 +205,17 @@ "Kunde inte initializera replications-strukturerna. Kontrollera privilegerna för 'master.info'", "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", -"DROP DATABASE not allowed while thread is holding global read lock", -"CREATE DATABASE not allowed while thread is holding global read lock", -#ER_WRONG_ARGUMENTS +"Man kan endast använda konstant-uttryck med SET", +"Fick inte ett lås i tid", +"Antal lås överskrider antalet reserverade lås", +"Updaterings-lås kan inte göras när man använder READ UNCOMMITTED", +"DROP DATABASE är inte tillåtet när man har ett globalt läs-lås", +"CREATE DATABASE är inte tillåtet när man har ett globalt läs-lås", "Felaktiga argument till %s", +"%-.32s@%-.64s har inte rättigheter att skapa nya användare", +"Fick fel vid anslutning till master: %-.128s", +"Fick fel vid utförande av command på mastern: %-.128s", +"Fick fel vid utförande av %s: %-.128s", +"Felaktig använding av %s and %s", +"SELECT kommandona har olika antal kolumner" +"Kan inte utföra kommandot emedan du har ett READ lås", diff --git a/sql/share/swedish/errmsg.txt b/sql/share/swedish/errmsg.txt index 3bdc5842cb1..e0da1bfb4c1 100644 --- a/sql/share/swedish/errmsg.txt +++ b/sql/share/swedish/errmsg.txt @@ -61,7 +61,7 @@ "Kommandot har både sum functions och enkla funktioner", "Antalet kolumner motsvarar inte antalet värden", "Kolumn namn '%-.64s' är för långt", -"Kolumn namn '%-64s finns flera gånger", +"Kolumn namn '%-.64s finns flera gånger", "Nyckel namn '%-.64s' finns flera gånger", "Dubbel nyckel '%-.64s' för nyckel: %d", "Felaktigt kolumn typ för kolumn: '%-.64s'", @@ -147,7 +147,7 @@ "%-.16s ej tillåtet för '%-.32s@%-.64s'\n för kolumn '%-.64s' i tabell '%-.64s'", "Felaktigt GRANT privilegium använt", "Felaktigt maskinnamn eller användarnamn använt med GRANT", -"Det finns ingen tabell som heter '%-64s.%s'", +"Det finns ingen tabell som heter '%-.64s.%s'", "Det finns inget privilegium definierat för användare '%-.32s' på '%-.64s' för tabell '%-.64s'", "Du kan inte använda detta kommando med denna MySQL version", "Du har något fel i din syntax", @@ -179,7 +179,7 @@ "Du använder 'säker uppdaterings mod' och försökte uppdatera en table utan en WHERE sats som använder sig av en nyckel", "Nyckel '%-.64s' finns inte in tabell '%-.64s'", "Kan inte öppna tabellen", -"Tabellhanteraren för denna tabell kan inte göra check/repair", +"Tabellhanteraren för denna tabell kan inte göra %s", "Du får inte utföra detta kommando i en transaktion", "Fick fel %d vid COMMIT", "Fick fel %d vid ROLLBACK", @@ -219,3 +219,22 @@ "Kan inte lägga till 'FOREIGN KEY constraint'", "FOREIGN KEY konflikt: Kan inte skriva barn", "FOREIGN KEY konflikt: Kan inte radera fader", +"Fick fel vid anslutning till master: %-.128s", +"Fick fel vid utförande av command på mastern: %-.128s", +"Fick fel vid utförande av %s: %-.128s", +"Felaktig använding av %s and %s", +"SELECT kommandona har olika antal kolumner" +"Kan inte utföra kommandot emedan du har ett READ lås", +"Blandning av transaktionella och icke-transaktionella tabeller är inaktiverat", +"Option '%s' användes två gånger", +"Användare '%-.64s' har överskridit '%s' (nuvarande värde: %ld)", +"Du har inte privlegiet '%-.128s' som behövs för denna operation", +"Variable '%-.64s' is a LOCAL variable and can't be used with SET GLOBAL", +"Variable '%-.64s' is a GLOBAL variable and should be set with SET GLOBAL", +"Variable '%-.64s' doesn't have a default value", +"Variable '%-.64s' can't be set to the value of '%-.64s'", +"Wrong argument type to variable '%-.64s'", +"Variable '%-.64s' can only be set, not read", +"Wrong usage/placement of '%s'", +"This version of MySQL doesn't yet support '%s'", +"Got fatal error %d: '%-.128s' from master when reading data from binary log", diff --git a/sql/share/ukrainian/errmsg.txt b/sql/share/ukrainian/errmsg.txt index 7772f54d483..c1d98e83f7d 100644 --- a/sql/share/ukrainian/errmsg.txt +++ b/sql/share/ukrainian/errmsg.txt @@ -184,7 +184,7 @@ "÷É Õ ÒÅÖÉͦ ÂÅÚÐÅÞÎÏÇÏ ÏÎÏ×ÌÅÎÎÑ ÔÁ ÎÁÍÁÇÁ¤ÔÅÓØ ÏÎÏ×ÉÔÉ ÔÁÂÌÉÃÀ ÂÅÚ ÏÐÅÒÁÔÏÒÁ WHERE, ÝÏ ×ÉËÏÒÉÓÔÏ×Õ¤ KEY ÓÔÏ×ÂÅÃØ", "ëÌÀÞ '%-.64s' ÎÅ ¦ÓÎÕ¤ × ÔÁÂÌÉæ '%-.64s'", "îÅ ÍÏÖÕ ×¦ÄËÒÉÔÉ ÔÁÂÌÉÃÀ", -"÷ËÁÚ¦×ÎÉË ÔÁÂÌÉæ ΊЦÄÔÒÉÍÕÅ ÐÅÒÅצÒËÕ/צÄÎÏ×ÌÅÎÎÑ", +"÷ËÁÚ¦×ÎÉË ÔÁÂÌÉæ ΊЦÄÔÒÉÍÕÅ %s", "÷ÁÍ ÎÅ ÄÏÚ×ÏÌÅÎÏ ×ÉËÏÎÕ×ÁÔÉ ÃÀ ËÏÍÁÎÄÕ × ÔÒÁÎÚÁËæ§", "ïÔÒÉÍÁÎÏ ÐÏÍÉÌËÕ %d Ð¦Ä ÞÁÓ COMMIT", "ïÔÒÉÍÁÎÏ ÐÏÍÉÌËÕ %d Ð¦Ä ÞÁÓ ROLLBACK", @@ -224,3 +224,22 @@ "Cannot add foreign key constraint", "Cannot add a child row: a foreign key constraint fails", "Cannot delete a parent row: a foreign key constraint fails", +"Error connecting to master: %-.128s", +"Error running query on master: %-.128s", +"Error when executing command %s: %-.128s", +"Wrong usage of %s and %s", +"The used SELECT statements have a different number of columns", +"Can't execute the query because you have a conflicting read lock", +"Mixing of transactional and non-transactional tables is disabled", +"Option '%s' used twice in statement", +"User '%-.64s' has exceeded the '%s' resource (current value: %ld)", +"Access denied. You need the %-.128s privilege for this operation", +"Variable '%-.64s' is a LOCAL variable and can't be used with SET GLOBAL", +"Variable '%-.64s' is a GLOBAL variable and should be set with SET GLOBAL", +"Variable '%-.64s' doesn't have a default value", +"Variable '%-.64s' can't be set to the value of '%-.64s'", +"Wrong argument type to variable '%-.64s'", +"Variable '%-.64s' can only be set, not read", +"Wrong usage/placement of '%s'", +"This version of MySQL doesn't yet support '%s'", +"Got fatal error %d: '%-.128s' from master when reading data from binary log", diff --git a/sql/slave.cc b/sql/slave.cc index a1972dc5a5d..4d08fcbbd5a 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2003 MySQL AB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,49 +20,146 @@ #include <myisam.h> #include "mini_client.h" #include "slave.h" +#include "sql_repl.h" +#include "repl_failsafe.h" #include <thr_alarm.h> #include <my_dir.h> #include <assert.h> -#define RPL_LOG_NAME (glob_mi.log_file_name[0] ? glob_mi.log_file_name :\ - "FIRST") - -volatile bool slave_running = 0; -pthread_t slave_real_id; -MASTER_INFO glob_mi; -MY_BITMAP slave_error_mask; bool use_slave_mask = 0; +MY_BITMAP slave_error_mask; + +typedef bool (*CHECK_KILLED_FUNC)(THD*,void*); + +volatile bool slave_sql_running = 0, slave_io_running = 0; +char* slave_load_tmpdir = 0; +MASTER_INFO *active_mi; +volatile int active_mi_in_use = 0; HASH replicate_do_table, replicate_ignore_table; DYNAMIC_ARRAY replicate_wild_do_table, replicate_wild_ignore_table; bool do_table_inited = 0, ignore_table_inited = 0; bool wild_do_table_inited = 0, wild_ignore_table_inited = 0; bool table_rules_on = 0; -uint32 slave_skip_counter = 0; -static TABLE* save_temporary_tables = 0; -THD* slave_thd = 0; -// when slave thread exits, we need to remember the temporary tables so we -// can re-use them on slave start - -static int last_slave_errno = 0; -static char last_slave_error[1024] = ""; -#ifndef DBUG_OFF -int disconnect_slave_event_count = 0, abort_slave_event_count = 0; -static int events_till_disconnect = -1, events_till_abort = -1; -static int stuck_count = 0; -#endif +ulonglong relay_log_space_limit = 0; -inline void skip_load_data_infile(NET* net); -inline bool slave_killed(THD* thd); -static int init_slave_thread(THD* thd); +/* + When slave thread exits, we need to remember the temporary tables so we + can re-use them on slave start. + + TODO: move the vars below under MASTER_INFO +*/ + +int disconnect_slave_event_count = 0, abort_slave_event_count = 0; +int events_till_abort = -1; +static int events_till_disconnect = -1; + +typedef enum { SLAVE_THD_IO, SLAVE_THD_SQL} SLAVE_THD_TYPE; + +void skip_load_data_infile(NET* net); +static int process_io_rotate(MASTER_INFO* mi, Rotate_log_event* rev); +static int process_io_create_file(MASTER_INFO* mi, Create_file_log_event* cev); +static bool wait_for_relay_log_space(RELAY_LOG_INFO* rli); +static inline bool io_slave_killed(THD* thd,MASTER_INFO* mi); +static inline bool sql_slave_killed(THD* thd,RELAY_LOG_INFO* rli); +static int count_relay_log_space(RELAY_LOG_INFO* rli); +static int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type); static int safe_connect(THD* thd, MYSQL* mysql, MASTER_INFO* mi); static int safe_reconnect(THD* thd, MYSQL* mysql, MASTER_INFO* mi, bool suppress_warnings); -static int safe_sleep(THD* thd, int sec); -static int request_table_dump(MYSQL* mysql, char* db, char* table); +static int connect_to_master(THD* thd, MYSQL* mysql, MASTER_INFO* mi, + bool reconnect, bool suppress_warnings); +static int safe_sleep(THD* thd, int sec, CHECK_KILLED_FUNC thread_killed, + void* thread_killed_arg); +static int request_table_dump(MYSQL* mysql, const char* db, const char* table); static int create_table_from_dump(THD* thd, NET* net, const char* db, const char* table_name); -inline char* rewrite_db(char* db); -static int check_expected_error(THD* thd, int expected_error); +static int check_master_version(MYSQL* mysql, MASTER_INFO* mi); +char* rewrite_db(char* db); + + +/* + Get a bit mask for which threads are running so that we later can + restart these threads +*/ + +void init_thread_mask(int* mask,MASTER_INFO* mi,bool inverse) +{ + bool set_io = mi->slave_running, set_sql = mi->rli.slave_running; + register int tmp_mask=0; + if (set_io) + tmp_mask |= SLAVE_IO; + if (set_sql) + tmp_mask |= SLAVE_SQL; + if (inverse) + tmp_mask^= (SLAVE_IO | SLAVE_SQL); + *mask = tmp_mask; +} + + +void lock_slave_threads(MASTER_INFO* mi) +{ + //TODO: see if we can do this without dual mutex + pthread_mutex_lock(&mi->run_lock); + pthread_mutex_lock(&mi->rli.run_lock); +} + +void unlock_slave_threads(MASTER_INFO* mi) +{ + //TODO: see if we can do this without dual mutex + pthread_mutex_unlock(&mi->rli.run_lock); + pthread_mutex_unlock(&mi->run_lock); +} + + +int init_slave() +{ + DBUG_ENTER("init_slave"); + + /* + TODO: re-write this to interate through the list of files + for multi-master + */ + active_mi= new MASTER_INFO; + + /* + If master_host is not specified, try to read it from the master_info file. + If master_host is specified, create the master_info file if it doesn't + exists. + */ + if (!active_mi || + init_master_info(active_mi,master_info_file,relay_log_info_file, + !master_host)) + { + sql_print_error("Note: Failed to initialized master info"); + goto err; + } + + /* + make sure slave thread gets started if server_id is set, + valid master.info is present, and master_host has not been specified + */ + if (server_id && !master_host && active_mi->host[0]) + master_host= active_mi->host; + + if (master_host && !opt_skip_slave_start) + { + if (start_slave_threads(1 /* need mutex */, + 0 /* no wait for start*/, + active_mi, + master_info_file, + relay_log_info_file, + SLAVE_IO | SLAVE_SQL)) + { + sql_print_error("Warning: Can't create threads to handle slave"); + goto err; + } + } + DBUG_RETURN(0); + +err: + DBUG_RETURN(1); +} + static void free_table_ent(TABLE_RULE_ENT* e) { @@ -76,10 +173,119 @@ static byte* get_table_key(TABLE_RULE_ENT* e, uint* len, return (byte*)e->db; } + +/* + Open the given relay log + + SYNOPSIS + init_relay_log_pos() + rli Relay information (will be initialized) + log Name of relay log file to read from. NULL = First log + pos Position in relay log file + need_data_lock Set to 1 if this functions should do mutex locks + errmsg Store pointer to error message here + + DESCRIPTION + - Close old open relay log files. + - If we are using the same relay log as the running IO-thread, then set + rli->cur_log to point to the same IO_CACHE entry. + - If not, open the 'log' binary file. + + TODO + - check proper initialization of master_log_name/master_log_pos + - We may always want to delete all logs before 'log'. + Currently if we are not calling this with 'log' as NULL or the first + log we will never delete relay logs. + If we want this we should not set skip_log_purge to 1. + + RETURN VALUES + 0 ok + 1 error. errmsg is set to point to the error message +*/ + +int init_relay_log_pos(RELAY_LOG_INFO* rli,const char* log, + ulonglong pos, bool need_data_lock, + const char** errmsg) +{ + DBUG_ENTER("init_relay_log_pos"); + + *errmsg=0; + pthread_mutex_t *log_lock=rli->relay_log.get_log_lock(); + pthread_mutex_lock(log_lock); + if (need_data_lock) + pthread_mutex_lock(&rli->data_lock); + + /* Close log file and free buffers if it's already open */ + if (rli->cur_log_fd >= 0) + { + end_io_cache(&rli->cache_buf); + my_close(rli->cur_log_fd, MYF(MY_WME)); + rli->cur_log_fd = -1; + } + + rli->relay_log_pos = pos; + + /* + Test to see if the previous run was with the skip of purging + If yes, we do not purge when we restart + */ + if (rli->relay_log.find_log_pos(&rli->linfo, NullS, 1)) + { + *errmsg="Could not find first log during relay log initialization"; + goto err; + } + + if (log) // If not first log + { + if (strcmp(log, rli->linfo.log_file_name)) + rli->skip_log_purge=1; // Different name; Don't purge + if (rli->relay_log.find_log_pos(&rli->linfo, log, 1)) + { + *errmsg="Could not find target log during relay log initialization"; + goto err; + } + } + strmake(rli->relay_log_name,rli->linfo.log_file_name, + sizeof(rli->relay_log_name)-1); + if (rli->relay_log.is_active(rli->linfo.log_file_name)) + { + /* + The IO thread is using this log file. + In this case, we will use the same IO_CACHE pointer to + read data as the IO thread is using to write data. + */ + if (my_b_tell((rli->cur_log=rli->relay_log.get_log_file())) == 0 && + check_binlog_magic(rli->cur_log,errmsg)) + goto err; + rli->cur_log_old_open_count=rli->relay_log.get_open_count(); + } + else + { + /* + Open the relay log and set rli->cur_log to point at this one + */ + if ((rli->cur_log_fd=open_binlog(&rli->cache_buf, + rli->linfo.log_file_name,errmsg)) < 0) + goto err; + rli->cur_log = &rli->cache_buf; + } + if (pos > BIN_LOG_HEADER_SIZE) + my_b_seek(rli->cur_log,(off_t)pos); + +err: + pthread_cond_broadcast(&rli->data_cond); + if (need_data_lock) + pthread_mutex_unlock(&rli->data_lock); + pthread_mutex_unlock(log_lock); + DBUG_RETURN ((*errmsg) ? 1 : 0); +} + + /* called from get_options() in mysqld.cc on start-up */ -void init_slave_skip_errors(char* arg) + +void init_slave_skip_errors(const char* arg) { - char* p; + const char *p; if (bitmap_init(&slave_error_mask,MAX_SLAVE_ERROR,0)) { fprintf(stderr, "Badly out of memory, please check your system status\n"); @@ -105,11 +311,276 @@ void init_slave_skip_errors(char* arg) } } + +/* + We assume we have a run lock on rli and that both slave thread + are not running +*/ + +int purge_relay_logs(RELAY_LOG_INFO* rli, THD *thd, bool just_reset, + const char** errmsg) +{ + int error=0; + DBUG_ENTER("purge_relay_logs"); + + /* + Even if rli->inited==0, we still try to empty rli->master_log_* variables. + Indeed, rli->inited==0 does not imply that they already are empty. + It could be that slave's info initialization partly succeeded : + for example if relay-log.info existed but *relay-bin*.* + have been manually removed, init_relay_log_info reads the old + relay-log.info and fills rli->master_log_*, then init_relay_log_info + checks for the existence of the relay log, this fails and + init_relay_log_info leaves rli->inited to 0. + In that pathological case, rli->master_log_pos* will be properly reinited + at the next START SLAVE (as RESET SLAVE or CHANGE + MASTER, the callers of purge_relay_logs, will delete bogus *.info files + or replace them with correct files), however if the user does SHOW SLAVE + STATUS before START SLAVE, he will see old, confusing rli->master_log_*. + In other words, we reinit rli->master_log_* for SHOW SLAVE STATUS + to display fine in any case. + */ + + rli->master_log_name[0]= 0; + rli->master_log_pos= 0; + rli->pending= 0; + + if (!rli->inited) + DBUG_RETURN(0); + + DBUG_ASSERT(rli->slave_running == 0); + DBUG_ASSERT(rli->mi->slave_running == 0); + + rli->slave_skip_counter=0; + pthread_mutex_lock(&rli->data_lock); + if (rli->relay_log.reset_logs(thd)) + { + *errmsg = "Failed during log reset"; + error=1; + goto err; + } + /* Save name of used relay log file */ + strmake(rli->relay_log_name, rli->relay_log.get_log_fname(), + sizeof(rli->relay_log_name)-1); + // Just first log with magic number and nothing else + rli->log_space_total= BIN_LOG_HEADER_SIZE; + rli->relay_log_pos= BIN_LOG_HEADER_SIZE; + rli->relay_log.reset_bytes_written(); + if (!just_reset) + error= init_relay_log_pos(rli, rli->relay_log_name, rli->relay_log_pos, + 0 /* do not need data lock */, errmsg); + +err: +#ifndef DBUG_OFF + char buf[22]; +#endif + DBUG_PRINT("info",("log_space_total: %s",llstr(rli->log_space_total,buf))); + pthread_mutex_unlock(&rli->data_lock); + DBUG_RETURN(error); +} + + +int terminate_slave_threads(MASTER_INFO* mi,int thread_mask,bool skip_lock) +{ + if (!mi->inited) + return 0; /* successfully do nothing */ + int error,force_all = (thread_mask & SLAVE_FORCE_ALL); + pthread_mutex_t *sql_lock = &mi->rli.run_lock, *io_lock = &mi->run_lock; + pthread_mutex_t *sql_cond_lock,*io_cond_lock; + DBUG_ENTER("terminate_slave_threads"); + + sql_cond_lock=sql_lock; + io_cond_lock=io_lock; + + if (skip_lock) + { + sql_lock = io_lock = 0; + } + if ((thread_mask & (SLAVE_IO|SLAVE_FORCE_ALL)) && mi->slave_running) + { + DBUG_PRINT("info",("Terminating IO thread")); + mi->abort_slave=1; + if ((error=terminate_slave_thread(mi->io_thd,io_lock, + io_cond_lock, + &mi->stop_cond, + &mi->slave_running)) && + !force_all) + DBUG_RETURN(error); + } + if ((thread_mask & (SLAVE_SQL|SLAVE_FORCE_ALL)) && mi->rli.slave_running) + { + DBUG_PRINT("info",("Terminating SQL thread")); + DBUG_ASSERT(mi->rli.sql_thd != 0) ; + mi->rli.abort_slave=1; + if ((error=terminate_slave_thread(mi->rli.sql_thd,sql_lock, + sql_cond_lock, + &mi->rli.stop_cond, + &mi->rli.slave_running)) && + !force_all) + DBUG_RETURN(error); + } + DBUG_RETURN(0); +} + + +int terminate_slave_thread(THD* thd, pthread_mutex_t* term_lock, + pthread_mutex_t *cond_lock, + pthread_cond_t* term_cond, + volatile bool* slave_running) +{ + if (term_lock) + { + pthread_mutex_lock(term_lock); + if (!*slave_running) + { + pthread_mutex_unlock(term_lock); + return ER_SLAVE_NOT_RUNNING; + } + } + DBUG_ASSERT(thd != 0); + /* + Is is criticate to test if the slave is running. Otherwise, we might + be referening freed memory trying to kick it + */ + THD_CHECK_SENTRY(thd); + if (*slave_running) + { + KICK_SLAVE(thd); + } + while (*slave_running) + { + /* + There is a small chance that slave thread might miss the first + alarm. To protect againts it, resend the signal until it reacts + */ + struct timespec abstime; + set_timespec(abstime,2); + pthread_cond_timedwait(term_cond, cond_lock, &abstime); + if (*slave_running) + { + KICK_SLAVE(thd); + } + } + if (term_lock) + pthread_mutex_unlock(term_lock); + return 0; +} + + +int start_slave_thread(pthread_handler h_func, pthread_mutex_t *start_lock, + pthread_mutex_t *cond_lock, + pthread_cond_t *start_cond, + volatile bool *slave_running, + volatile ulong *slave_run_id, + MASTER_INFO* mi) +{ + pthread_t th; + ulong start_id; + DBUG_ASSERT(mi->inited); + DBUG_ENTER("start_slave_thread"); + + if (start_lock) + pthread_mutex_lock(start_lock); + if (!server_id) + { + if (start_cond) + pthread_cond_broadcast(start_cond); + if (start_lock) + pthread_mutex_unlock(start_lock); + sql_print_error("Server id not set, will not start slave"); + DBUG_RETURN(ER_BAD_SLAVE); + } + + if (*slave_running) + { + if (start_cond) + pthread_cond_broadcast(start_cond); + if (start_lock) + pthread_mutex_unlock(start_lock); + DBUG_RETURN(ER_SLAVE_MUST_STOP); + } + start_id= *slave_run_id; + DBUG_PRINT("info",("Creating new slave thread")); + if (pthread_create(&th, &connection_attrib, h_func, (void*)mi)) + { + if (start_lock) + pthread_mutex_unlock(start_lock); + DBUG_RETURN(ER_SLAVE_THREAD); + } + if (start_cond && cond_lock) + { + THD* thd = current_thd; + while (start_id == *slave_run_id) + { + DBUG_PRINT("sleep",("Waiting for slave thread to start")); + const char* old_msg = thd->enter_cond(start_cond,cond_lock, + "Waiting for slave thread to start"); + pthread_cond_wait(start_cond,cond_lock); + thd->exit_cond(old_msg); + if (thd->killed) + { + pthread_mutex_unlock(cond_lock); + DBUG_RETURN(ER_SERVER_SHUTDOWN); + } + } + } + if (start_lock) + pthread_mutex_unlock(start_lock); + DBUG_RETURN(0); +} + + +/* + SLAVE_FORCE_ALL is not implemented here on purpose since it does not make + sense to do that for starting a slave - we always care if it actually + started the threads that were not previously running +*/ + +int start_slave_threads(bool need_slave_mutex, bool wait_for_start, + MASTER_INFO* mi, const char* master_info_fname, + const char* slave_info_fname, int thread_mask) +{ + pthread_mutex_t *lock_io=0,*lock_sql=0,*lock_cond_io=0,*lock_cond_sql=0; + pthread_cond_t* cond_io=0,*cond_sql=0; + int error=0; + DBUG_ENTER("start_slave_threads"); + + if (need_slave_mutex) + { + lock_io = &mi->run_lock; + lock_sql = &mi->rli.run_lock; + } + if (wait_for_start) + { + cond_io = &mi->start_cond; + cond_sql = &mi->rli.start_cond; + lock_cond_io = &mi->run_lock; + lock_cond_sql = &mi->rli.run_lock; + } + + if (thread_mask & SLAVE_IO) + error=start_slave_thread(handle_slave_io,lock_io,lock_cond_io, + cond_io, + &mi->slave_running, &mi->slave_run_id, + mi); + if (!error && (thread_mask & SLAVE_SQL)) + { + error=start_slave_thread(handle_slave_sql,lock_sql,lock_cond_sql, + cond_sql, + &mi->rli.slave_running, &mi->rli.slave_run_id, + mi); + if (error) + terminate_slave_threads(mi, thread_mask & SLAVE_IO, 0); + } + DBUG_RETURN(error); +} + + void init_table_rule_hash(HASH* h, bool* h_inited) { hash_init(h, TABLE_RULE_HASH_SIZE,0,0, (hash_get_key) get_table_key, - (void (*)(void*)) free_table_ent, 0); + (hash_free_key) free_table_ent, 0); *h_inited = 1; } @@ -125,11 +596,11 @@ static TABLE_RULE_ENT* find_wild(DYNAMIC_ARRAY *a, const char* key, int len) uint i; const char* key_end = key + len; - for(i = 0; i < a->elements; i++) + for (i = 0; i < a->elements; i++) { TABLE_RULE_ENT* e ; get_dynamic(a, (gptr)&e, i); - if(!wild_case_compare(key, key_end, (const char*)e->db, + if (!wild_case_compare(key, key_end, (const char*)e->db, (const char*)(e->db + e->key_len),'\\')) return e; } @@ -137,51 +608,130 @@ static TABLE_RULE_ENT* find_wild(DYNAMIC_ARRAY *a, const char* key, int len) return 0; } + +/* + Checks whether tables match some (wild_)do_table and (wild_)ignore_table + rules (for replication) + + SYNOPSIS + tables_ok() + thd thread (SQL slave thread normally) + tables list of tables to check + + NOTES + Note that changing the order of the tables in the list can lead to + different results. Note also the order of precedence of the do/ignore + rules (see code below). For that reason, users should not set conflicting + rules because they may get unpredicted results. + + RETURN VALUES + 0 should not be logged/replicated + 1 should be logged/replicated +*/ + int tables_ok(THD* thd, TABLE_LIST* tables) { + DBUG_ENTER("tables_ok"); + for (; tables; tables = tables->next) { + char hash_key[2*NAME_LEN+2]; + char *end; + uint len; + if (!tables->updating) continue; - char hash_key[2*NAME_LEN+2]; - char* p; - p = strmov(hash_key, tables->db ? tables->db : thd->db); - *p++ = '.'; - uint len = strmov(p, tables->real_name) - hash_key ; + end= strmov(hash_key, tables->db ? tables->db : thd->db); + *end++= '.'; + len= (uint) (strmov(end, tables->real_name) - hash_key); if (do_table_inited) // if there are any do's { if (hash_search(&replicate_do_table, (byte*) hash_key, len)) - return 1; + DBUG_RETURN(1); } - if (ignore_table_inited) // if there are any do's + if (ignore_table_inited) // if there are any ignores { if (hash_search(&replicate_ignore_table, (byte*) hash_key, len)) - return 0; + DBUG_RETURN(0); } if (wild_do_table_inited && find_wild(&replicate_wild_do_table, hash_key, len)) - return 1; + DBUG_RETURN(1); if (wild_ignore_table_inited && find_wild(&replicate_wild_ignore_table, hash_key, len)) - return 0; + DBUG_RETURN(0); } - // if no explicit rule found - // and there was a do list, do not replicate. If there was - // no do list, go ahead - return !do_table_inited && !wild_do_table_inited; + /* + If no explicit rule found and there was a do list, do not replicate. + If there was no do list, go ahead + */ + DBUG_RETURN(!do_table_inited && !wild_do_table_inited); +} + + +/* + Checks whether a db matches wild_do_table and wild_ignore_table + rules (for replication) + + SYNOPSIS + db_ok_with_wild_table() + db name of the db to check. + Is tested with check_db_name() before calling this function. + + NOTES + Here is the reason for this function. + We advise users who want to exclude a database 'db1' safely to do it + with replicate_wild_ignore_table='db1.%' instead of binlog_ignore_db or + replicate_ignore_db because the two lasts only check for the selected db, + which won't work in that case: + USE db2; + UPDATE db1.t SET ... #this will be replicated and should not + whereas replicate_wild_ignore_table will work in all cases. + With replicate_wild_ignore_table, we only check tables. When + one does 'DROP DATABASE db1', tables are not involved and the + statement will be replicated, while users could expect it would not (as it + rougly means 'DROP db1.first_table, DROP db1.second_table...'). + In other words, we want to interpret 'db1.%' as "everything touching db1". + That is why we want to match 'db1' against 'db1.%' wild table rules. + + RETURN VALUES + 0 should not be logged/replicated + 1 should be logged/replicated + */ + +int db_ok_with_wild_table(const char *db) +{ + char hash_key[NAME_LEN+2]; + char *end; + int len; + end= strmov(hash_key, db); + *end++= '.'; + len= end - hash_key ; + if (wild_do_table_inited && find_wild(&replicate_wild_do_table, + hash_key, len)) + return 1; + if (wild_ignore_table_inited && find_wild(&replicate_wild_ignore_table, + hash_key, len)) + return 0; + + /* + If no explicit rule found and there was a do list, do not replicate. + If there was no do list, go ahead + */ + return !wild_do_table_inited; } int add_table_rule(HASH* h, const char* table_spec) { const char* dot = strchr(table_spec, '.'); - if(!dot) return 1; + if (!dot) return 1; // len is always > 0 because we know the there exists a '.' uint len = (uint)strlen(table_spec); TABLE_RULE_ENT* e = (TABLE_RULE_ENT*)my_malloc(sizeof(TABLE_RULE_ENT) + len, MYF(MY_WME)); - if(!e) return 1; + if (!e) return 1; e->db = (char*)e + sizeof(TABLE_RULE_ENT); e->tbl_name = e->db + (dot - table_spec) + 1; e->key_len = len; @@ -193,11 +743,11 @@ int add_table_rule(HASH* h, const char* table_spec) int add_wild_table_rule(DYNAMIC_ARRAY* a, const char* table_spec) { const char* dot = strchr(table_spec, '.'); - if(!dot) return 1; + if (!dot) return 1; uint len = (uint)strlen(table_spec); TABLE_RULE_ENT* e = (TABLE_RULE_ENT*)my_malloc(sizeof(TABLE_RULE_ENT) + len, MYF(MY_WME)); - if(!e) return 1; + if (!e) return 1; e->db = (char*)e + sizeof(TABLE_RULE_ENT); e->tbl_name = e->db + (dot - table_spec) + 1; e->key_len = len; @@ -209,7 +759,7 @@ int add_wild_table_rule(DYNAMIC_ARRAY* a, const char* table_spec) static void free_string_array(DYNAMIC_ARRAY *a) { uint i; - for(i = 0; i < a->elements; i++) + for (i = 0; i < a->elements; i++) { char* p; get_dynamic(a, (gptr) &p, i); @@ -218,100 +768,151 @@ static void free_string_array(DYNAMIC_ARRAY *a) delete_dynamic(a); } +#ifdef NOT_USED_YET + +static int end_slave_on_walk(MASTER_INFO* mi, gptr /*unused*/) +{ + end_master_info(mi); + return 0; +} +#endif + + void end_slave() { - pthread_mutex_lock(&LOCK_slave); - if (slave_running) + if (active_mi) { - abort_slave = 1; - thr_alarm_kill(slave_real_id); -#ifdef SIGNAL_WITH_VIO_CLOSE - slave_thd->close_active_vio(); -#endif - while (slave_running) - pthread_cond_wait(&COND_slave_stopped, &LOCK_slave); + /* + TODO: replace the line below with + list_walk(&master_list, (list_walk_action)end_slave_on_walk,0); + once multi-master code is ready. + */ + terminate_slave_threads(active_mi,SLAVE_FORCE_ALL); + end_master_info(active_mi); + if (do_table_inited) + hash_free(&replicate_do_table); + if (ignore_table_inited) + hash_free(&replicate_ignore_table); + if (wild_do_table_inited) + free_string_array(&replicate_wild_do_table); + if (wild_ignore_table_inited) + free_string_array(&replicate_wild_ignore_table); + delete active_mi; + active_mi= 0; } - pthread_mutex_unlock(&LOCK_slave); - - end_master_info(&glob_mi); - if(do_table_inited) - hash_free(&replicate_do_table); - if(ignore_table_inited) - hash_free(&replicate_ignore_table); - if(wild_do_table_inited) - free_string_array(&replicate_wild_do_table); - if(wild_ignore_table_inited) - free_string_array(&replicate_wild_ignore_table); } -inline bool slave_killed(THD* thd) + +static bool io_slave_killed(THD* thd, MASTER_INFO* mi) { - return abort_slave || abort_loop || thd->killed; + DBUG_ASSERT(mi->io_thd == thd); + DBUG_ASSERT(mi->slave_running == 1); // tracking buffer overrun + return mi->abort_slave || abort_loop || thd->killed; } -inline void skip_load_data_infile(NET* net) + +static bool sql_slave_killed(THD* thd, RELAY_LOG_INFO* rli) +{ + DBUG_ASSERT(rli->sql_thd == thd); + DBUG_ASSERT(rli->slave_running == 1);// tracking buffer overrun + return rli->abort_slave || abort_loop || thd->killed; +} + + +void slave_print_error(RELAY_LOG_INFO* rli, int err_code, const char* msg, ...) +{ + va_list args; + va_start(args,msg); + my_vsnprintf(rli->last_slave_error, + sizeof(rli->last_slave_error), msg, args); + sql_print_error("Slave: %s, error_code=%d", rli->last_slave_error, + err_code); + rli->last_slave_errno = err_code; +} + + +void skip_load_data_infile(NET* net) { (void)my_net_write(net, "\xfb/dev/null", 10); (void)net_flush(net); - (void)my_net_read(net); // discard response - send_ok(net); // the master expects it + (void)my_net_read(net); // discard response + send_ok(net); // the master expects it } -inline char* rewrite_db(char* db) + +char* rewrite_db(char* db) { - if(replicate_rewrite_db.is_empty() || !db) return db; + if (replicate_rewrite_db.is_empty() || !db) + return db; I_List_iterator<i_string_pair> it(replicate_rewrite_db); i_string_pair* tmp; - while((tmp=it++)) - { - if(!strcmp(tmp->key, db)) - return tmp->val; - } - + while ((tmp=it++)) + { + if (!strcmp(tmp->key, db)) + return tmp->val; + } return db; } + +/* + Checks whether a db matches some do_db and ignore_db rules + (for logging or replication) + + SYNOPSIS + db_ok() + db name of the db to check + do_list either binlog_do_db or replicate_do_db + ignore_list either binlog_ignore_db or replicate_ignore_db + + RETURN VALUES + 0 should not be logged/replicated + 1 should be logged/replicated +*/ + int db_ok(const char* db, I_List<i_string> &do_list, I_List<i_string> &ignore_list ) { - if(do_list.is_empty() && ignore_list.is_empty()) + if (do_list.is_empty() && ignore_list.is_empty()) return 1; // ok to replicate if the user puts no constraints - // if the user has specified restrictions on which databases to replicate - // and db was not selected, do not replicate - if(!db) + /* + If the user has specified restrictions on which databases to replicate + and db was not selected, do not replicate. + */ + if (!db) return 0; - if(!do_list.is_empty()) // if the do's are not empty - { - I_List_iterator<i_string> it(do_list); - i_string* tmp; + if (!do_list.is_empty()) // if the do's are not empty + { + I_List_iterator<i_string> it(do_list); + i_string* tmp; - while((tmp=it++)) - { - if(!strcmp(tmp->ptr, db)) - return 1; // match - } - return 0; + while ((tmp=it++)) + { + if (!strcmp(tmp->ptr, db)) + return 1; // match } + return 0; + } else // there are some elements in the don't, otherwise we cannot get here - { - I_List_iterator<i_string> it(ignore_list); - i_string* tmp; + { + I_List_iterator<i_string> it(ignore_list); + i_string* tmp; - while((tmp=it++)) - { - if(!strcmp(tmp->ptr, db)) - return 0; // match - } - - return 1; + while ((tmp=it++)) + { + if (!strcmp(tmp->ptr, db)) + return 0; // match } + return 1; + } } -static int init_strvar_from_file(char* var, int max_size, IO_CACHE* f, - char* default_val) + +static int init_strvar_from_file(char *var, int max_size, IO_CACHE *f, + const char *default_val) { uint length; if ((length=my_b_gets(f,var, max_size))) @@ -321,10 +922,12 @@ static int init_strvar_from_file(char* var, int max_size, IO_CACHE* f, *last_p = 0; // if we stopped on newline, kill it else { - // if we truncated a line or stopped on last char, remove all chars - // up to and including newline + /* + If we truncated a line or stopped on last char, remove all chars + up to and including newline. + */ int c; - while( ((c=my_b_get(f)) != '\n' && c != my_b_EOF)); + while (((c=my_b_get(f)) != '\n' && c != my_b_EOF)); } return 0; } @@ -336,6 +939,7 @@ static int init_strvar_from_file(char* var, int max_size, IO_CACHE* f, return 1; } + static int init_intvar_from_file(int* var, IO_CACHE* f, int default_val) { char buf[32]; @@ -345,7 +949,7 @@ static int init_intvar_from_file(int* var, IO_CACHE* f, int default_val) *var = atoi(buf); return 0; } - else if(default_val) + else if (default_val) { *var = default_val; return 0; @@ -354,16 +958,43 @@ static int init_intvar_from_file(int* var, IO_CACHE* f, int default_val) } +static int check_master_version(MYSQL* mysql, MASTER_INFO* mi) +{ + const char* errmsg= 0; + + switch (*mysql->server_version) { + case '3': + mi->old_format = 1; + break; + case '4': + case '5': + mi->old_format = 0; + break; + default: + errmsg = "Master reported unrecognized MySQL version"; + break; + } + + if (errmsg) + { + sql_print_error(errmsg); + return 1; + } + return 0; +} + + static int create_table_from_dump(THD* thd, NET* net, const char* db, const char* table_name) { - uint packet_len = my_net_read(net); // read create table statement + ulong packet_len = my_net_read(net); // read create table statement + char *query; Vio* save_vio; HA_CHECK_OPT check_opt; TABLE_LIST tables; int error= 1; handler *file; - char *query; + ulong save_options; if (packet_len == packet_error) { @@ -397,12 +1028,17 @@ static int create_table_from_dump(THD* thd, NET* net, const char* db, thd->current_tablenr = 0; thd->query_error = 0; thd->net.no_send_ok = 1; + + /* we do not want to log create table statement */ + save_options = thd->options; + thd->options &= ~(ulong) (OPTION_BIN_LOG); thd->proc_info = "Creating table from master dump"; // save old db in case we are creating in a different database char* save_db = thd->db; - thd->db = thd->last_nx_db; + thd->db = (char*)db; mysql_parse(thd, thd->query, packet_len); // run create table thd->db = save_db; // leave things the way the were before + thd->options = save_options; if (thd->query_error) goto err; // mysql_parse took care of the error send @@ -430,12 +1066,13 @@ static int create_table_from_dump(THD* thd, NET* net, const char* db, } check_opt.init(); - check_opt.flags|= T_VERY_SILENT | T_CALC_CHECKSUM; - check_opt.quick = 1; + check_opt.flags|= T_VERY_SILENT | T_CALC_CHECKSUM | T_QUICK; thd->proc_info = "Rebuilding the index on master dump table"; - // we do not want repair() to spam us with messages - // just send them to the error log, and report the failure in case of - // problems + /* + We do not want repair() to spam us with messages + just send them to the error log, and report the failure in case of + problems. + */ save_vio = thd->net.vio; thd->net.vio = 0; error=file->repair(thd,&check_opt) != 0; @@ -449,100 +1086,310 @@ err: return error; } -int fetch_nx_table(THD* thd, MASTER_INFO* mi) +int fetch_master_table(THD *thd, const char *db_name, const char *table_name, + MASTER_INFO *mi, MYSQL *mysql) { - MYSQL* mysql = mc_mysql_init(NULL); - int error = 1; - int nx_errno = 0; - if (!mysql) + int error= 1; + const char *errmsg=0; + bool called_connected= (mysql != NULL); + DBUG_ENTER("fetch_master_table"); + DBUG_PRINT("enter", ("db_name: '%s' table_name: '%s'", + db_name,table_name)); + + if (!called_connected) + { + if (!(mysql = mc_mysql_init(NULL))) + { + send_error(&thd->net); // EOM + DBUG_RETURN(1); + } + if (connect_to_master(thd, mysql, mi)) + { + net_printf(&thd->net, ER_CONNECT_TO_MASTER, mc_mysql_error(mysql)); + mc_mysql_close(mysql); + DBUG_RETURN(1); + } + if (thd->killed) + goto err; + } + + if (request_table_dump(mysql, db_name, table_name)) { - sql_print_error("fetch_nx_table: Error in mysql_init()"); - nx_errno = ER_GET_ERRNO; + error= ER_UNKNOWN_ERROR; + errmsg= "Failed on table dump request"; goto err; } + if (create_table_from_dump(thd, &mysql->net, db_name, + table_name)) + goto err; // create_table_from_dump will have sent the error already + error = 0; - if (!mi->host || !*mi->host) + err: + thd->net.no_send_ok = 0; // Clear up garbage after create_table_from_dump + if (!called_connected) + mc_mysql_close(mysql); + if (errmsg && thd->net.vio) + send_error(&thd->net, error, errmsg); + DBUG_RETURN(test(error)); // Return 1 on error +} + + +void end_master_info(MASTER_INFO* mi) +{ + DBUG_ENTER("end_master_info"); + + if (!mi->inited) + DBUG_VOID_RETURN; + end_relay_log_info(&mi->rli); + if (mi->fd >= 0) { - nx_errno = ER_BAD_HOST_ERROR; - goto err; + end_io_cache(&mi->file); + (void)my_close(mi->fd, MYF(MY_WME)); + mi->fd = -1; } + mi->inited = 0; + + DBUG_VOID_RETURN; +} - safe_connect(thd, mysql, mi); - if (slave_killed(thd)) - goto err; - if (request_table_dump(mysql, thd->last_nx_db, thd->last_nx_table)) +int init_relay_log_info(RELAY_LOG_INFO* rli, const char* info_fname) +{ + char fname[FN_REFLEN+128]; + int info_fd; + const char* msg = 0; + int error = 0; + DBUG_ENTER("init_relay_log_info"); + + if (rli->inited) // Set if this function called + DBUG_RETURN(0); + fn_format(fname, info_fname, mysql_data_home, "", 4+32); + pthread_mutex_lock(&rli->data_lock); + info_fd = rli->info_fd; + rli->pending = 0; + rli->cur_log_fd = -1; + rli->slave_skip_counter=0; + rli->abort_pos_wait=0; + rli->skip_log_purge=0; + rli->log_space_limit = relay_log_space_limit; + rli->log_space_total = 0; + + // TODO: make this work with multi-master + if (!opt_relay_logname) { - nx_errno = ER_GET_ERRNO; - sql_print_error("fetch_nx_table: failed on table dump request "); - goto err; + char tmp[FN_REFLEN]; + /* + TODO: The following should be using fn_format(); We just need to + first change fn_format() to cut the file name if it's too long. + */ + strmake(tmp,glob_hostname,FN_REFLEN-5); + strmov(strcend(tmp,'.'),"-relay-bin"); + opt_relay_logname=my_strdup(tmp,MYF(MY_WME)); } + if (open_log(&rli->relay_log, glob_hostname, opt_relay_logname, + "-relay-bin", opt_relaylog_index_name, + LOG_BIN, 1 /* read_append cache */, + 1 /* no auto events */)) + DBUG_RETURN(1); + + /* if file does not exist */ + if (access(fname,F_OK)) + { + /* + If someone removed the file from underneath our feet, just close + the old descriptor and re-create the old file + */ + if (info_fd >= 0) + my_close(info_fd, MYF(MY_WME)); + if ((info_fd = my_open(fname, O_CREAT|O_RDWR|O_BINARY, MYF(MY_WME))) < 0 || + init_io_cache(&rli->info_file, info_fd, IO_SIZE*2, READ_CACHE, 0L,0, + MYF(MY_WME))) + { + msg= current_thd->net.last_error; + goto err; + } - if (create_table_from_dump(thd, &mysql->net, thd->last_nx_db, - thd->last_nx_table)) + /* Init relay log with first entry in the relay index file */ + if (init_relay_log_pos(rli,NullS,BIN_LOG_HEADER_SIZE,0 /* no data lock */, + &msg)) + goto err; + rli->master_log_name[0]= 0; + rli->master_log_pos= 0; + rli->info_fd= info_fd; + } + else // file exists + { + if (info_fd >= 0) + reinit_io_cache(&rli->info_file, READ_CACHE, 0L,0,0); + else if ((info_fd = my_open(fname, O_RDWR|O_BINARY, MYF(MY_WME))) < 0 || + init_io_cache(&rli->info_file, info_fd, + IO_SIZE*2, READ_CACHE, 0L, 0, MYF(MY_WME))) + { + if (info_fd >= 0) + my_close(info_fd, MYF(0)); + rli->info_fd= -1; + rli->relay_log.close(1); + pthread_mutex_unlock(&rli->data_lock); + DBUG_RETURN(1); + } + + rli->info_fd = info_fd; + int relay_log_pos, master_log_pos; + if (init_strvar_from_file(rli->relay_log_name, + sizeof(rli->relay_log_name), &rli->info_file, + "") || + init_intvar_from_file(&relay_log_pos, + &rli->info_file, BIN_LOG_HEADER_SIZE) || + init_strvar_from_file(rli->master_log_name, + sizeof(rli->master_log_name), &rli->info_file, + "") || + init_intvar_from_file(&master_log_pos, &rli->info_file, 0)) + { + msg="Error reading slave log configuration"; + goto err; + } + rli->relay_log_pos= relay_log_pos; + rli->master_log_pos= master_log_pos; + + if (init_relay_log_pos(rli, + rli->relay_log_name, + rli->relay_log_pos, + 0 /* no data lock*/, + &msg)) + goto err; + } + DBUG_ASSERT(rli->relay_log_pos >= BIN_LOG_HEADER_SIZE); + DBUG_ASSERT(my_b_tell(rli->cur_log) == rli->relay_log_pos); + /* + Now change the cache from READ to WRITE - must do this + before flush_relay_log_info + */ + reinit_io_cache(&rli->info_file, WRITE_CACHE,0L,0,1); + error= flush_relay_log_info(rli); + if (count_relay_log_space(rli)) { - // create_table_from_dump will have sent the error alread - sql_print_error("fetch_nx_table: failed on create table "); + msg="Error counting relay log space"; goto err; } - - error = 0; + rli->inited= 1; + pthread_mutex_unlock(&rli->data_lock); + DBUG_RETURN(error); - err: - if (mysql) - mc_mysql_close(mysql); - if (nx_errno && thd->net.vio) - send_error(&thd->net, nx_errno, "Error in fetch_nx_table"); - thd->net.no_send_ok = 0; // Clear up garbage after create_table_from_dump - return error; +err: + sql_print_error(msg); + end_io_cache(&rli->info_file); + if (info_fd >= 0) + my_close(info_fd, MYF(0)); + rli->info_fd= -1; + rli->relay_log.close(1); + pthread_mutex_unlock(&rli->data_lock); + DBUG_RETURN(1); } -void end_master_info(MASTER_INFO* mi) + +static inline int add_relay_log(RELAY_LOG_INFO* rli,LOG_INFO* linfo) { - if(mi->fd >= 0) - { - end_io_cache(&mi->file); - (void)my_close(mi->fd, MYF(MY_WME)); - mi->fd = -1; - } - mi->inited = 0; + MY_STAT s; + DBUG_ENTER("add_relay_log"); + if (!my_stat(linfo->log_file_name,&s,MYF(0))) + { + sql_print_error("log %s listed in the index, but failed to stat", + linfo->log_file_name); + DBUG_RETURN(1); + } + rli->log_space_total += s.st_size; +#ifndef DBUG_OFF + char buf[22]; + DBUG_PRINT("info",("log_space_total: %s", llstr(rli->log_space_total,buf))); +#endif + DBUG_RETURN(0); } -int init_master_info(MASTER_INFO* mi) + +static bool wait_for_relay_log_space(RELAY_LOG_INFO* rli) { - if (mi->inited) - return 0; - int fd,length,error; - MY_STAT stat_area; + bool slave_killed=0; + MASTER_INFO* mi = rli->mi; + const char* save_proc_info; + THD* thd = mi->io_thd; + + DBUG_ENTER("wait_for_relay_log_space"); + pthread_mutex_lock(&rli->log_space_lock); + save_proc_info = thd->proc_info; + thd->proc_info = "Waiting for relay log space to free"; + while (rli->log_space_limit < rli->log_space_total && + !(slave_killed=io_slave_killed(thd,mi))) + { + pthread_cond_wait(&rli->log_space_cond, &rli->log_space_lock); + } + thd->proc_info = save_proc_info; + pthread_mutex_unlock(&rli->log_space_lock); + DBUG_RETURN(slave_killed); +} + + +static int count_relay_log_space(RELAY_LOG_INFO* rli) +{ + LOG_INFO linfo; + DBUG_ENTER("count_relay_log_space"); + rli->log_space_total = 0; + if (rli->relay_log.find_log_pos(&linfo, NullS, 1)) + { + sql_print_error("Could not find first log while counting relay log space"); + DBUG_RETURN(1); + } + do + { + if (add_relay_log(rli,&linfo)) + DBUG_RETURN(1); + } while (!rli->relay_log.find_next_log(&linfo, 1)); + DBUG_RETURN(0); +} + + +int init_master_info(MASTER_INFO* mi, const char* master_info_fname, + const char* slave_info_fname, + bool abort_if_no_master_info_file) +{ + int fd,error; char fname[FN_REFLEN+128]; - const char *msg; - fn_format(fname, master_info_file, mysql_data_home, "", 4+16+32); + DBUG_ENTER("init_master_info"); + + if (mi->inited) + DBUG_RETURN(0); + mi->mysql=0; + mi->file_id=1; + mi->ignore_stop_event=0; + fn_format(fname, master_info_fname, mysql_data_home, "", 4+32); - // we need a mutex while we are changing master info parameters to - // keep other threads from reading bogus info + /* + We need a mutex while we are changing master info parameters to + keep other threads from reading bogus info + */ - pthread_mutex_lock(&mi->lock); - mi->pending = 0; + pthread_mutex_lock(&mi->data_lock); fd = mi->fd; - // we do not want any messages if the file does not exist - if (!my_stat(fname, &stat_area, MYF(0))) + if (access(fname,F_OK)) { - // if someone removed the file from underneath our feet, just close - // the old descriptor and re-create the old file - if (fd >= 0) - my_close(fd, MYF(MY_WME)); - if ((fd = my_open(fname, O_CREAT|O_RDWR|O_BINARY, MYF(MY_WME))) < 0 - || init_io_cache(&mi->file, fd, IO_SIZE*2, READ_CACHE, 0L,0, - MYF(MY_WME))) + if (abort_if_no_master_info_file) { - if(fd >= 0) - my_close(fd, MYF(0)); - pthread_mutex_unlock(&mi->lock); - return 1; + pthread_mutex_unlock(&mi->data_lock); + DBUG_RETURN(0); } - mi->log_file_name[0] = 0; - mi->pos = 4; // skip magic number + /* + if someone removed the file from underneath our feet, just close + the old descriptor and re-create the old file + */ + if (fd >= 0) + my_close(fd, MYF(MY_WME)); + if ((fd = my_open(fname, O_CREAT|O_RDWR|O_BINARY, MYF(MY_WME))) < 0 || + init_io_cache(&mi->file, fd, IO_SIZE*2, READ_CACHE, 0L,0, + MYF(MY_WME))) + goto err; + + mi->master_log_name[0] = 0; + mi->master_log_pos = BIN_LOG_HEADER_SIZE; // skip magic number mi->fd = fd; if (master_host) @@ -556,240 +1403,463 @@ int init_master_info(MASTER_INFO* mi) } else // file exists { - if(fd >= 0) + if (fd >= 0) reinit_io_cache(&mi->file, READ_CACHE, 0L,0,0); - else if((fd = my_open(fname, O_RDWR|O_BINARY, MYF(MY_WME))) < 0 - || init_io_cache(&mi->file, fd, IO_SIZE*2, READ_CACHE, 0L, - 0, MYF(MY_WME))) - { - if(fd >= 0) - my_close(fd, MYF(0)); - pthread_mutex_unlock(&mi->lock); - return 1; - } - - if ((length=my_b_gets(&mi->file, mi->log_file_name, - sizeof(mi->log_file_name))) < 1) - { - msg="Error reading log file name from master info file "; - goto error; - } - - mi->log_file_name[length-1]= 0; // kill \n - /* Reuse fname buffer */ - if(!my_b_gets(&mi->file, fname, sizeof(fname))) - { - msg="Error reading log file position from master info file"; - goto error; - } - mi->pos = strtoull(fname,(char**) 0, 10); + else if ((fd = my_open(fname, O_RDWR|O_BINARY, MYF(MY_WME))) < 0 || + init_io_cache(&mi->file, fd, IO_SIZE*2, READ_CACHE, 0L, + 0, MYF(MY_WME))) + goto err; mi->fd = fd; - if(init_strvar_from_file(mi->host, sizeof(mi->host), &mi->file, - master_host) || - init_strvar_from_file(mi->user, sizeof(mi->user), &mi->file, - master_user) || - init_strvar_from_file(mi->password, HASH_PASSWORD_LENGTH+1, &mi->file, - master_password) || - init_intvar_from_file((int*)&mi->port, &mi->file, master_port) || - init_intvar_from_file((int*)&mi->connect_retry, &mi->file, - master_connect_retry)) + int port, connect_retry, master_log_pos; + + if (init_strvar_from_file(mi->master_log_name, + sizeof(mi->master_log_name), &mi->file, + "") || + init_intvar_from_file(&master_log_pos, &mi->file, 4) || + init_strvar_from_file(mi->host, sizeof(mi->host), &mi->file, + master_host) || + init_strvar_from_file(mi->user, sizeof(mi->user), &mi->file, + master_user) || + init_strvar_from_file(mi->password, HASH_PASSWORD_LENGTH+1, &mi->file, + master_password) || + init_intvar_from_file(&port, &mi->file, master_port) || + init_intvar_from_file(&connect_retry, &mi->file, + master_connect_retry)) { - msg="Error reading master configuration"; - goto error; + sql_print_error("Error reading master configuration"); + goto err; } + /* + This has to be handled here as init_intvar_from_file can't handle + my_off_t types + */ + mi->master_log_pos= (my_off_t) master_log_pos; + mi->port= (uint) port; + mi->connect_retry= (uint) connect_retry; } - + DBUG_PRINT("master_info",("log_file_name: %s position: %ld", + mi->master_log_name, + (ulong) mi->master_log_pos)); + + if (init_relay_log_info(&mi->rli, slave_info_fname)) + goto err; + mi->rli.mi = mi; + mi->inited = 1; - // now change the cache from READ to WRITE - must do this - // before flush_master_info - reinit_io_cache(&mi->file, WRITE_CACHE, 0L,0,1); + // now change cache READ -> WRITE - must do this before flush_master_info + reinit_io_cache(&mi->file, WRITE_CACHE,0L,0,1); error=test(flush_master_info(mi)); - pthread_mutex_unlock(&mi->lock); - return error; + pthread_mutex_unlock(&mi->data_lock); + DBUG_RETURN(error); -error: - sql_print_error(msg); - end_io_cache(&mi->file); - my_close(fd, MYF(0)); - pthread_mutex_unlock(&mi->lock); - return 1; +err: + if (fd >= 0) + { + my_close(fd, MYF(0)); + end_io_cache(&mi->file); + } + mi->fd= -1; + pthread_mutex_unlock(&mi->data_lock); + DBUG_RETURN(1); } -int show_master_info(THD* thd) + +int register_slave_on_master(MYSQL* mysql) { + String packet; + char buf[4]; + + if (!report_host) + return 0; + + int4store(buf, server_id); + packet.append(buf, 4); + + net_store_data(&packet, report_host); + if (report_user) + net_store_data(&packet, report_user); + else + packet.append((char)0); + + if (report_password) + net_store_data(&packet, report_user); + else + packet.append((char)0); + + int2store(buf, (uint16)report_port); + packet.append(buf, 2); + int4store(buf, rpl_recovery_rank); + packet.append(buf, 4); + int4store(buf, 0); /* tell the master will fill in master_id */ + packet.append(buf, 4); + + if (mc_simple_command(mysql, COM_REGISTER_SLAVE, (char*)packet.ptr(), + packet.length(), 0)) + { + sql_print_error("Error on COM_REGISTER_SLAVE: %d '%s'", + mc_mysql_errno(mysql), + mc_mysql_error(mysql)); + return 1; + } + + return 0; +} + +int show_master_info(THD* thd, MASTER_INFO* mi) +{ + // TODO: fix this for multi-master DBUG_ENTER("show_master_info"); List<Item> field_list; field_list.push_back(new Item_empty_string("Master_Host", - sizeof(glob_mi.host))); + sizeof(mi->host))); field_list.push_back(new Item_empty_string("Master_User", - sizeof(glob_mi.user))); + sizeof(mi->user))); field_list.push_back(new Item_empty_string("Master_Port", 6)); field_list.push_back(new Item_empty_string("Connect_retry", 6)); - field_list.push_back( new Item_empty_string("Log_File", + field_list.push_back(new Item_empty_string("Master_Log_File", FN_REFLEN)); - field_list.push_back(new Item_empty_string("Pos", 12)); - field_list.push_back(new Item_empty_string("Slave_Running", 3)); + field_list.push_back(new Item_empty_string("Read_Master_Log_Pos", 12)); + field_list.push_back(new Item_empty_string("Relay_Log_File", + FN_REFLEN)); + field_list.push_back(new Item_empty_string("Relay_Log_Pos", 12)); + field_list.push_back(new Item_empty_string("Relay_Master_Log_File", + FN_REFLEN)); + field_list.push_back(new Item_empty_string("Slave_IO_Running", 3)); + field_list.push_back(new Item_empty_string("Slave_SQL_Running", 3)); field_list.push_back(new Item_empty_string("Replicate_do_db", 20)); field_list.push_back(new Item_empty_string("Replicate_ignore_db", 20)); field_list.push_back(new Item_empty_string("Last_errno", 4)); field_list.push_back(new Item_empty_string("Last_error", 20)); field_list.push_back(new Item_empty_string("Skip_counter", 12)); - if(send_fields(thd, field_list, 1)) + field_list.push_back(new Item_empty_string("Exec_master_log_pos", 12)); + field_list.push_back(new Item_empty_string("Relay_log_space", 12)); + if (send_fields(thd, field_list, 1)) DBUG_RETURN(-1); - String* packet = &thd->packet; - packet->length(0); + if (mi->host[0]) + { + String *packet= &thd->packet; + packet->length(0); - pthread_mutex_lock(&glob_mi.lock); - net_store_data(packet, glob_mi.host); - net_store_data(packet, glob_mi.user); - net_store_data(packet, (uint32) glob_mi.port); - net_store_data(packet, (uint32) glob_mi.connect_retry); - net_store_data(packet, glob_mi.log_file_name); - net_store_data(packet, (uint32) glob_mi.pos); // QQ: Should be fixed - pthread_mutex_unlock(&glob_mi.lock); - pthread_mutex_lock(&LOCK_slave); - net_store_data(packet, slave_running ? "Yes":"No"); - pthread_mutex_unlock(&LOCK_slave); - net_store_data(packet, &replicate_do_db); - net_store_data(packet, &replicate_ignore_db); - net_store_data(packet, (uint32)last_slave_errno); - net_store_data(packet, last_slave_error); - net_store_data(packet, slave_skip_counter); + pthread_mutex_lock(&mi->data_lock); + pthread_mutex_lock(&mi->rli.data_lock); + net_store_data(packet, mi->host); + net_store_data(packet, mi->user); + net_store_data(packet, (uint32) mi->port); + net_store_data(packet, (uint32) mi->connect_retry); + net_store_data(packet, mi->master_log_name); + net_store_data(packet, (longlong) mi->master_log_pos); + net_store_data(packet, mi->rli.relay_log_name + + dirname_length(mi->rli.relay_log_name)); + net_store_data(packet, (longlong) mi->rli.relay_log_pos); + net_store_data(packet, mi->rli.master_log_name); + net_store_data(packet, mi->slave_running ? "Yes":"No"); + net_store_data(packet, mi->rli.slave_running ? "Yes":"No"); + net_store_data(packet, &replicate_do_db); + net_store_data(packet, &replicate_ignore_db); + net_store_data(packet, (uint32)mi->rli.last_slave_errno); + net_store_data(packet, mi->rli.last_slave_error); + net_store_data(packet, mi->rli.slave_skip_counter); + net_store_data(packet, (longlong) mi->rli.master_log_pos); + net_store_data(packet, (longlong) mi->rli.log_space_total); + pthread_mutex_unlock(&mi->rli.data_lock); + pthread_mutex_unlock(&mi->data_lock); - if (my_net_write(&thd->net, (char*)thd->packet.ptr(), packet->length())) - DBUG_RETURN(-1); - + if (my_net_write(&thd->net, (char*)thd->packet.ptr(), packet->length())) + DBUG_RETURN(-1); + } send_eof(&thd->net); DBUG_RETURN(0); } -int flush_master_info(MASTER_INFO* mi) + +bool flush_master_info(MASTER_INFO* mi) { IO_CACHE* file = &mi->file; char lbuf[22]; - + DBUG_ENTER("flush_master_info"); + DBUG_PRINT("enter",("master_pos: %ld", (long) mi->master_log_pos)); + my_b_seek(file, 0L); my_b_printf(file, "%s\n%s\n%s\n%s\n%s\n%d\n%d\n", - mi->log_file_name, llstr(mi->pos, lbuf), mi->host, mi->user, - mi->password, mi->port, mi->connect_retry); + mi->master_log_name, llstr(mi->master_log_pos, lbuf), + mi->host, mi->user, + mi->password, mi->port, mi->connect_retry + ); flush_io_cache(file); - return 0; + DBUG_RETURN(0); +} + + +st_relay_log_info::st_relay_log_info() + :info_fd(-1), cur_log_fd(-1), master_log_pos(0), save_temporary_tables(0), + cur_log_old_open_count(0), log_space_total(0), + slave_skip_counter(0), abort_pos_wait(0), slave_run_id(0), + sql_thd(0), last_slave_errno(0), inited(0), abort_slave(0), + slave_running(0), skip_log_purge(0), + inside_transaction(0) /* the default is autocommit=1 */ +{ + relay_log_name[0] = master_log_name[0] = 0; + last_slave_error[0]=0; + + + bzero(&info_file,sizeof(info_file)); + bzero(&cache_buf, sizeof(cache_buf)); + pthread_mutex_init(&run_lock, MY_MUTEX_INIT_FAST); + pthread_mutex_init(&data_lock, MY_MUTEX_INIT_FAST); + pthread_mutex_init(&log_space_lock, MY_MUTEX_INIT_FAST); + pthread_cond_init(&data_cond, NULL); + pthread_cond_init(&start_cond, NULL); + pthread_cond_init(&stop_cond, NULL); + pthread_cond_init(&log_space_cond, NULL); +} + + +st_relay_log_info::~st_relay_log_info() +{ + pthread_mutex_destroy(&run_lock); + pthread_mutex_destroy(&data_lock); + pthread_mutex_destroy(&log_space_lock); + pthread_cond_destroy(&data_cond); + pthread_cond_destroy(&start_cond); + pthread_cond_destroy(&stop_cond); + pthread_cond_destroy(&log_space_cond); } -int st_master_info::wait_for_pos(THD* thd, String* log_name, ulonglong log_pos) +/* + Waits until the SQL thread reaches (has executed up to) the + log/position or timed out. + + SYNOPSIS + wait_for_pos() + thd client thread that sent SELECT MASTER_POS_WAIT + log_name log name to wait for + log_pos position to wait for + timeout timeout in seconds before giving up waiting + + NOTES + timeout is longlong whereas it should be ulong ; but this is + to catch if the user submitted a negative timeout. + + RETURN VALUES + -2 improper arguments (log_pos<0) + or slave not running, or master info changed + during the function's execution, + or client thread killed. -2 is translated to NULL by caller + -1 timed out + >=0 number of log events the function had to wait + before reaching the desired log/position + */ + +int st_relay_log_info::wait_for_pos(THD* thd, String* log_name, + longlong log_pos, + longlong timeout) { - if (!inited) return -1; - bool pos_reached; + if (!inited) + return -1; int event_count = 0; - pthread_mutex_lock(&lock); - while(!thd->killed) + ulong init_abort_pos_wait; + int error=0; + struct timespec abstime; // for timeout checking + set_timespec(abstime,timeout); + + DBUG_ENTER("wait_for_pos"); + DBUG_PRINT("enter",("master_log_name: '%s' pos: %lu timeout: %ld", + master_log_name, (ulong) master_log_pos, + (long) timeout)); + + pthread_mutex_lock(&data_lock); + /* + This function will abort when it notices that + some CHANGE MASTER or RESET MASTER has changed + the master info. To catch this, these commands + modify abort_pos_wait ; we just monitor abort_pos_wait + and see if it has changed. + */ + init_abort_pos_wait= abort_pos_wait; + + /* + We'll need to + handle all possible log names comparisons (e.g. 999 vs 1000). + We use ulong for string->number conversion ; this is no + stronger limitation than in find_uniq_filename in sql/log.cc + */ + ulong log_name_extension; + char log_name_tmp[FN_REFLEN]; //make a char[] from String + char *end= strmake(log_name_tmp, log_name->ptr(), min(log_name->length(), + FN_REFLEN-1)); + char *p= fn_ext(log_name_tmp); + char *p_end; + if (!*p || log_pos<0) { - int cmp_result; - if (*log_file_name) + error= -2; //means improper arguments + goto err; + } + //p points to '.' + log_name_extension= strtoul(++p, &p_end, 10); + /* + p_end points to the first invalid character. + If it equals to p, no digits were found, error. + If it contains '\0' it means conversion went ok. + */ + if (p_end==p || *p_end) + { + error= -2; + goto err; + } + + //"compare and wait" main loop + while (!thd->killed && + init_abort_pos_wait == abort_pos_wait && + mi->slave_running) + { + bool pos_reached; + int cmp_result= 0; + DBUG_ASSERT(*master_log_name || master_log_pos == 0); + if (*master_log_name) { + char *basename= master_log_name + dirname_length(master_log_name); /* - We should use dirname_length() here when we have a version of - this that doesn't modify the argument */ - char *basename = strrchr(log_file_name, FN_LIBCHAR); - if (basename) - ++basename; + First compare the parts before the extension. + Find the dot in the master's log basename, + and protect against user's input error : + if the names do not match up to '.' included, return error + */ + char *q= (char*)(fn_ext(basename)+1); + if (strncmp(basename, log_name_tmp, (int)(q-basename))) + { + error= -2; + break; + } + // Now compare extensions. + char *q_end; + ulong master_log_name_extension= strtoul(q, &q_end, 10); + if (master_log_name_extension < log_name_extension) + cmp_result = -1 ; else - basename = log_file_name; - cmp_result = strncmp(basename, log_name->ptr(), - log_name->length()); + cmp_result= (master_log_name_extension > log_name_extension) ? 1 : 0 ; } - else - cmp_result = 0; - - pos_reached = ((!cmp_result && pos >= log_pos) || cmp_result > 0); + pos_reached = ((!cmp_result && master_log_pos >= (ulonglong)log_pos) || + cmp_result > 0); if (pos_reached || thd->killed) break; + + //wait for master update, with optional timeout. - const char* msg = thd->enter_cond(&cond, &lock, - "Waiting for master update"); - pthread_cond_wait(&cond, &lock); + DBUG_PRINT("info",("Waiting for master update")); + const char* msg = thd->enter_cond(&data_cond, &data_lock, + "Waiting for master update"); + if (timeout > 0) + { + /* + Note that pthread_cond_timedwait checks for the timeout + before for the condition ; i.e. it returns ETIMEDOUT + if the system time equals or exceeds the time specified by abstime + before the condition variable is signaled or broadcast, _or_ if + the absolute time specified by abstime has already passed at the time + of the call. + For that reason, pthread_cond_timedwait will do the "timeoutting" job + even if its condition is always immediately signaled (case of a loaded + master). + */ + error=pthread_cond_timedwait(&data_cond, &data_lock, &abstime); + } + else + pthread_cond_wait(&data_cond, &data_lock); thd->exit_cond(msg); + if (error == ETIMEDOUT || error == ETIME) + { + error= -1; + break; + } + error=0; event_count++; } - pthread_mutex_unlock(&lock); - return thd->killed ? -1 : event_count; + +err: + pthread_mutex_unlock(&data_lock); + DBUG_PRINT("exit",("killed: %d abort: %d slave_running: %d \ +improper_arguments: %d timed_out: %d", + (int) thd->killed, + (int) (init_abort_pos_wait != abort_pos_wait), + (int) mi->slave_running, + (int) (error == -2), + (int) (error == -1))); + if (thd->killed || init_abort_pos_wait != abort_pos_wait || + !mi->slave_running) + { + error= -2; + } + DBUG_RETURN( error ? error : event_count ); } -static int init_slave_thread(THD* thd) +static int init_slave_thread(THD* thd, SLAVE_THD_TYPE thd_type) { DBUG_ENTER("init_slave_thread"); thd->system_thread = thd->bootstrap = 1; + thd->host_or_ip= ""; thd->client_capabilities = 0; my_net_init(&thd->net, 0); - thd->net.timeout = slave_net_timeout; - thd->max_packet_length=thd->net.max_packet; + thd->net.read_timeout = slave_net_timeout; thd->master_access= ~0; thd->priv_user = 0; thd->slave_thread = 1; thd->options = (((opt_log_slave_updates) ? OPTION_BIN_LOG:0) | OPTION_AUTO_IS_NULL) ; - thd->system_thread = 1; thd->client_capabilities = CLIENT_LOCAL_FILES; - slave_real_id=thd->real_id=pthread_self(); + thd->real_id=pthread_self(); pthread_mutex_lock(&LOCK_thread_count); thd->thread_id = thread_id++; pthread_mutex_unlock(&LOCK_thread_count); - if (init_thr_lock() || - my_pthread_setspecific_ptr(THR_THD, thd) || - my_pthread_setspecific_ptr(THR_MALLOC, &thd->mem_root) || - my_pthread_setspecific_ptr(THR_NET, &thd->net)) + if (init_thr_lock() || thd->store_globals()) { - close_connection(&thd->net,ER_OUT_OF_RESOURCES); // is this needed? end_thread(thd,0); DBUG_RETURN(-1); } - thd->mysys_var=my_thread_var; - thd->dbug_thread_id=my_thread_id(); -#if !defined(__WIN__) && !defined(OS2) +#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__) sigset_t set; VOID(sigemptyset(&set)); // Get mask in use VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals)); #endif - thd->mem_root.free=thd->mem_root.used=0; // Probably not needed - if (thd->max_join_size == (ulong) ~0L) + if (thd->variables.max_join_size == HA_POS_ERROR) thd->options |= OPTION_BIG_SELECTS; - thd->proc_info="Waiting for master update"; + if (thd_type == SLAVE_THD_SQL) + thd->proc_info= "Waiting for the next event in slave queue"; + else + thd->proc_info= "Waiting for master update"; thd->version=refresh_version; thd->set_time(); - DBUG_RETURN(0); } -static int safe_sleep(THD* thd, int sec) + +static int safe_sleep(THD* thd, int sec, CHECK_KILLED_FUNC thread_killed, + void* thread_killed_arg) { + int nap_time; thr_alarm_t alarmed; thr_alarm_init(&alarmed); time_t start_time= time((time_t*) 0); time_t end_time= start_time+sec; - ALARM alarm_buff; - while (start_time < end_time) + while ((nap_time= (int) (end_time - start_time)) > 0) { - int nap_time = (int) (end_time - start_time); + ALARM alarm_buff; /* - the only reason we are asking for alarm is so that + The only reason we are asking for alarm is so that we will be woken up in case of murder, so if we do not get killed, set the alarm so it goes off after we wake up naturally */ - thr_alarm(&alarmed, 2 * nap_time,&alarm_buff); + thr_alarm(&alarmed, 2 * nap_time, &alarm_buff); sleep(nap_time); - // if we wake up before the alarm goes off, hit the button - // so it will not wake up the wife and kids :-) - if (thr_alarm_in_use(&alarmed)) - thr_end_alarm(&alarmed); + thr_end_alarm(&alarmed); - if (slave_killed(thd)) + if ((*thread_killed)(thd,thread_killed_arg)) return 1; start_time=time((time_t*) 0); } @@ -797,41 +1867,52 @@ static int safe_sleep(THD* thd, int sec) } -static int request_dump(MYSQL* mysql, MASTER_INFO* mi) +static int request_dump(MYSQL* mysql, MASTER_INFO* mi, + bool *suppress_warnings) { char buf[FN_REFLEN + 10]; int len; int binlog_flags = 0; // for now - char* logname = mi->log_file_name; - int4store(buf, mi->pos); + char* logname = mi->master_log_name; + DBUG_ENTER("request_dump"); + + // TODO if big log files: Change next to int8store() + int4store(buf, (longlong) mi->master_log_pos); int2store(buf + 4, binlog_flags); int4store(buf + 6, server_id); len = (uint) strlen(logname); memcpy(buf + 10, logname,len); if (mc_simple_command(mysql, COM_BINLOG_DUMP, buf, len + 10, 1)) { - // something went wrong, so we will just reconnect and retry later - // in the future, we should do a better error analysis, but for - // now we just fill up the error log :-) - sql_print_error("Error on COM_BINLOG_DUMP: %s, will retry in %d secs", - mc_mysql_error(mysql), master_connect_retry); - return 1; + /* + Something went wrong, so we will just reconnect and retry later + in the future, we should do a better error analysis, but for + now we just fill up the error log :-) + */ + if (mc_mysql_errno(mysql) == ER_NET_READ_INTERRUPTED) + *suppress_warnings= 1; // Suppress reconnect warning + else + sql_print_error("Error on COM_BINLOG_DUMP: %d %s, will retry in %d secs", + mc_mysql_errno(mysql), mc_mysql_error(mysql), + master_connect_retry); + DBUG_RETURN(1); } - return 0; + DBUG_RETURN(0); } -static int request_table_dump(MYSQL* mysql, char* db, char* table) + +static int request_table_dump(MYSQL* mysql, const char* db, const char* table) { char buf[1024]; char * p = buf; uint table_len = (uint) strlen(table); uint db_len = (uint) strlen(db); - if(table_len + db_len > sizeof(buf) - 2) - { - sql_print_error("request_table_dump: Buffer overrun"); - return 1; - } + if (table_len + db_len > sizeof(buf) - 2) + { + sql_print_error("request_table_dump: Buffer overrun"); + return 1; + } *p++ = db_len; memcpy(p, db, db_len); @@ -851,26 +1932,40 @@ command"); /* - We set suppress_warnings TRUE when a normal net read timeout has - caused us to try a reconnect. We do not want to print anything to - the error log in this case because this a anormal event in an idle - server. + read one event from the master + + SYNOPSIS + read_event() + mysql MySQL connection + mi Master connection information + suppress_warnings TRUE when a normal net read timeout has caused us to + try a reconnect. We do not want to print anything to + the error log in this case because this a anormal + event in an idle server. + + RETURN VALUES + 'packet_error' Error + number Length of packet + */ -static uint read_event(MYSQL* mysql, MASTER_INFO *mi, bool* suppress_warnings) +static ulong read_event(MYSQL* mysql, MASTER_INFO *mi, bool* suppress_warnings) { - uint len = packet_error; + ulong len; + + *suppress_warnings= 0; + /* + my_real_read() will time us out + We check if we were told to die, and if not, try reading again - // my_real_read() will time us out - // we check if we were told to die, and if not, try reading again + TODO: Move 'events_till_disconnect' to the MASTER_INFO structure + */ #ifndef DBUG_OFF if (disconnect_slave_event_count && !(events_till_disconnect--)) return packet_error; #endif - *suppress_warnings= 0; - + len = mc_net_safe_read(mysql); - if (len == packet_error || (long) len < 1) { if (mc_mysql_errno(mysql) == ER_NET_READ_INTERRUPTED) @@ -902,358 +1997,82 @@ server_errno=%d)", return len - 1; } -static int check_expected_error(THD* thd, int expected_error) + +int check_expected_error(THD* thd, RELAY_LOG_INFO* rli, int expected_error) { - switch(expected_error) - { - case ER_NET_READ_ERROR: - case ER_NET_ERROR_ON_WRITE: - case ER_SERVER_SHUTDOWN: - case ER_NEW_ABORTING_CONNECTION: - my_snprintf(last_slave_error, sizeof(last_slave_error), - "Slave: query '%s' partially completed on the master \ + switch (expected_error) { + case ER_NET_READ_ERROR: + case ER_NET_ERROR_ON_WRITE: + case ER_SERVER_SHUTDOWN: + case ER_NEW_ABORTING_CONNECTION: + my_snprintf(rli->last_slave_error, sizeof(rli->last_slave_error), + "Slave: query '%s' partially completed on the master \ and was aborted. There is a chance that your master is inconsistent at this \ point. If you are sure that your master is ok, run this query manually on the\ - slave and then restart the slave with SET SQL_SLAVE_SKIP_COUNTER=1;\ + slave and then restart the slave with SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1;\ SLAVE START;", thd->query); - last_slave_errno = expected_error; - sql_print_error("%s",last_slave_error); - return 1; - default: - return 0; - } + rli->last_slave_errno = expected_error; + sql_print_error("%s",rli->last_slave_error); + return 1; + default: + return 0; + } } -inline int ignored_error_code(int err_code) -{ - return use_slave_mask && bitmap_is_set(&slave_error_mask, err_code); -} -static int exec_event(THD* thd, NET* net, MASTER_INFO* mi, int event_len) +static int exec_relay_log_event(THD* thd, RELAY_LOG_INFO* rli) { - Log_event * ev = Log_event::read_log_event((const char*)net->read_pos + 1, - event_len); - char llbuff[22]; - - mi->event_len = event_len; /* Added by Heikki: InnoDB internally stores the - master log position it has processed so far; - position to store is really - mi->pos + mi->pending + mi->event_len - since we must store the pos of the END of the - current log event */ + DBUG_ASSERT(rli->sql_thd==thd); + Log_event * ev = next_event(rli); + DBUG_ASSERT(rli->sql_thd==thd); + if (sql_slave_killed(thd,rli)) + return 1; if (ev) { int type_code = ev->get_type_code(); - if (ev->server_id == ::server_id || slave_skip_counter) + int exec_res; + pthread_mutex_lock(&rli->data_lock); + + /* + Skip queries originating from this server or number of + queries specified by the user in slave_skip_counter + We can't however skip event's that has something to do with the + log files themselves. + */ + + if (ev->server_id == (uint32) ::server_id || + (rli->slave_skip_counter && type_code != ROTATE_EVENT)) { - if(type_code == LOAD_EVENT) - skip_load_data_infile(net); - - mi->inc_pos(event_len); - flush_master_info(mi); - if(slave_skip_counter && /* protect against common user error of - setting the counter to 1 instead of 2 - while recovering from an failed - auto-increment insert */ - !(type_code == INTVAR_EVENT && - slave_skip_counter == 1)) - --slave_skip_counter; + /* TODO: I/O thread should not even log events with the same server id */ + rli->inc_pos(ev->get_event_len(), + type_code != STOP_EVENT ? ev->log_pos : LL(0), + 1/* skip lock*/); + flush_relay_log_info(rli); + + /* + Protect against common user error of setting the counter to 1 + instead of 2 while recovering from an failed auto-increment insert + */ + if (rli->slave_skip_counter && + !((type_code == INTVAR_EVENT || type_code == STOP_EVENT) && + rli->slave_skip_counter == 1)) + --rli->slave_skip_counter; + pthread_mutex_unlock(&rli->data_lock); delete ev; return 0; // avoid infinite update loops } + pthread_mutex_unlock(&rli->data_lock); thd->server_id = ev->server_id; // use the original server id for logging thd->set_time(); // time the query - if(!ev->when) + if (!ev->when) ev->when = time(NULL); - - switch(type_code) { - case QUERY_EVENT: - { - Query_log_event* qev = (Query_log_event*)ev; - int q_len = qev->q_len; - int expected_error,actual_error = 0; - init_sql_alloc(&thd->mem_root, 8192,0); - thd->db = rewrite_db((char*)qev->db); - if (db_ok(thd->db, replicate_do_db, replicate_ignore_db)) - { - thd->query_length= q_len; - thd->set_time((time_t)qev->when); - thd->current_tablenr = 0; - VOID(pthread_mutex_lock(&LOCK_thread_count)); - thd->query = (char*)qev->query; - thd->query_id = query_id++; - VOID(pthread_mutex_unlock(&LOCK_thread_count)); - thd->last_nx_table = thd->last_nx_db = 0; - thd->query_error = 0; // clear error - thd->net.last_errno = 0; - thd->net.last_error[0] = 0; - thd->slave_proxy_id = qev->thread_id; // for temp tables - - // sanity check to make sure the master did not get a really bad - // error on the query - if (ignored_error_code((expected_error=qev->error_code)) || - !check_expected_error(thd, expected_error)) - { - mysql_parse(thd, thd->query, q_len); - if (expected_error != - (actual_error = thd->net.last_errno) && expected_error && - !ignored_error_code(actual_error)) - { - const char* errmsg = "Slave: did not get the expected error\ - running query from master - expected: '%s' (%d), got '%s' (%d)"; - sql_print_error(errmsg, ER_SAFE(expected_error), - expected_error, - actual_error ? thd->net.last_error:"no error", - actual_error); - thd->query_error = 1; - } - else if (expected_error == actual_error || - ignored_error_code(actual_error)) - { - thd->query_error = 0; - *last_slave_error = 0; - last_slave_errno = 0; - } - } - else - { - // master could be inconsistent, abort and tell DBA to check/fix it - VOID(pthread_mutex_lock(&LOCK_thread_count)); - thd->db = thd->query = 0; - VOID(pthread_mutex_unlock(&LOCK_thread_count)); - thd->convert_set = 0; - close_thread_tables(thd); - free_root(&thd->mem_root,0); - delete ev; - return 1; - } - } - thd->db = 0; // prevent db from being freed - VOID(pthread_mutex_lock(&LOCK_thread_count)); - thd->query = 0; // just to be sure - VOID(pthread_mutex_unlock(&LOCK_thread_count)); - // assume no convert for next query unless set explictly - thd->convert_set = 0; - close_thread_tables(thd); - - if (thd->query_error || thd->fatal_error) - { - sql_print_error("Slave: error running query '%s' ", - qev->query); - last_slave_errno = actual_error ? actual_error : -1; - my_snprintf(last_slave_error, sizeof(last_slave_error), - "error '%s' on query '%s'", - actual_error ? thd->net.last_error : - "unexpected success or fatal error", - qev->query - ); - free_root(&thd->mem_root,0); - delete ev; - return 1; - } - free_root(&thd->mem_root,0); - delete ev; - - mi->inc_pos(event_len); - - if (!(thd->options & OPTION_BEGIN)) { - - /* We only flush the master info position to the master.info file if - the transaction is not open any more: an incomplete transaction will - be rolled back automatically in crash recovery in transactional - table handlers */ - - flush_master_info(mi); - } - break; - } - - case LOAD_EVENT: - { - Load_log_event* lev = (Load_log_event*)ev; - init_sql_alloc(&thd->mem_root, 8192,0); - thd->db = rewrite_db((char*)lev->db); - DBUG_ASSERT(thd->query == 0); - thd->query = 0; - thd->query_error = 0; - - if (db_ok(thd->db, replicate_do_db, replicate_ignore_db)) - { - thd->set_time((time_t)lev->when); - thd->current_tablenr = 0; - VOID(pthread_mutex_lock(&LOCK_thread_count)); - thd->query_id = query_id++; - VOID(pthread_mutex_unlock(&LOCK_thread_count)); - - TABLE_LIST tables; - bzero((char*) &tables,sizeof(tables)); - tables.db = thd->db; - tables.alias= tables.real_name= (char*)lev->table_name; - tables.lock_type = TL_WRITE; - tables.updating= 1; - // the table will be opened in mysql_load - if(table_rules_on && !tables_ok(thd, &tables)) - { - skip_load_data_infile(net); - } - else - { - enum enum_duplicates handle_dup = DUP_IGNORE; - if(lev->sql_ex.opt_flags && REPLACE_FLAG) - handle_dup = DUP_REPLACE; - sql_exchange ex((char*)lev->fname, lev->sql_ex.opt_flags && - DUMPFILE_FLAG ); - String field_term(&lev->sql_ex.field_term, 1), - enclosed(&lev->sql_ex.enclosed, 1), - line_term(&lev->sql_ex.line_term,1), - escaped(&lev->sql_ex.escaped, 1), - line_start(&lev->sql_ex.line_start, 1); - - ex.field_term = &field_term; - if(lev->sql_ex.empty_flags & FIELD_TERM_EMPTY) - ex.field_term->length(0); - - ex.enclosed = &enclosed; - if(lev->sql_ex.empty_flags & ENCLOSED_EMPTY) - ex.enclosed->length(0); - - ex.line_term = &line_term; - if(lev->sql_ex.empty_flags & LINE_TERM_EMPTY) - ex.line_term->length(0); - - ex.line_start = &line_start; - if(lev->sql_ex.empty_flags & LINE_START_EMPTY) - ex.line_start->length(0); - - ex.escaped = &escaped; - if(lev->sql_ex.empty_flags & ESCAPED_EMPTY) - ex.escaped->length(0); - - ex.opt_enclosed = (lev->sql_ex.opt_flags & OPT_ENCLOSED_FLAG); - if(lev->sql_ex.empty_flags & FIELD_TERM_EMPTY) - ex.field_term->length(0); - - ex.skip_lines = lev->skip_lines; - - - List<Item> fields; - lev->set_fields(fields); - thd->slave_proxy_id = thd->thread_id; - thd->net.vio = net->vio; - // mysql_load will use thd->net to read the file - thd->net.pkt_nr = net->pkt_nr; - // make sure the client does not get confused - // about the packet sequence - if(mysql_load(thd, &ex, &tables, fields, handle_dup, 1, - TL_WRITE)) - thd->query_error = 1; - if(thd->cuted_fields) - sql_print_error("Slave: load data infile at position %s in log \ -'%s' produced %d warning(s)", llstr(glob_mi.pos,llbuff), RPL_LOG_NAME, - thd->cuted_fields ); - net->pkt_nr = thd->net.pkt_nr; - } - } - else - { - // we will just ask the master to send us /dev/null if we do not - // want to load the data :-) - skip_load_data_infile(net); - } - - thd->net.vio = 0; - thd->db = 0;// prevent db from being freed - close_thread_tables(thd); - if(thd->query_error) - { - int sql_error = thd->net.last_errno; - if(!sql_error) - sql_error = ER_UNKNOWN_ERROR; - - sql_print_error("Slave: Error '%s' running load data infile ", - ER(sql_error)); - delete ev; - free_root(&thd->mem_root,0); - return 1; - } - - delete ev; - free_root(&thd->mem_root,0); - - if(thd->fatal_error) - { - sql_print_error("Slave: Fatal error running query '%s' ", - thd->query); - return 1; - } - - mi->inc_pos(event_len); - - if (!(thd->options & OPTION_BEGIN)) - flush_master_info(mi); - - break; - } - - /* Question: in a START or STOP event, what happens if we have transaction - open? */ - - case START_EVENT: - mi->inc_pos(event_len); - flush_master_info(mi); - delete ev; - break; - - case STOP_EVENT: - if(mi->pos > 4) // stop event should be ignored after rotate event - { - close_temporary_tables(thd); - mi->inc_pos(event_len); - flush_master_info(mi); - } - delete ev; - break; - case ROTATE_EVENT: - { - Rotate_log_event* rev = (Rotate_log_event*)ev; - int ident_len = rev->ident_len; - pthread_mutex_lock(&mi->lock); - memcpy(mi->log_file_name, rev->new_log_ident,ident_len ); - mi->log_file_name[ident_len] = 0; - mi->pos = 4; // skip magic number - pthread_cond_broadcast(&mi->cond); - pthread_mutex_unlock(&mi->lock); - - if (!(thd->options & OPTION_BEGIN)) - flush_master_info(mi); -#ifndef DBUG_OFF - if(abort_slave_event_count) - ++events_till_abort; -#endif - delete ev; - break; - } - - case INTVAR_EVENT: - { - Intvar_log_event* iev = (Intvar_log_event*)ev; - switch(iev->type) - { - case LAST_INSERT_ID_EVENT: - thd->last_insert_id_used = 1; - thd->last_insert_id = iev->val; - break; - case INSERT_ID_EVENT: - thd->next_insert_id = iev->val; - break; - - } - mi->inc_pending(event_len); - delete ev; - break; - } - } + ev->thd = thd; + thd->log_pos = ev->log_pos; + exec_res = ev->exec_event(rli); + DBUG_ASSERT(rli->sql_thd==thd); + delete ev; + return exec_res; } else { @@ -1263,364 +2082,1127 @@ This may also be a network problem, or just a bug in the master or slave code.\ "); return 1; } - return 0; } - -// slave thread -pthread_handler_decl(handle_slave,arg __attribute__((unused))) + +/* slave I/O thread */ +extern "C" pthread_handler_decl(handle_slave_io,arg) { -#ifndef DBUG_OFF - slave_begin: -#endif THD *thd; // needs to be first for thread_stack - MYSQL *mysql = NULL ; + MYSQL *mysql; + MASTER_INFO *mi = (MASTER_INFO*)arg; char llbuff[22]; - - pthread_mutex_lock(&LOCK_slave); - if (!server_id) - { - pthread_cond_broadcast(&COND_slave_start); - pthread_mutex_unlock(&LOCK_slave); - sql_print_error("Server id not set, will not start slave"); - pthread_exit((void*)1); - } + uint retry_count; - if(slave_running) - { - pthread_cond_broadcast(&COND_slave_start); - pthread_mutex_unlock(&LOCK_slave); - pthread_exit((void*)1); // safety just in case - } - slave_running = 1; - abort_slave = 0; + // needs to call my_thread_init(), otherwise we get a coredump in DBUG_ stuff + my_thread_init(); + +#ifndef DBUG_OFF +slave_begin: +#endif + DBUG_ASSERT(mi->inited); + mysql= NULL ; + retry_count= 0; + + pthread_mutex_lock(&mi->run_lock); + /* Inform waiting threads that slave has started */ + mi->slave_run_id++; + #ifndef DBUG_OFF - events_till_abort = abort_slave_event_count; + mi->events_till_abort = abort_slave_event_count; #endif - pthread_cond_broadcast(&COND_slave_start); - pthread_mutex_unlock(&LOCK_slave); - // int error = 1; - bool retried_once = 0; - ulonglong last_failed_pos = 0; - - // needs to call my_thread_init(), otherwise we get a coredump in DBUG_ stuff - my_thread_init(); - slave_thd = thd = new THD; // note that contructor of THD uses DBUG_ ! - thd->set_time(); - DBUG_ENTER("handle_slave"); + thd= new THD; // note that contructor of THD uses DBUG_ ! + DBUG_ENTER("handle_slave_io"); + THD_CHECK_SENTRY(thd); pthread_detach_this_thread(); - if (init_slave_thread(thd) || init_master_info(&glob_mi)) - { - sql_print_error("Failed during slave thread initialization"); - goto err; - } + if (init_slave_thread(thd, SLAVE_THD_IO)) + { + pthread_cond_broadcast(&mi->start_cond); + pthread_mutex_unlock(&mi->run_lock); + sql_print_error("Failed during slave I/O thread initialization"); + goto err; + } + mi->io_thd = thd; thd->thread_stack = (char*)&thd; // remember where our stack is - thd->temporary_tables = save_temporary_tables; // restore temp tables - (void) pthread_mutex_lock(&LOCK_thread_count); + pthread_mutex_lock(&LOCK_thread_count); threads.append(thd); - (void) pthread_mutex_unlock(&LOCK_thread_count); - glob_mi.pending = 0; //this should always be set to 0 when the slave thread - // is started + pthread_mutex_unlock(&LOCK_thread_count); + mi->slave_running = 1; + mi->abort_slave = 0; + pthread_mutex_unlock(&mi->run_lock); + pthread_cond_broadcast(&mi->start_cond); - DBUG_PRINT("info",("master info: log_file_name=%s, position=%s", - glob_mi.log_file_name, llstr(glob_mi.pos,llbuff))); - + DBUG_PRINT("master_info",("log_file_name: '%s' position: %s", + mi->master_log_name, + llstr(mi->master_log_pos,llbuff))); - if (!(mysql = mc_mysql_init(NULL))) + if (!(mi->mysql = mysql = mc_mysql_init(NULL))) { - sql_print_error("Slave thread: error in mc_mysql_init()"); + sql_print_error("Slave I/O thread: error in mc_mysql_init()"); goto err; } thd->proc_info = "connecting to master"; -#ifndef DBUG_OFF - sql_print_error("Slave thread initialized"); -#endif // we can get killed during safe_connect - if (!safe_connect(thd, mysql, &glob_mi)) - sql_print_error("Slave: connected to master '%s@%s:%d',\ - replication started in log '%s' at position %s", glob_mi.user, - glob_mi.host, glob_mi.port, - RPL_LOG_NAME, - llstr(glob_mi.pos,llbuff)); + if (!safe_connect(thd, mysql, mi)) + sql_print_error("Slave I/O thread: connected to master '%s@%s:%d',\ + replication started in log '%s' at position %s", mi->user, + mi->host, mi->port, + IO_RPL_LOG_NAME, + llstr(mi->master_log_pos,llbuff)); else { - sql_print_error("Slave thread killed while connecting to master"); + sql_print_error("Slave I/O thread killed while connecting to master"); goto err; } - + connected: - mysql->net.timeout=slave_net_timeout; - while (!slave_killed(thd)) + thd->slave_net = &mysql->net; + thd->proc_info = "Checking master version"; + if (check_master_version(mysql, mi)) + goto err; + if (!mi->old_format) { - thd->proc_info = "Requesting binlog dump"; - if(request_dump(mysql, &glob_mi)) - { - sql_print_error("Failed on request_dump()"); - if(slave_killed(thd)) - { - sql_print_error("Slave thread killed while requesting master \ + /* + Register ourselves with the master. + If fails, this is not fatal - we just print the error message and go + on with life. + */ + thd->proc_info = "Registering slave on master"; + if (register_slave_on_master(mysql) || update_slave_list(mysql)) + goto err; + } + + DBUG_PRINT("info",("Starting reading binary log from master")); + while (!io_slave_killed(thd,mi)) + { + bool suppress_warnings= 0; + thd->proc_info = "Requesting binlog dump"; + if (request_dump(mysql, mi, &suppress_warnings)) + { + sql_print_error("Failed on request_dump()"); + if (io_slave_killed(thd,mi)) + { + sql_print_error("Slave I/O thread killed while requesting master \ dump"); - goto err; - } - - thd->proc_info = "Waiiting to reconnect after a failed dump request"; - if(mysql->net.vio) - vio_close(mysql->net.vio); - // first time retry immediately, assuming that we can recover - // right away - if first time fails, sleep between re-tries - // hopefuly the admin can fix the problem sometime - if(retried_once) - safe_sleep(thd, glob_mi.connect_retry); - else - retried_once = 1; + goto err; + } - if(slave_killed(thd)) - { - sql_print_error("Slave thread killed while retrying master \ + thd->proc_info = "Waiiting to reconnect after a failed dump request"; + mc_end_server(mysql); + /* + First time retry immediately, assuming that we can recover + right away - if first time fails, sleep between re-tries + hopefuly the admin can fix the problem sometime + */ + if (retry_count++) + { + if (retry_count > master_retry_count) + goto err; // Don't retry forever + safe_sleep(thd,mi->connect_retry,(CHECK_KILLED_FUNC)io_slave_killed, + (void*)mi); + } + if (io_slave_killed(thd,mi)) + { + sql_print_error("Slave I/O thread killed while retrying master \ dump"); - goto err; - } - - thd->proc_info = "Reconnecting after a failed dump request"; - last_failed_pos=glob_mi.pos; - sql_print_error("Slave: failed dump request, reconnecting to \ -try again, log '%s' at postion %s", RPL_LOG_NAME, - llstr(last_failed_pos,llbuff)); - if(safe_reconnect(thd, mysql, &glob_mi, 0) || slave_killed(thd)) - { - sql_print_error("Slave thread killed during or after reconnect"); - goto err; - } - - goto connected; - } + goto err; + } - while(!slave_killed(thd)) - { - bool suppress_warnings= 0; - - thd->proc_info = "Reading master update"; - uint event_len = read_event(mysql, &glob_mi, &suppress_warnings); - - if(slave_killed(thd)) - { - sql_print_error("Slave thread killed while reading event"); - goto err; - } + thd->proc_info = "Reconnecting after a failed dump request"; + if (!suppress_warnings) + sql_print_error("Slave I/O thread: failed dump request, \ +reconnecting to try again, log '%s' at postion %s", IO_RPL_LOG_NAME, + llstr(mi->master_log_pos,llbuff)); + if (safe_reconnect(thd, mysql, mi, suppress_warnings) || + io_slave_killed(thd,mi)) + { + sql_print_error("Slave I/O thread killed during or \ +after reconnect"); + goto err; + } + + goto connected; + } + + while (!io_slave_killed(thd,mi)) + { + bool suppress_warnings= 0; + thd->proc_info = "Reading master update"; + ulong event_len = read_event(mysql, mi, &suppress_warnings); + if (io_slave_killed(thd,mi)) + { + if (global_system_variables.log_warnings) + sql_print_error("Slave I/O thread killed while reading event"); + goto err; + } - if (event_len == packet_error) - { - if(mc_mysql_errno(mysql) == ER_NET_PACKET_TOO_LARGE) - { - sql_print_error("Log entry on master is longer than \ -max_allowed_packet on slave. Slave thread will be aborted. If the entry is \ -really supposed to be that long, restart the server with a higher value of \ -max_allowed_packet. The current value is %ld", max_allowed_packet); - goto err; - } - - thd->proc_info = "Waiting to reconnect after a failed read"; - if(mysql->net.vio) - vio_close(mysql->net.vio); - if(retried_once) // punish repeat offender with sleep - safe_sleep(thd, glob_mi.connect_retry); - else - retried_once = 1; - - if(slave_killed(thd)) - { - sql_print_error("Slave thread killed while waiting to \ + if (event_len == packet_error) + { + uint mysql_error_number= mc_mysql_errno(mysql); + if (mysql_error_number == ER_NET_PACKET_TOO_LARGE) + { + sql_print_error("\ +Log entry on master is longer than max_allowed_packet (%ld) on \ +slave. If the entry is correct, restart the server with a higher value of \ +max_allowed_packet", + thd->variables.max_allowed_packet); + goto err; + } + if (mysql_error_number == ER_MASTER_FATAL_ERROR_READING_BINLOG) + { + sql_print_error(ER(mysql_error_number), mysql_error_number, + mc_mysql_error(mysql)); + goto err; + } + thd->proc_info = "Waiting to reconnect after a failed read"; + mc_end_server(mysql); + if (retry_count++) + { + if (retry_count > master_retry_count) + goto err; // Don't retry forever + safe_sleep(thd,mi->connect_retry,(CHECK_KILLED_FUNC)io_slave_killed, + (void*) mi); + } + if (io_slave_killed(thd,mi)) + { + if (global_system_variables.log_warnings) + sql_print_error("Slave I/O thread killed while waiting to \ reconnect after a failed read"); - goto err; - } - thd->proc_info = "Reconnecting after a failed read"; - last_failed_pos= glob_mi.pos; - - if (!suppress_warnings) - sql_print_error("Slave: Failed reading log event, \ -reconnecting to retry, log '%s' position %s", RPL_LOG_NAME, - llstr(last_failed_pos, llbuff)); - if(safe_reconnect(thd, mysql, &glob_mi, - suppress_warnings) - || slave_killed(thd)) - { - sql_print_error("Slave thread killed during or after a \ + goto err; + } + thd->proc_info = "Reconnecting after a failed read"; + if (!suppress_warnings) + sql_print_error("Slave I/O thread: Failed reading log event, \ +reconnecting to retry, log '%s' position %s", IO_RPL_LOG_NAME, + llstr(mi->master_log_pos, llbuff)); + if (safe_reconnect(thd, mysql, mi, suppress_warnings) || + io_slave_killed(thd,mi)) + { + if (global_system_variables.log_warnings) + sql_print_error("Slave I/O thread killed during or after a \ reconnect done to recover from failed read"); - goto err; - } - - goto connected; - } // if(event_len == packet_error) - - thd->proc_info = "Processing master log event"; - if(exec_event(thd, &mysql->net, &glob_mi, event_len)) - { - sql_print_error("\ -Error running query, slave aborted. Fix the problem, and re-start \ -the slave thread with \"mysqladmin start-slave\". We stopped at log \ -'%s' position %s", - RPL_LOG_NAME, llstr(glob_mi.pos, llbuff)); - goto err; - // there was an error running the query - // abort the slave thread, when the problem is fixed, the user - // should restart the slave with mysqladmin start-slave - } -#ifndef DBUG_OFF - if(abort_slave_event_count && !--events_till_abort) - { - sql_print_error("Slave: debugging abort"); - goto err; - } -#endif + goto err; + } + goto connected; + } // if (event_len == packet_error) - // successful exec with offset advance, - // the slave repents and his sins are forgiven! - if(glob_mi.pos > last_failed_pos) - { - retried_once = 0; + retry_count=0; // ok event, reset retry counter + thd->proc_info = "Queueing event from master"; + if (queue_event(mi,(const char*)mysql->net.read_pos + 1, + event_len)) + { + sql_print_error("Slave I/O thread could not queue event from master"); + goto err; + } + flush_master_info(mi); + if (mi->rli.log_space_limit && mi->rli.log_space_limit < + mi->rli.log_space_total) + if (wait_for_relay_log_space(&mi->rli)) + { + sql_print_error("Slave I/O thread aborted while waiting for relay \ +log space"); + goto err; + } + // TODO: check debugging abort code #ifndef DBUG_OFF - stuck_count = 0; + if (abort_slave_event_count && !--events_till_abort) + { + sql_print_error("Slave I/O thread: debugging abort"); + goto err; + } #endif - } -#ifndef DBUG_OFF - else - { - // show a little mercy, allow slave to read one more event - // before cutting him off - otherwise he gets stuck - // on Intvar events, since they do not advance the offset - // immediately - if (++stuck_count > 2) - events_till_disconnect++; - } -#endif - } // while(!slave_killed(thd)) - read/exec loop - } // while(!slave_killed(thd)) - slave loop + } + } // error = 0; - err: - // print the current replication position - sql_print_error("Slave thread exiting, replication stopped in log '%s' at \ -position %s", - RPL_LOG_NAME, llstr(glob_mi.pos,llbuff)); +err: + // print the current replication position + sql_print_error("Slave I/O thread exiting, read up to log '%s', position %s", + IO_RPL_LOG_NAME, llstr(mi->master_log_pos,llbuff)); VOID(pthread_mutex_lock(&LOCK_thread_count)); thd->query = thd->db = 0; // extra safety VOID(pthread_mutex_unlock(&LOCK_thread_count)); if (mysql) + { mc_mysql_close(mysql); + mi->mysql=0; + } thd->proc_info = "Waiting for slave mutex on exit"; - pthread_mutex_lock(&LOCK_slave); - slave_running = 0; - abort_slave = 0; - save_temporary_tables = thd->temporary_tables; - thd->temporary_tables = 0; // remove tempation from destructor to close them - pthread_cond_broadcast(&COND_slave_stopped); // tell the world we are done - pthread_mutex_unlock(&LOCK_slave); - net_end(&thd->net); // destructor will not free it, because we are weird - slave_thd = 0; - (void) pthread_mutex_lock(&LOCK_thread_count); + pthread_mutex_lock(&mi->run_lock); + mi->slave_running = 0; + mi->io_thd = 0; + // TODO: make rpl_status part of MASTER_INFO + change_rpl_status(RPL_ACTIVE_SLAVE,RPL_IDLE_SLAVE); + mi->abort_slave = 0; // TODO: check if this is needed + DBUG_ASSERT(thd->net.buff != 0); + net_end(&thd->net); // destructor will not free it, because net.vio is 0 + pthread_mutex_lock(&LOCK_thread_count); + THD_CHECK_SENTRY(thd); delete thd; - (void) pthread_mutex_unlock(&LOCK_thread_count); - my_thread_end(); + pthread_mutex_unlock(&LOCK_thread_count); + pthread_cond_broadcast(&mi->stop_cond); // tell the world we are done + pthread_mutex_unlock(&mi->run_lock); #ifndef DBUG_OFF - if(abort_slave_event_count && !events_till_abort) + if (abort_slave_event_count && !events_till_abort) goto slave_begin; #endif + my_thread_end(); +#ifndef __NETWARE__ pthread_exit(0); +#endif /* __NETWARE__ */ DBUG_RETURN(0); // Can't return anything here } -/* try to connect until successful or slave killed */ +/* slave SQL logic thread */ -static int safe_connect(THD* thd, MYSQL* mysql, MASTER_INFO* mi) +extern "C" pthread_handler_decl(handle_slave_sql,arg) { - int slave_was_killed; + THD *thd; /* needs to be first for thread_stack */ + char llbuff[22],llbuff1[22]; + RELAY_LOG_INFO* rli = &((MASTER_INFO*)arg)->rli; + const char *errmsg; + + // needs to call my_thread_init(), otherwise we get a coredump in DBUG_ stuff + my_thread_init(); + #ifndef DBUG_OFF - events_till_disconnect = disconnect_slave_event_count; +slave_begin: +#endif + + DBUG_ASSERT(rli->inited); + pthread_mutex_lock(&rli->run_lock); + DBUG_ASSERT(!rli->slave_running); + errmsg= 0; +#ifndef DBUG_OFF + rli->events_till_abort = abort_slave_event_count; +#endif + DBUG_ENTER("handle_slave_sql"); + + thd = new THD; // note that contructor of THD uses DBUG_ ! + THD_CHECK_SENTRY(thd); + /* Inform waiting threads that slave has started */ + rli->slave_run_id++; + + pthread_detach_this_thread(); + if (init_slave_thread(thd, SLAVE_THD_SQL)) + { + /* + TODO: this is currently broken - slave start and change master + will be stuck if we fail here + */ + pthread_cond_broadcast(&rli->start_cond); + pthread_mutex_unlock(&rli->run_lock); + sql_print_error("Failed during slave thread initialization"); + goto err; + } + rli->sql_thd= thd; + thd->temporary_tables = rli->save_temporary_tables; // restore temp tables + thd->thread_stack = (char*)&thd; // remember where our stack is + pthread_mutex_lock(&LOCK_thread_count); + threads.append(thd); + pthread_mutex_unlock(&LOCK_thread_count); + rli->slave_running = 1; + rli->abort_slave = 0; + pthread_mutex_unlock(&rli->run_lock); + pthread_cond_broadcast(&rli->start_cond); + // This should always be set to 0 when the slave thread is started + rli->pending = 0; + if (init_relay_log_pos(rli, + rli->relay_log_name, + rli->relay_log_pos, + 1 /*need data lock*/, &errmsg)) + { + sql_print_error("Error initializing relay log position: %s", + errmsg); + goto err; + } + THD_CHECK_SENTRY(thd); + DBUG_ASSERT(rli->relay_log_pos >= BIN_LOG_HEADER_SIZE); + DBUG_ASSERT(my_b_tell(rli->cur_log) == rli->relay_log_pos); + DBUG_ASSERT(rli->sql_thd == thd); + + DBUG_PRINT("master_info",("log_file_name: %s position: %s", + rli->master_log_name, + llstr(rli->master_log_pos,llbuff))); + if (global_system_variables.log_warnings) + sql_print_error("Slave SQL thread initialized, starting replication in \ +log '%s' at position %s, relay log '%s' position: %s", RPL_LOG_NAME, + llstr(rli->master_log_pos,llbuff),rli->relay_log_name, + llstr(rli->relay_log_pos,llbuff1)); + + /* Read queries from the IO/THREAD until this thread is killed */ + + while (!sql_slave_killed(thd,rli)) + { + thd->proc_info = "Processing master log event"; + DBUG_ASSERT(rli->sql_thd == thd); + THD_CHECK_SENTRY(thd); + if (exec_relay_log_event(thd,rli)) + { + // do not scare the user if SQL thread was simply killed or stopped + if (!sql_slave_killed(thd,rli)) + sql_print_error("\ +Error running query, slave SQL thread aborted. Fix the problem, and restart \ +the slave SQL thread with \"SLAVE START\". We stopped at log \ +'%s' position %s", + RPL_LOG_NAME, llstr(rli->master_log_pos, llbuff)); + goto err; + } + } + + /* Thread stopped. Print the current replication position to the log */ + sql_print_error("Slave SQL thread exiting, replication stopped in log \ + '%s' at position %s", + RPL_LOG_NAME, llstr(rli->master_log_pos,llbuff)); + + err: + VOID(pthread_mutex_lock(&LOCK_thread_count)); + thd->query = thd->db = 0; // extra safety + VOID(pthread_mutex_unlock(&LOCK_thread_count)); + thd->proc_info = "Waiting for slave mutex on exit"; + pthread_mutex_lock(&rli->run_lock); + DBUG_ASSERT(rli->slave_running == 1); // tracking buffer overrun + rli->slave_running = 0; + rli->save_temporary_tables = thd->temporary_tables; + + /* + TODO: see if we can do this conditionally in next_event() instead + to avoid unneeded position re-init + */ + thd->temporary_tables = 0; // remove tempation from destructor to close them + DBUG_ASSERT(thd->net.buff != 0); + net_end(&thd->net); // destructor will not free it, because we are weird + DBUG_ASSERT(rli->sql_thd == thd); + THD_CHECK_SENTRY(thd); + rli->sql_thd= 0; + pthread_mutex_lock(&LOCK_thread_count); + THD_CHECK_SENTRY(thd); + delete thd; + pthread_mutex_unlock(&LOCK_thread_count); + pthread_cond_broadcast(&rli->stop_cond); + // tell the world we are done + pthread_mutex_unlock(&rli->run_lock); +#ifndef DBUG_OFF // TODO: reconsider the code below + if (abort_slave_event_count && !rli->events_till_abort) + goto slave_begin; #endif - while(!(slave_was_killed = slave_killed(thd)) && - !mc_mysql_connect(mysql, mi->host, mi->user, mi->password, 0, - mi->port, 0, 0)) + my_thread_end(); // clean-up before broadcasting termination +#ifndef __NETWARE__ + pthread_exit(0); +#endif /* __NETWARE__ */ + DBUG_RETURN(0); // Can't return anything here +} + + +static int process_io_create_file(MASTER_INFO* mi, Create_file_log_event* cev) +{ + int error = 1; + ulong num_bytes; + bool cev_not_written; + THD* thd; + NET* net = &mi->mysql->net; + DBUG_ENTER("process_io_create_file"); + + if (unlikely(!cev->is_valid())) + DBUG_RETURN(1); + /* + TODO: fix to honor table rules, not only db rules + */ + if (!db_ok(cev->db, replicate_do_db, replicate_ignore_db)) { - sql_print_error("Slave thread: error connecting to master: %s (%d),\ - retry in %d sec", mc_mysql_error(mysql), errno, mi->connect_retry); - safe_sleep(thd, mi->connect_retry); + skip_load_data_infile(net); + DBUG_RETURN(0); + } + DBUG_ASSERT(cev->inited_from_old); + thd = mi->io_thd; + thd->file_id = cev->file_id = mi->file_id++; + thd->server_id = cev->server_id; + cev_not_written = 1; + + if (unlikely(net_request_file(net,cev->fname))) + { + sql_print_error("Slave I/O: failed requesting download of '%s'", + cev->fname); + goto err; } + + /* + This dummy block is so we could instantiate Append_block_log_event + once and then modify it slightly instead of doing it multiple times + in the loop + */ + { + Append_block_log_event aev(thd,0,0,0); - if(!slave_was_killed) + for (;;) { - mysql_log.write(thd, COM_CONNECT_OUT, "%s@%s:%d", - mi->user, mi->host, mi->port); -#ifdef SIGNAL_WITH_VIO_CLOSE - thd->set_active_vio(mysql->net.vio); -#endif + if (unlikely((num_bytes=my_net_read(net)) == packet_error)) + { + sql_print_error("Network read error downloading '%s' from master", + cev->fname); + goto err; + } + if (unlikely(!num_bytes)) /* eof */ + { + send_ok(net); /* 3.23 master wants it */ + Execute_load_log_event xev(thd,0); + xev.log_pos = mi->master_log_pos; + if (unlikely(mi->rli.relay_log.append(&xev))) + { + sql_print_error("Slave I/O: error writing Exec_load event to \ +relay log"); + goto err; + } + mi->rli.relay_log.harvest_bytes_written(&mi->rli.log_space_total); + break; + } + if (unlikely(cev_not_written)) + { + cev->block = (char*)net->read_pos; + cev->block_len = num_bytes; + cev->log_pos = mi->master_log_pos; + if (unlikely(mi->rli.relay_log.append(cev))) + { + sql_print_error("Slave I/O: error writing Create_file event to \ +relay log"); + goto err; + } + cev_not_written=0; + mi->rli.relay_log.harvest_bytes_written(&mi->rli.log_space_total); + } + else + { + aev.block = (char*)net->read_pos; + aev.block_len = num_bytes; + aev.log_pos = mi->master_log_pos; + if (unlikely(mi->rli.relay_log.append(&aev))) + { + sql_print_error("Slave I/O: error writing Append_block event to \ +relay log"); + goto err; + } + mi->rli.relay_log.harvest_bytes_written(&mi->rli.log_space_total) ; + } } + } + error=0; +err: + DBUG_RETURN(error); +} + +/* + Start using a new binary log on the master + + SYNOPSIS + process_io_rotate() + mi master_info for the slave + rev The rotate log event read from the binary log + + DESCRIPTION + Updates the master info and relay data with the place in the next binary + log where we should start reading. + + NOTES + We assume we already locked mi->data_lock + + RETURN VALUES + 0 ok + 1 Log event is illegal +*/ + +static int process_io_rotate(MASTER_INFO *mi, Rotate_log_event *rev) +{ + int return_val= 1; + DBUG_ENTER("process_io_rotate"); + safe_mutex_assert_owner(&mi->data_lock); + + if (unlikely(!rev->is_valid())) + DBUG_RETURN(1); + + memcpy(mi->master_log_name, rev->new_log_ident, rev->ident_len+1); + mi->master_log_pos= rev->pos; + DBUG_PRINT("info", ("master_log_pos: '%s' %d", + mi->master_log_name, (ulong) mi->master_log_pos)); +#ifndef DBUG_OFF + /* + If we do not do this, we will be getting the first + rotate event forever, so we need to not disconnect after one. + */ + if (disconnect_slave_event_count) + events_till_disconnect++; +#endif + DBUG_RETURN(0); +} + +/* + TODO: + Test this code before release - it has to be tested on a separate + setup with 3.23 master +*/ + +static int queue_old_event(MASTER_INFO *mi, const char *buf, + ulong event_len) +{ + const char *errmsg = 0; + ulong inc_pos; + bool ignore_event= 0; + char *tmp_buf = 0; + RELAY_LOG_INFO *rli= &mi->rli; + DBUG_ENTER("queue_old_event"); + + /* + If we get Load event, we need to pass a non-reusable buffer + to read_log_event, so we do a trick + */ + if (buf[EVENT_TYPE_OFFSET] == LOAD_EVENT) + { + if (unlikely(!(tmp_buf=(char*)my_malloc(event_len+1,MYF(MY_WME))))) + { + sql_print_error("Slave I/O: out of memory for Load event"); + DBUG_RETURN(1); + } + memcpy(tmp_buf,buf,event_len); + tmp_buf[event_len]=0; // Create_file constructor wants null-term buffer + buf = (const char*)tmp_buf; + } + Log_event *ev = Log_event::read_log_event(buf,event_len, &errmsg, + 1 /*old format*/ ); + if (unlikely(!ev)) + { + sql_print_error("Read invalid event from master: '%s',\ + master could be corrupt but a more likely cause of this is a bug", + errmsg); + my_free((char*) tmp_buf, MYF(MY_ALLOW_ZERO_PTR)); + DBUG_RETURN(1); + } + pthread_mutex_lock(&mi->data_lock); + ev->log_pos = mi->master_log_pos; + switch (ev->get_type_code()) { + case STOP_EVENT: + ignore_event= mi->ignore_stop_event; + mi->ignore_stop_event=0; + inc_pos= event_len; + break; + case ROTATE_EVENT: + if (unlikely(process_io_rotate(mi,(Rotate_log_event*)ev))) + { + delete ev; + pthread_mutex_unlock(&mi->data_lock); + DBUG_RETURN(1); + } + mi->ignore_stop_event=1; + inc_pos= 0; + break; + case CREATE_FILE_EVENT: + { + /* We come here when and only when tmp_buf != 0 */ + DBUG_ASSERT(tmp_buf); + int error = process_io_create_file(mi,(Create_file_log_event*)ev); + delete ev; + mi->master_log_pos += event_len; + DBUG_PRINT("info", ("master_log_pos: %d", (ulong) mi->master_log_pos)); + pthread_mutex_unlock(&mi->data_lock); + my_free((char*)tmp_buf, MYF(0)); + DBUG_RETURN(error); + } + default: + mi->ignore_stop_event=0; + inc_pos= event_len; + break; + } + if (likely(!ignore_event)) + { + if (unlikely(rli->relay_log.append(ev))) + { + delete ev; + pthread_mutex_unlock(&mi->data_lock); + DBUG_RETURN(1); + } + rli->relay_log.harvest_bytes_written(&rli->log_space_total); + } + delete ev; + mi->master_log_pos+= inc_pos; + DBUG_PRINT("info", ("master_log_pos: %d", (ulong) mi->master_log_pos)); + pthread_mutex_unlock(&mi->data_lock); + DBUG_RETURN(0); +} + +/* + TODO: verify the issue with stop events, see if we need them at all + in the relay log +*/ + +int queue_event(MASTER_INFO* mi,const char* buf, ulong event_len) +{ + int error= 0; + ulong inc_pos; + bool ignore_event= 0; + RELAY_LOG_INFO *rli= &mi->rli; + DBUG_ENTER("queue_event"); + + if (mi->old_format) + DBUG_RETURN(queue_old_event(mi,buf,event_len)); + + pthread_mutex_lock(&mi->data_lock); + + /* + TODO: figure out if other events in addition to Rotate + require special processing + */ + switch (buf[EVENT_TYPE_OFFSET]) { + case STOP_EVENT: + ignore_event= mi->ignore_stop_event; + mi->ignore_stop_event= 0; + inc_pos= event_len; + break; + case ROTATE_EVENT: + { + Rotate_log_event rev(buf,event_len,0); + if (unlikely(process_io_rotate(mi,&rev))) + { + pthread_mutex_unlock(&mi->data_lock); + DBUG_RETURN(1); + } + mi->ignore_stop_event= 1; + inc_pos= 0; + break; + } + default: + mi->ignore_stop_event= 0; + inc_pos= event_len; + break; + } - return slave_was_killed; + if (likely(!ignore_event && + !(error= rli->relay_log.appendv(buf,event_len,0)))) + { + mi->master_log_pos+= inc_pos; + DBUG_PRINT("info", ("master_log_pos: %d", (ulong) mi->master_log_pos)); + rli->relay_log.harvest_bytes_written(&rli->log_space_total); + } + pthread_mutex_unlock(&mi->data_lock); + DBUG_RETURN(error); } + +void end_relay_log_info(RELAY_LOG_INFO* rli) +{ + DBUG_ENTER("end_relay_log_info"); + + if (!rli->inited) + DBUG_VOID_RETURN; + if (rli->info_fd >= 0) + { + end_io_cache(&rli->info_file); + (void) my_close(rli->info_fd, MYF(MY_WME)); + rli->info_fd = -1; + } + if (rli->cur_log_fd >= 0) + { + end_io_cache(&rli->cache_buf); + (void)my_close(rli->cur_log_fd, MYF(MY_WME)); + rli->cur_log_fd = -1; + } + rli->inited = 0; + rli->relay_log.close(1); + DBUG_VOID_RETURN; +} + +/* try to connect until successful or slave killed */ +static int safe_connect(THD* thd, MYSQL* mysql, MASTER_INFO* mi) +{ + return connect_to_master(thd, mysql, mi, 0, 0); +} + + /* Try to connect until successful or slave killed or we have retried master_retry_count times */ -static int safe_reconnect(THD* thd, MYSQL* mysql, MASTER_INFO* mi, - bool suppress_warnings) +static int connect_to_master(THD* thd, MYSQL* mysql, MASTER_INFO* mi, + bool reconnect, bool suppress_warnings) { int slave_was_killed; int last_errno= -2; // impossible error ulong err_count=0; char llbuff[22]; + DBUG_ENTER("connect_to_master"); - /* - If we lost connection after reading a state set event - we will be re-reading it, so pending needs to be cleared - */ - mi->pending = 0; #ifndef DBUG_OFF events_till_disconnect = disconnect_slave_event_count; #endif - while (!(slave_was_killed = slave_killed(thd)) && mc_mysql_reconnect(mysql)) + uint client_flag=0; + if (opt_slave_compressed_protocol) + client_flag=CLIENT_COMPRESS; /* We will use compression */ + + while (!(slave_was_killed = io_slave_killed(thd,mi)) && + (reconnect ? mc_mysql_reconnect(mysql) != 0: + !mc_mysql_connect(mysql, mi->host, mi->user, mi->password, 0, + mi->port, 0, client_flag, + thd->variables.net_read_timeout))) { /* Don't repeat last error */ if (mc_mysql_errno(mysql) != last_errno) { + last_errno=mc_mysql_errno(mysql); suppress_warnings= 0; - sql_print_error("Slave thread: error re-connecting to master: \ -%s, last_errno=%d, retry in %d sec", - mc_mysql_error(mysql), last_errno=mc_mysql_errno(mysql), - mi->connect_retry); + sql_print_error("Slave I/O thread: error %s to master \ +'%s@%s:%d': \ +Error: '%s' errno: %d retry-time: %d retries: %d", + (reconnect ? "reconnecting" : "connecting"), + mi->user,mi->host,mi->port, + mc_mysql_error(mysql), last_errno, + mi->connect_retry, + master_retry_count); } - safe_sleep(thd, mi->connect_retry); - /* if master_retry_count is not set, keep trying until success */ - if (master_retry_count && err_count++ == master_retry_count) + /* + By default we try forever. The reason is that failure will trigger + master election, so if the user did not set master_retry_count we + do not want to have election triggered on the first failure to + connect + */ + if (++err_count == master_retry_count) { slave_was_killed=1; + if (reconnect) + change_rpl_status(RPL_ACTIVE_SLAVE,RPL_LOST_SOLDIER); break; } + safe_sleep(thd,mi->connect_retry,(CHECK_KILLED_FUNC)io_slave_killed, + (void*)mi); } if (!slave_was_killed) { - if (!suppress_warnings) - sql_print_error("Slave: reconnected to master '%s@%s:%d',\ -replication resumed in log '%s' at position %s", glob_mi.user, - glob_mi.host, glob_mi.port, - RPL_LOG_NAME, - llstr(glob_mi.pos,llbuff)); + if (reconnect) + { + if (!suppress_warnings && global_system_variables.log_warnings) + sql_print_error("Slave: connected to master '%s@%s:%d',\ +replication resumed in log '%s' at position %s", mi->user, + mi->host, mi->port, + IO_RPL_LOG_NAME, + llstr(mi->master_log_pos,llbuff)); + } + else + { + change_rpl_status(RPL_IDLE_SLAVE,RPL_ACTIVE_SLAVE); + mysql_log.write(thd, COM_CONNECT_OUT, "%s@%s:%d", + mi->user, mi->host, mi->port); + } #ifdef SIGNAL_WITH_VIO_CLOSE thd->set_active_vio(mysql->net.vio); #endif } + DBUG_PRINT("exit",("slave_was_killed: %d", slave_was_killed)); + DBUG_RETURN(slave_was_killed); +} + + +/* + Try to connect until successful or slave killed or we have retried + master_retry_count times +*/ + +static int safe_reconnect(THD* thd, MYSQL* mysql, MASTER_INFO* mi, + bool suppress_warnings) +{ + return connect_to_master(thd, mysql, mi, 1, suppress_warnings); +} + + +/* + Store the file and position where the execute-slave thread are in the + relay log. + + SYNOPSIS + flush_relay_log_info() + rli Relay log information + + NOTES + - As this is only called by the slave thread, we don't need to + have a lock on this. + - If there is an active transaction, then we don't update the position + in the relay log. This is to ensure that we re-execute statements + if we die in the middle of an transaction that was rolled back. + - As a transaction never spans binary logs, we don't have to handle the + case where we do a relay-log-rotation in the middle of the transaction. + If this would not be the case, we would have to ensure that we + don't delete the relay log file where the transaction started when + we switch to a new relay log file. + + TODO + - Change the log file information to a binary format to avoid calling + longlong2str. + + RETURN VALUES + 0 ok + 1 write error +*/ + +bool flush_relay_log_info(RELAY_LOG_INFO* rli) +{ + bool error=0; + IO_CACHE *file = &rli->info_file; + char buff[FN_REFLEN*2+22*2+4], *pos; + + /* sql_thd is not set when calling from init_slave() */ + if ((rli->sql_thd && rli->sql_thd->options & OPTION_BEGIN)) + return 0; // Wait for COMMIT + + my_b_seek(file, 0L); + pos=strmov(buff, rli->relay_log_name); + *pos++='\n'; + pos=longlong2str(rli->relay_log_pos, pos, 10); + *pos++='\n'; + pos=strmov(pos, rli->master_log_name); + *pos++='\n'; + pos=longlong2str(rli->master_log_pos, pos, 10); + *pos='\n'; + if (my_b_write(file, (byte*) buff, (ulong) (pos-buff)+1)) + error=1; + if (flush_io_cache(file)) + error=1; + if (flush_io_cache(rli->cur_log)) // QQ Why this call ? + error=1; + return error; +} + + +/* + This function is called when we notice that the current "hot" log + got rotated under our feet. +*/ + +static IO_CACHE *reopen_relay_log(RELAY_LOG_INFO *rli, const char **errmsg) +{ + DBUG_ASSERT(rli->cur_log != &rli->cache_buf); + DBUG_ASSERT(rli->cur_log_fd == -1); + DBUG_ENTER("reopen_relay_log"); + + IO_CACHE *cur_log = rli->cur_log=&rli->cache_buf; + if ((rli->cur_log_fd=open_binlog(cur_log,rli->relay_log_name, + errmsg)) <0) + DBUG_RETURN(0); + /* + We want to start exactly where we was before: + relay_log_pos Current log pos + pending Number of bytes already processed from the event + */ + my_b_seek(cur_log,rli->relay_log_pos + rli->pending); + DBUG_RETURN(cur_log); +} + + +Log_event* next_event(RELAY_LOG_INFO* rli) +{ + Log_event* ev; + IO_CACHE* cur_log = rli->cur_log; + pthread_mutex_t *log_lock = rli->relay_log.get_log_lock(); + const char* errmsg=0; + THD* thd = rli->sql_thd; + DBUG_ENTER("next_event"); + DBUG_ASSERT(thd != 0); + + /* + For most operations we need to protect rli members with data_lock, + so we will hold it for the most of the loop below + However, we will release it whenever it is worth the hassle, + and in the cases when we go into a pthread_cond_wait() with the + non-data_lock mutex + */ + pthread_mutex_lock(&rli->data_lock); + + while (!sql_slave_killed(thd,rli)) + { + /* + We can have two kinds of log reading: + hot_log: + rli->cur_log points at the IO_CACHE of relay_log, which + is actively being updated by the I/O thread. We need to be careful + in this case and make sure that we are not looking at a stale log that + has already been rotated. If it has been, we reopen the log. + + The other case is much simpler: + We just have a read only log that nobody else will be updating. + */ + bool hot_log; + if ((hot_log = (cur_log != &rli->cache_buf))) + { + DBUG_ASSERT(rli->cur_log_fd == -1); // foreign descriptor + pthread_mutex_lock(log_lock); + + /* + Reading xxx_file_id is safe because the log will only + be rotated when we hold relay_log.LOCK_log + */ + if (rli->relay_log.get_open_count() != rli->cur_log_old_open_count) + { + // The master has switched to a new log file; Reopen the old log file + cur_log=reopen_relay_log(rli, &errmsg); + pthread_mutex_unlock(log_lock); + if (!cur_log) // No more log files + goto err; + hot_log=0; // Using old binary log + } + } + DBUG_ASSERT(my_b_tell(cur_log) >= BIN_LOG_HEADER_SIZE); + DBUG_ASSERT(my_b_tell(cur_log) == rli->relay_log_pos + rli->pending); + /* + Relay log is always in new format - if the master is 3.23, the + I/O thread will convert the format for us + */ + if ((ev=Log_event::read_log_event(cur_log,0,(bool)0 /* new format */))) + { + DBUG_ASSERT(thd==rli->sql_thd); + if (hot_log) + pthread_mutex_unlock(log_lock); + pthread_mutex_unlock(&rli->data_lock); + DBUG_RETURN(ev); + } + DBUG_ASSERT(thd==rli->sql_thd); + if (opt_reckless_slave) // For mysql-test + cur_log->error = 0; + if (cur_log->error < 0) + { + errmsg = "slave SQL thread aborted because of I/O error"; + if (hot_log) + pthread_mutex_unlock(log_lock); + goto err; + } + if (!cur_log->error) /* EOF */ + { + /* + On a hot log, EOF means that there are no more updates to + process and we must block until I/O thread adds some and + signals us to continue + */ + if (hot_log) + { + DBUG_ASSERT(rli->relay_log.get_open_count() == rli->cur_log_old_open_count); + /* + We can, and should release data_lock while we are waiting for + update. If we do not, show slave status will block + */ + pthread_mutex_unlock(&rli->data_lock); + /* Note that wait_for_update unlocks lock_log ! */ + rli->relay_log.wait_for_update(rli->sql_thd); + + // re-acquire data lock since we released it earlier + pthread_mutex_lock(&rli->data_lock); + continue; + } + /* + If the log was not hot, we need to move to the next log in + sequence. The next log could be hot or cold, we deal with both + cases separately after doing some common initialization + */ + end_io_cache(cur_log); + DBUG_ASSERT(rli->cur_log_fd >= 0); + my_close(rli->cur_log_fd, MYF(MY_WME)); + rli->cur_log_fd = -1; + + /* + TODO: make skip_log_purge a start-up option. At this point this + is not critical priority + */ + if (!rli->skip_log_purge) + { + // purge_first_log will properly set up relay log coordinates in rli + if (rli->relay_log.purge_first_log(rli)) + { + errmsg = "Error purging processed log"; + goto err; + } + } + else + { + /* + If hot_log is set, then we already have a lock on + LOCK_log. If not, we have to get the lock. + + According to Sasha, the only time this code will ever be executed + is if we are recovering from a bug. + */ + if (rli->relay_log.find_next_log(&rli->linfo, !hot_log)) + { + errmsg = "error switching to the next log"; + goto err; + } + rli->relay_log_pos = BIN_LOG_HEADER_SIZE; + rli->pending=0; + strmake(rli->relay_log_name,rli->linfo.log_file_name, + sizeof(rli->relay_log_name)-1); + flush_relay_log_info(rli); + } + + // next log is hot + if (rli->relay_log.is_active(rli->linfo.log_file_name)) + { +#ifdef EXTRA_DEBUG + sql_print_error("next log '%s' is currently active", + rli->linfo.log_file_name); +#endif + rli->cur_log= cur_log= rli->relay_log.get_log_file(); + rli->cur_log_old_open_count= rli->relay_log.get_open_count(); + DBUG_ASSERT(rli->cur_log_fd == -1); + + /* + Read pointer has to be at the start since we are the only + reader + */ + if (check_binlog_magic(cur_log,&errmsg)) + goto err; + continue; + } + /* + if we get here, the log was not hot, so we will have to + open it ourselves + */ +#ifdef EXTRA_DEBUG + sql_print_error("next log '%s' is not active", + rli->linfo.log_file_name); +#endif + // open_binlog() will check the magic header + if ((rli->cur_log_fd=open_binlog(cur_log,rli->linfo.log_file_name, + &errmsg)) <0) + goto err; + } + else + { + /* + Read failed with a non-EOF error. + TODO: come up with something better to handle this error + */ + if (hot_log) + pthread_mutex_unlock(log_lock); + sql_print_error("Slave SQL thread: I/O error reading \ +event(errno: %d cur_log->error: %d)", + my_errno,cur_log->error); + // set read position to the beginning of the event + my_b_seek(cur_log,rli->relay_log_pos+rli->pending); + /* otherwise, we have had a partial read */ + errmsg = "Aborting slave SQL thread because of partial event read"; + break; // To end of function + } + } + if (!errmsg && global_system_variables.log_warnings) + errmsg = "slave SQL thread was killed"; - return slave_was_killed; +err: + pthread_mutex_unlock(&rli->data_lock); + if (errmsg) + sql_print_error("Error reading relay log event: %s", errmsg); + DBUG_RETURN(0); } + #ifdef __GNUC__ template class I_List_iterator<i_string>; template class I_List_iterator<i_string_pair>; diff --git a/sql/slave.h b/sql/slave.h index 769689ebfa2..fe0f0b045f3 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -1,71 +1,297 @@ #ifndef SLAVE_H #define SLAVE_H +#include "mysql.h" +#include "my_list.h" #define SLAVE_NET_TIMEOUT 3600 +#define MAX_SLAVE_ERRMSG 1024 #define MAX_SLAVE_ERROR 2000 +/* + The replication is accomplished by starting two threads - I/O + thread, and SQL thread. I/O thread is associated with its + MASTER_INFO struct, so MASTER_INFO can be viewed as I/O thread + descriptor. SQL thread is associated with RELAY_LOG_INFO struct. + + I/O thread reads maintains a connection to the master, and reads log + events from the master as they arrive, queueing them by writing them + out into the temporary slave binary log (relay log). The SQL thread, + in turn, reads the slave binary log executing each event. + + Relay log is needed to be able to handle situations when there is a large + backlog of unprocessed events from the master (eg. one particular update + takes a day to finish), and to be able to restart the slave server without + having to re-read the master updates. + */ + extern ulong slave_net_timeout, master_retry_count; extern MY_BITMAP slave_error_mask; extern bool use_slave_mask; +extern char* slave_load_tmpdir; +extern my_string master_info_file,relay_log_info_file; +extern my_string opt_relay_logname, opt_relaylog_index_name; +extern my_bool opt_skip_slave_start, opt_reckless_slave; +extern my_bool opt_log_slave_updates; +extern ulonglong relay_log_space_limit; +struct st_master_info; + +/* + TODO: this needs to be redone, but for now it does not matter since + we do not have multi-master yet. +*/ + +#define LOCK_ACTIVE_MI { pthread_mutex_lock(&LOCK_active_mi); \ + ++active_mi_in_use; \ + pthread_mutex_unlock(&LOCK_active_mi);} + +#define UNLOCK_ACTIVE_MI { pthread_mutex_lock(&LOCK_active_mi); \ + --active_mi_in_use; \ + pthread_mutex_unlock(&LOCK_active_mi); } + +/* + st_relay_log_info contains information on the current relay log and + relay log offset, and master log name and log sequence corresponding to the + last update. Additionally, misc information specific to the SQL thread is + included. + + st_relay_log_info is initialized from the slave.info file if such exists. + Otherwise, data members are intialized with defaults. The initialization is + done with init_relay_log_info() call. + + The format of slave.info file: + + relay_log_name + relay_log_pos + master_log_name + master_log_pos + + To clean up, call end_relay_log_info() + */ + +typedef struct st_relay_log_info +{ + /*** The following variables can only be read when protect by data lock ****/ + + /* + info_fd - file descriptor of the info file. set only during + initialization or clean up - safe to read anytime + cur_log_fd - file descriptor of the current read relay log + */ + File info_fd,cur_log_fd; + /* name of current read relay log */ + char relay_log_name[FN_REFLEN]; + /* master log name corresponding to current read position */ + char master_log_name[FN_REFLEN]; + /* original log position of last processed event */ + volatile my_off_t master_log_pos; + + /* + Protected with internal locks. + Must get data_lock when resetting the logs. + */ + MYSQL_LOG relay_log; + LOG_INFO linfo; + IO_CACHE cache_buf,*cur_log; + + /* The following variables are safe to read any time */ + + /* IO_CACHE of the info file - set only during init or end */ + IO_CACHE info_file; + + /* + When we restart slave thread we need to have access to the previously + created temporary tables. Modified only on init/end and by the SQL + thread, read only by SQL thread. + */ + TABLE *save_temporary_tables; + + /* + standard lock acquistion order to avoid deadlocks: + run_lock, data_lock, relay_log.LOCK_log, relay_log.LOCK_index + */ + pthread_mutex_t data_lock,run_lock; + + /* + start_cond is broadcast when SQL thread is started + stop_cond - when stopped + data_cond - when data protected by data_lock changes + */ + pthread_cond_t start_cond, stop_cond, data_cond; + + /* parent master info structure */ + struct st_master_info *mi; + + /* + Needed to deal properly with cur_log getting closed and re-opened with + a different log under our feet + */ + uint32 cur_log_old_open_count; + + /* + Current offset in the relay log. + pending - in some cases we do not increment offset immediately after + processing an event, because the following event needs to be processed + atomically together with this one ( so far, there is only one type of + such event - Intvar_event that sets auto_increment value). However, once + both events have been processed, we need to increment by the cumulative + offset. pending stored the extra offset to be added to the position. + */ + ulonglong relay_log_pos, pending; + ulonglong log_space_limit,log_space_total; + + /* + InnoDB internally stores the master log position it has processed + so far; the position to store is really the sum of + pos + pending + event_len here since we must store the pos of the + END of the current log event + */ + int event_len; + + /* + Needed for problems when slave stops and we want to restart it + skipping one or more events in the master log that have caused + errors, and have been manually applied by DBA already. + */ + volatile uint32 slave_skip_counter; + volatile ulong abort_pos_wait; /* Incremented on change master */ + volatile ulong slave_run_id; /* Incremented on slave start */ + pthread_mutex_t log_space_lock; + pthread_cond_t log_space_cond; + THD * sql_thd; + int last_slave_errno; +#ifndef DBUG_OFF + int events_till_abort; +#endif + char last_slave_error[MAX_SLAVE_ERRMSG]; + + /* if not set, the value of other members of the structure are undefined */ + bool inited; + volatile bool abort_slave, slave_running; + bool skip_log_purge; + bool inside_transaction; + + st_relay_log_info(); + ~st_relay_log_info(); + inline void inc_pending(ulonglong val) + { + pending += val; + } + /* TODO: this probably needs to be fixed */ + inline void inc_pos(ulonglong val, ulonglong log_pos, bool skip_lock=0) + { + if (!skip_lock) + pthread_mutex_lock(&data_lock); + relay_log_pos += val+pending; + pending = 0; + if (log_pos) + master_log_pos = log_pos+ val; + pthread_cond_broadcast(&data_cond); + if (!skip_lock) + pthread_mutex_unlock(&data_lock); + } + /* + thread safe read of position - not needed if we are in the slave thread, + but required otherwise as var is a longlong + */ + inline void read_pos(ulonglong& var) + { + pthread_mutex_lock(&data_lock); + var = relay_log_pos; + pthread_mutex_unlock(&data_lock); + } + + int wait_for_pos(THD* thd, String* log_name, longlong log_pos, + longlong timeout); +} RELAY_LOG_INFO; + + +Log_event* next_event(RELAY_LOG_INFO* rli); + +/* + st_master_info contains information about how to connect to a master, + current master log name, and current log offset, as well as misc + control variables + + st_master_info is initialized once from the master.info file if such + exists. Otherwise, data members corresponding to master.info fields + are initialized with defaults specified by master-* options. The + initialization is done through init_master_info() call. + + The format of master.info file: + + log_name + log_pos + master_host + master_user + master_pass + master_port + master_connect_retry + + To write out the contents of master.info file to disk ( needed every + time we read and queue data from the master ), a call to + flush_master_info() is required. + + To clean up, call end_master_info() +*/ + typedef struct st_master_info { - char log_file_name[FN_REFLEN]; - ulonglong pos,pending; - int event_len; /* Added by Heikki: InnoDB internally stores the - master log position it has processed so far; the - position to store is really the sum - pos + pending + event_len - here since we must store the pos of the END of the - current log event */ + char master_log_name[FN_REFLEN]; + + my_off_t master_log_pos; File fd; // we keep the file open, so we need to remember the file pointer IO_CACHE file; - // the variables below are needed because we can change masters on the fly + + /* the variables below are needed because we can change masters on the fly */ char host[HOSTNAME_LENGTH+1]; char user[USERNAME_LENGTH+1]; char password[HASH_PASSWORD_LENGTH+1]; + pthread_mutex_t data_lock,run_lock; + pthread_cond_t data_cond,start_cond,stop_cond; + THD *io_thd; + MYSQL* mysql; + uint32 file_id; /* for 3.23 load data infile */ + RELAY_LOG_INFO rli; uint port; uint connect_retry; - pthread_mutex_t lock; - pthread_cond_t cond; +#ifndef DBUG_OFF + int events_till_abort; +#endif bool inited; + bool old_format; /* master binlog is in 3.23 format */ + volatile bool abort_slave, slave_running; + volatile ulong slave_run_id; + bool ignore_stop_event; + - st_master_info():pending(0),fd(-1),inited(0) + st_master_info() + :fd(-1), io_thd(0), inited(0), old_format(0),abort_slave(0), + slave_running(0), slave_run_id(0) { host[0] = 0; user[0] = 0; password[0] = 0; - pthread_mutex_init(&lock, MY_MUTEX_INIT_FAST); - pthread_cond_init(&cond, NULL); + bzero(&file, sizeof(file)); + pthread_mutex_init(&run_lock, MY_MUTEX_INIT_FAST); + pthread_mutex_init(&data_lock, MY_MUTEX_INIT_FAST); + pthread_cond_init(&data_cond, NULL); + pthread_cond_init(&start_cond, NULL); + pthread_cond_init(&stop_cond, NULL); } ~st_master_info() { - pthread_mutex_destroy(&lock); - pthread_cond_destroy(&cond); - } - inline void inc_pending(ulonglong val) - { - pending += val; - } - inline void inc_pos(ulonglong val) - { - pthread_mutex_lock(&lock); - pos += val + pending; - pending = 0; - pthread_cond_broadcast(&cond); - pthread_mutex_unlock(&lock); - } - // thread safe read of position - not needed if we are in the slave thread, - // but required otherwise - inline void read_pos(ulonglong& var) - { - pthread_mutex_lock(&lock); - var = pos; - pthread_mutex_unlock(&lock); + pthread_mutex_destroy(&run_lock); + pthread_mutex_destroy(&data_lock); + pthread_cond_destroy(&data_cond); + pthread_cond_destroy(&start_cond); + pthread_cond_destroy(&stop_cond); } - int wait_for_pos(THD* thd, String* log_name, ulonglong log_pos); } MASTER_INFO; + +int queue_event(MASTER_INFO* mi,const char* buf,ulong event_len); + typedef struct st_table_rule_ent { char* db; @@ -75,64 +301,119 @@ typedef struct st_table_rule_ent #define TABLE_RULE_HASH_SIZE 16 #define TABLE_RULE_ARR_SIZE 16 +#define MAX_SLAVE_ERRMSG 1024 + +#define RPL_LOG_NAME (rli->master_log_name[0] ? rli->master_log_name :\ + "FIRST") +#define IO_RPL_LOG_NAME (mi->master_log_name[0] ? mi->master_log_name :\ + "FIRST") + +/* masks for start/stop operations on io and sql slave threads */ +#define SLAVE_IO 1 +#define SLAVE_SQL 2 -int flush_master_info(MASTER_INFO* mi); +/* + If the following is set, if first gives an error, second will be + tried. Otherwise, if first fails, we fail. +*/ +#define SLAVE_FORCE_ALL 4 -int mysql_table_dump(THD* thd, char* db, char* tbl_name, int fd = -1); -// if fd is -1, dump to NET -int fetch_nx_table(THD* thd, MASTER_INFO* mi); -// retrieve non-exitent table from master -// the caller must set thd->last_nx_table and thd->last_nx_db first -int show_master_info(THD* thd); +int init_slave(); +void init_slave_skip_errors(const char* arg); +bool flush_master_info(MASTER_INFO* mi); +bool flush_relay_log_info(RELAY_LOG_INFO* rli); +int register_slave_on_master(MYSQL* mysql); +int terminate_slave_threads(MASTER_INFO* mi, int thread_mask, + bool skip_lock = 0); +int terminate_slave_thread(THD* thd, pthread_mutex_t* term_mutex, + pthread_mutex_t* cond_lock, + pthread_cond_t* term_cond, + volatile bool* slave_running); +int start_slave_threads(bool need_slave_mutex, bool wait_for_start, + MASTER_INFO* mi, const char* master_info_fname, + const char* slave_info_fname, int thread_mask); +/* + cond_lock is usually same as start_lock. It is needed for the case when + start_lock is 0 which happens if start_slave_thread() is called already + inside the start_lock section, but at the same time we want a + pthread_cond_wait() on start_cond,start_lock +*/ +int start_slave_thread(pthread_handler h_func, pthread_mutex_t* start_lock, + pthread_mutex_t *cond_lock, + pthread_cond_t* start_cond, + volatile bool *slave_running, + volatile ulong *slave_run_id, + MASTER_INFO* mi); + +/* If fd is -1, dump to NET */ +int mysql_table_dump(THD* thd, const char* db, + const char* tbl_name, int fd = -1); + +/* retrieve non-exitent table from master */ +int fetch_master_table(THD* thd, const char* db_name, const char* table_name, + MASTER_INFO* mi, MYSQL* mysql); + +int show_master_info(THD* thd, MASTER_INFO* mi); int show_binlog_info(THD* thd); +/* See if the query uses any tables that should not be replicated */ int tables_ok(THD* thd, TABLE_LIST* tables); -// see if the query uses any tables that should not be replicated +/* + Check to see if the database is ok to operate on with respect to the + do and ignore lists - used in replication +*/ int db_ok(const char* db, I_List<i_string> &do_list, I_List<i_string> &ignore_list ); -// check to see if the database is ok to operate on with respect to the -// do and ignore lists - used in replication +int db_ok_with_wild_table(const char *db); int add_table_rule(HASH* h, const char* table_spec); int add_wild_table_rule(DYNAMIC_ARRAY* a, const char* table_spec); void init_table_rule_hash(HASH* h, bool* h_inited); void init_table_rule_array(DYNAMIC_ARRAY* a, bool* a_inited); -void init_slave_skip_errors(char* arg); +char* rewrite_db(char* db); +int check_expected_error(THD* thd, RELAY_LOG_INFO* rli, int error_code); +void skip_load_data_infile(NET* net); +void slave_print_error(RELAY_LOG_INFO* rli,int err_code, const char* msg, ...); -void end_slave(); // clean up -int init_master_info(MASTER_INFO* mi); +void end_slave(); /* clean up */ +int init_master_info(MASTER_INFO* mi, const char* master_info_fname, + const char* slave_info_fname, + bool abort_if_no_master_info_file); void end_master_info(MASTER_INFO* mi); -extern bool opt_log_slave_updates ; -pthread_handler_decl(handle_slave,arg); -extern bool volatile abort_loop, abort_slave, slave_running; -extern uint32 slave_skip_counter; -// needed for problems when slave stops and -// we want to restart it skipping one or more events in the master log that -// have caused errors, and have been manually applied by DBA already - -extern pthread_t slave_real_id; -extern THD* slave_thd; -extern MASTER_INFO glob_mi; +int init_relay_log_info(RELAY_LOG_INFO* rli, const char* info_fname); +void end_relay_log_info(RELAY_LOG_INFO* rli); +void lock_slave_threads(MASTER_INFO* mi); +void unlock_slave_threads(MASTER_INFO* mi); +void init_thread_mask(int* mask,MASTER_INFO* mi,bool inverse); +int init_relay_log_pos(RELAY_LOG_INFO* rli,const char* log,ulonglong pos, + bool need_data_lock, const char** errmsg); + +int purge_relay_logs(RELAY_LOG_INFO* rli, THD *thd, bool just_reset, + const char** errmsg); + +extern "C" pthread_handler_decl(handle_slave_io,arg); +extern "C" pthread_handler_decl(handle_slave_sql,arg); +extern bool volatile abort_loop; +extern MASTER_INFO main_mi, *active_mi; /* active_mi for multi-master */ +extern volatile int active_mi_in_use; +extern LIST master_list; extern HASH replicate_do_table, replicate_ignore_table; extern DYNAMIC_ARRAY replicate_wild_do_table, replicate_wild_ignore_table; extern bool do_table_inited, ignore_table_inited, wild_do_table_inited, wild_ignore_table_inited; extern bool table_rules_on; -#ifndef DBUG_OFF extern int disconnect_slave_event_count, abort_slave_event_count ; -#endif -// the master variables are defaults read from my.cnf or command line -extern uint master_port, master_connect_retry; +/* the master variables are defaults read from my.cnf or command line */ +extern uint master_port, master_connect_retry, report_port; extern my_string master_user, master_password, master_host, - master_info_file; + master_info_file, relay_log_info_file, report_user, report_host, + report_password; extern I_List<i_string> replicate_do_db, replicate_ignore_db; extern I_List<i_string_pair> replicate_rewrite_db; extern I_List<THD> threads; #endif - - diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 05ec57b134a..ee9d3f1c1ea 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -1,15 +1,15 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - +/* Copyright (C) 2000 MySQL AB + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -29,24 +29,25 @@ #include "sql_acl.h" #include "hash_filo.h" #include <m_ctype.h> +#include <assert.h> #include <stdarg.h> -/* - ACL_HOST is used if no host is specified - */ - struct acl_host_and_ip { char *hostname; long ip,ip_mask; // Used with masked ip:s }; + class ACL_ACCESS { public: ulong sort; - uint access; + ulong access; }; + +/* ACL_HOST is used if no host is specified */ + class ACL_HOST :public ACL_ACCESS { public: @@ -54,15 +55,20 @@ public: char *db; }; + class ACL_USER :public ACL_ACCESS { public: acl_host_and_ip host; uint hostname_length; + USER_RESOURCES user_resource; char *user,*password; ulong salt[2]; + enum SSL_type ssl_type; + const char *ssl_cipher, *x509_issuer, *x509_subject; }; + class ACL_DB :public ACL_ACCESS { public: @@ -70,14 +76,16 @@ public: char *user,*db; }; + class acl_entry :public hash_filo_element { public: - uint access; + ulong access; uint16 length; char key[1]; // Key will be stored here }; + static byte* acl_entry_get_key(acl_entry *entry,uint *length, my_bool not_used __attribute__((unused))) { @@ -95,7 +103,7 @@ static HASH acl_check_hosts, hash_tables; static DYNAMIC_ARRAY acl_wild_hosts; static hash_filo *acl_cache; static uint grant_version=0; -static uint get_access(TABLE *form,uint fieldnr); +static ulong get_access(TABLE *form,uint fieldnr); static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b); static ulong get_sort(uint count,...); static void init_check_host(void); @@ -106,29 +114,47 @@ static void update_hostname(acl_host_and_ip *host, const char *hostname); static bool compare_hostname(const acl_host_and_ip *host, const char *hostname, const char *ip); -int acl_init(bool dont_read_acl_tables) +/* + Read grant privileges from the privilege tables in the 'mysql' database. + + SYNOPSIS + acl_init() + thd Thread handler + dont_read_acl_tables Set to 1 if run with --skip-grant + + RETURN VALUES + 0 ok + 1 Could not initialize grant's +*/ + + +my_bool acl_init(THD *org_thd, bool dont_read_acl_tables) { THD *thd; TABLE_LIST tables[3]; TABLE *table; READ_RECORD read_record_info; + MYSQL_LOCK *lock; + my_bool return_val=1; DBUG_ENTER("acl_init"); if (!acl_cache) acl_cache=new hash_filo(ACL_CACHE_SIZE,0,0, (hash_get_key) acl_entry_get_key, - (void (*)(void*)) free); + (hash_free_key) free); if (dont_read_acl_tables) DBUG_RETURN(0); /* purecov: tested */ + /* + To be able to run this from boot, we allocate a temporary THD + */ if (!(thd=new THD)) DBUG_RETURN(1); /* purecov: inspected */ + thd->store_globals(); + acl_cache->clear(1); // Clear locked hostname cache - thd->version=refresh_version; - thd->mysys_var=my_thread_var; - thd->current_tablenr=0; - thd->open_tables=0; - thd->db=my_strdup("mysql",MYF(0)); + thd->db= my_strdup("mysql",MYF(0)); + thd->db_length=5; // Safety bzero((char*) &tables,sizeof(tables)); tables[0].alias=tables[0].real_name=(char*) "host"; tables[1].alias=tables[1].real_name=(char*) "user"; @@ -140,22 +166,20 @@ int acl_init(bool dont_read_acl_tables) if (open_tables(thd,tables)) { - close_thread_tables(thd); /* purecov: inspected */ - delete thd; /* purecov: inspected */ - DBUG_RETURN(1); /* purecov: inspected */ + sql_print_error("Fatal error: Can't open privilege tables: %s", + thd->net.last_error); + goto end; } TABLE *ptr[3]; // Lock tables for quick update ptr[0]= tables[0].table; ptr[1]= tables[1].table; ptr[2]= tables[2].table; - MYSQL_LOCK *lock=mysql_lock_tables(thd,ptr,3); - if (!lock) + if (!(lock=mysql_lock_tables(thd,ptr,3))) { - close_thread_tables(thd); /* purecov: inspected */ - delete thd; /* purecov: inspected */ - DBUG_RETURN(1); /* purecov: inspected */ + sql_print_error("Fatal error: Can't lock privilege tables: %s", + thd->net.last_error); + goto end; } - init_sql_alloc(&mem,1024,0); init_read_record(&read_record_info,thd,table= tables[0].table,NULL,1,0); VOID(my_init_dynamic_array(&acl_hosts,sizeof(ACL_HOST),20,50)); @@ -163,15 +187,15 @@ int acl_init(bool dont_read_acl_tables) { ACL_HOST host; update_hostname(&host.host,get_field(&mem, table,0)); - host.db=get_field(&mem, table,1); - host.access=get_access(table,2); - host.access=fix_rights_for_db(host.access); - host.sort=get_sort(2,host.host.hostname,host.db); + host.db= get_field(&mem, table,1); + host.access= get_access(table,2); + host.access= fix_rights_for_db(host.access); + host.sort= get_sort(2,host.host.hostname,host.db); #ifndef TO_BE_REMOVED if (table->fields == 8) { // Without grant if (host.access & CREATE_ACL) - host.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL; + host.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_TMP_ACL; } #endif VOID(push_dynamic(&acl_hosts,(gptr) &host)); @@ -191,6 +215,7 @@ int acl_init(bool dont_read_acl_tables) protocol_version=9; /* purecov: tested */ } + DBUG_PRINT("info",("user table fields: %d",table->fields)); allow_all_hosts=0; while (!(read_record_info.read_record(&read_record_info))) { @@ -203,7 +228,7 @@ int acl_init(bool dont_read_acl_tables) protocol_version == PROTOCOL_VERSION) { sql_print_error( - "Found old style password for user '%s'. Ignoring user. (You may want to restart using --old-protocol)", + "Found old style password for user '%s'. Ignoring user. (You may want to restart mysqld using --old-protocol)", user.user ? user.user : ""); /* purecov: tested */ } else if (length % 8) // This holds true for passwords @@ -212,19 +237,57 @@ int acl_init(bool dont_read_acl_tables) "Found invalid password for user: '%s@%s'; Ignoring user", user.user ? user.user : "", user.host.hostname ? user.host.hostname : ""); /* purecov: tested */ - continue; /* purecov: tested */ + continue; /* purecov: tested */ } get_salt_from_password(user.salt,user.password); user.access=get_access(table,3) & GLOBAL_ACLS; user.sort=get_sort(2,user.host.hostname,user.user); - user.hostname_length=user.host.hostname ? (uint) strlen(user.host.hostname) : 0; -#ifndef TO_BE_REMOVED - if (table->fields <= 13) - { // Without grant - if (user.access & CREATE_ACL) - user.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL; + user.hostname_length= (user.host.hostname ? + (uint) strlen(user.host.hostname) : 0); + if (table->fields >= 31) /* Starting from 4.0.2 we have more fields */ + { + char *ssl_type=get_field(&mem, table, 24); + if (!ssl_type) + user.ssl_type=SSL_TYPE_NONE; + else if (!strcmp(ssl_type, "ANY")) + user.ssl_type=SSL_TYPE_ANY; + else if (!strcmp(ssl_type, "X509")) + user.ssl_type=SSL_TYPE_X509; + else /* !strcmp(ssl_type, "SPECIFIED") */ + user.ssl_type=SSL_TYPE_SPECIFIED; + + user.ssl_cipher= get_field(&mem, table, 25); + user.x509_issuer= get_field(&mem, table, 26); + user.x509_subject= get_field(&mem, table, 27); + + char *ptr = get_field(&mem, table, 28); + user.user_resource.questions=atoi(ptr); + ptr = get_field(&mem, table, 29); + user.user_resource.updates=atoi(ptr); + ptr = get_field(&mem, table, 30); + user.user_resource.connections=atoi(ptr); + if (user.user_resource.questions || user.user_resource.updates || + user.user_resource.connections) + mqh_used=1; } + else + { + user.ssl_type=SSL_TYPE_NONE; + bzero(&(user.user_resource),sizeof(user.user_resource)); +#ifndef TO_BE_REMOVED + if (table->fields <= 13) + { // Without grant + if (user.access & CREATE_ACL) + user.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL; + } + /* Convert old privileges */ + user.access|= LOCK_TABLES_ACL | CREATE_TMP_ACL | SHOW_DB_ACL; + if (user.access & FILE_ACL) + user.access|= REPL_CLIENT_ACL | REPL_SLAVE_ACL; + if (user.access & PROCESS_ACL) + user.access|= SUPER_ACL | EXECUTE_ACL; #endif + } VOID(push_dynamic(&acl_users,(gptr) &user)); if (!user.host.hostname || user.host.hostname[0] == wild_many && !user.host.hostname[1]) @@ -267,11 +330,21 @@ int acl_init(bool dont_read_acl_tables) init_check_host(); mysql_unlock_tables(thd, lock); + initialized=1; thd->version--; // Force close to free memory + return_val=0; + +end: close_thread_tables(thd); delete thd; - initialized=1; - DBUG_RETURN(0); + if (org_thd) + org_thd->store_globals(); /* purecov: inspected */ + else + { + /* Remember that we don't have a THD */ + my_pthread_setspecific_ptr(THR_THD, 0); + } + DBUG_RETURN(return_val); } @@ -294,18 +367,18 @@ void acl_free(bool end) /* Reload acl list if possible */ -void acl_reload(void) +void acl_reload(THD *thd) { DYNAMIC_ARRAY old_acl_hosts,old_acl_users,old_acl_dbs; MEM_ROOT old_mem; bool old_initialized; DBUG_ENTER("acl_reload"); - if (current_thd && current_thd->locked_tables) + if (thd && thd->locked_tables) { // Can't have locked tables here - current_thd->lock=current_thd->locked_tables; - current_thd->locked_tables=0; - close_thread_tables(current_thd); + thd->lock=thd->locked_tables; + thd->locked_tables=0; + close_thread_tables(thd); } if ((old_initialized=initialized)) VOID(pthread_mutex_lock(&acl_cache->lock)); @@ -317,9 +390,9 @@ void acl_reload(void) delete_dynamic(&acl_wild_hosts); hash_free(&acl_check_hosts); - if (acl_init(0)) + if (acl_init(thd, 0)) { // Error. Revert to old list - acl_free(); /* purecov: inspected */ + acl_free(); /* purecov: inspected */ acl_hosts=old_acl_hosts; acl_users=old_acl_users; acl_dbs=old_acl_dbs; @@ -339,31 +412,38 @@ void acl_reload(void) } -/* Get all access bits from table after fieldnr */ +/* + Get all access bits from table after fieldnr + We know that the access privileges ends when there is no more fields + or the field is not an enum with two elements. +*/ -static uint get_access(TABLE *form,uint fieldnr) +static ulong get_access(TABLE *form, uint fieldnr) { - uint access_bits=0,bit; + ulong access_bits=0,bit; char buff[2]; String res(buff,sizeof(buff)); Field **pos; - for (pos=form->field+fieldnr,bit=1 ; *pos ; pos++ , bit<<=1) + for (pos=form->field+fieldnr, bit=1; + *pos && (*pos)->real_type() == FIELD_TYPE_ENUM && + ((Field_enum*) (*pos))->typelib->count == 2 ; + pos++ , bit<<=1) { (*pos)->val_str(&res,&res); if (toupper(res[0]) == 'Y') - access_bits|=bit; + access_bits|= bit; } return access_bits; } /* - return a number which, if sorted 'desc', puts strings in this order: - no wildcards - wildcards - empty string - */ + Return a number which, if sorted 'desc', puts strings in this order: + no wildcards + wildcards + empty string +*/ static ulong get_sort(uint count,...) { @@ -403,18 +483,25 @@ static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b) } -/* Get master privilges for user (priviliges for all tables) */ - +/* + Get master privilges for user (priviliges for all tables). + Required before connecting to MySQL +*/ -uint acl_getroot(const char *host, const char *ip, const char *user, - const char *password,const char *message,char **priv_user, - bool old_ver) +ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user, + const char *password,const char *message,char **priv_user, + bool old_ver, USER_RESOURCES *mqh) { - uint user_access=NO_ACCESS; + ulong user_access=NO_ACCESS; *priv_user=(char*) user; + DBUG_ENTER("acl_getroot"); + bzero(mqh,sizeof(USER_RESOURCES)); if (!initialized) - return (uint) ~NO_ACCESS; // If no data allow anything /* purecov: tested */ + { + // If no data allow anything + DBUG_RETURN((ulong) ~NO_ACCESS); /* purecov: tested */ + } VOID(pthread_mutex_lock(&acl_cache->lock)); /* @@ -433,7 +520,90 @@ uint acl_getroot(const char *host, const char *ip, const char *user, !check_scramble(password,message,acl_user->salt, (my_bool) old_ver))) { +#ifdef HAVE_OPENSSL + Vio *vio=thd->net.vio; + /* + In this point we know that user is allowed to connect + from given host by given username/password pair. Now + we check if SSL is required, if user is using SSL and + if X509 certificate attributes are OK + */ + switch (acl_user->ssl_type) { + case SSL_TYPE_NOT_SPECIFIED: // Impossible + case SSL_TYPE_NONE: /* SSL is not required to connect */ + user_access=acl_user->access; + break; + case SSL_TYPE_ANY: /* Any kind of SSL is good enough */ + if (vio_type(vio) == VIO_TYPE_SSL) + user_access=acl_user->access; + break; + case SSL_TYPE_X509: /* Client should have any valid certificate. */ + /* + Connections with non-valid certificates are dropped already + in sslaccept() anyway, so we do not check validity here. + */ + if (SSL_get_peer_certificate(vio->ssl_)) + user_access=acl_user->access; + break; + case SSL_TYPE_SPECIFIED: /* Client should have specified attrib */ + /* + We do not check for absence of SSL because without SSL it does + not pass all checks here anyway. + If cipher name is specified, we compare it to actual cipher in + use. + */ + if (acl_user->ssl_cipher) + { + DBUG_PRINT("info",("comparing ciphers: '%s' and '%s'", + acl_user->ssl_cipher, + SSL_get_cipher(vio->ssl_))); + if (!strcmp(acl_user->ssl_cipher,SSL_get_cipher(vio->ssl_))) + user_access=acl_user->access; + else + { + user_access=NO_ACCESS; + break; + } + } + /* Prepare certificate (if exists) */ + DBUG_PRINT("info",("checkpoint 1")); + X509* cert=SSL_get_peer_certificate(vio->ssl_); + DBUG_PRINT("info",("checkpoint 2")); + /* If X509 issuer is speified, we check it... */ + if (acl_user->x509_issuer) + { + DBUG_PRINT("info",("checkpoint 3")); + char *ptr = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0); + DBUG_PRINT("info",("comparing issuers: '%s' and '%s'", + acl_user->x509_issuer, ptr)); + if (strcmp(acl_user->x509_issuer, ptr)) + { + user_access=NO_ACCESS; + free(ptr); + break; + } + user_access=acl_user->access; + free(ptr); + } + DBUG_PRINT("info",("checkpoint 4")); + /* X509 subject is specified, we check it .. */ + if (acl_user->x509_subject) + { + char *ptr= X509_NAME_oneline(X509_get_subject_name(cert), 0, 0); + DBUG_PRINT("info",("comparing subjects: '%s' and '%s'", + acl_user->x509_subject, ptr)); + if (strcmp(acl_user->x509_subject,ptr)) + user_access=NO_ACCESS; + else + user_access=acl_user->access; + free(ptr); + } + break; + } +#else /* HAVE_OPENSSL */ user_access=acl_user->access; +#endif /* HAVE_OPENSSL */ + *mqh=acl_user->user_resource; if (!acl_user->user) *priv_user=(char*) ""; // Change to anonymous user /* purecov: inspected */ break; @@ -445,7 +615,7 @@ uint acl_getroot(const char *host, const char *ip, const char *user, } } VOID(pthread_mutex_unlock(&acl_cache->lock)); - return user_access; + DBUG_RETURN(user_access); } @@ -462,7 +632,13 @@ static byte* check_get_key(ACL_USER *buff,uint *length, } static void acl_update_user(const char *user, const char *host, - const char *password, uint privileges) + const char *password, + enum SSL_type ssl_type, + const char *ssl_cipher, + const char *x509_issuer, + const char *x509_subject, + USER_RESOURCES *mqh, + ulong privileges) { for (uint i=0 ; i < acl_users.elements ; i++) { @@ -472,9 +648,26 @@ static void acl_update_user(const char *user, const char *host, !strcmp(user,acl_user->user)) { if (!acl_user->host.hostname && !host[0] || - acl_user->host.hostname && !strcmp(host,acl_user->host.hostname)) + acl_user->host.hostname && + !my_strcasecmp(host,acl_user->host.hostname)) { acl_user->access=privileges; + if (mqh->bits & 1) + acl_user->user_resource.questions=mqh->questions; + if (mqh->bits & 2) + acl_user->user_resource.updates=mqh->updates; + if (mqh->bits & 4) + acl_user->user_resource.connections=mqh->connections; + if (ssl_type != SSL_TYPE_NOT_SPECIFIED) + { + acl_user->ssl_type= ssl_type; + acl_user->ssl_cipher= (ssl_cipher ? strdup_root(&mem,ssl_cipher) : + 0); + acl_user->x509_issuer= (x509_issuer ? strdup_root(&mem,x509_issuer) : + 0); + acl_user->x509_subject= (x509_subject ? + strdup_root(&mem,x509_subject) : 0); + } if (password) { if (!password[0]) @@ -493,16 +686,27 @@ static void acl_update_user(const char *user, const char *host, static void acl_insert_user(const char *user, const char *host, - const char *password, - uint privileges) + const char *password, + enum SSL_type ssl_type, + const char *ssl_cipher, + const char *x509_issuer, + const char *x509_subject, + USER_RESOURCES *mqh, + ulong privileges) { ACL_USER acl_user; acl_user.user=strdup_root(&mem,user); update_hostname(&acl_user.host,strdup_root(&mem,host)); acl_user.password=0; acl_user.access=privileges; + acl_user.user_resource = *mqh; acl_user.sort=get_sort(2,acl_user.host.hostname,acl_user.user); acl_user.hostname_length=(uint) strlen(acl_user.host.hostname); + acl_user.ssl_type= (ssl_type != SSL_TYPE_NOT_SPECIFIED ? + ssl_type : SSL_TYPE_NONE); + acl_user.ssl_cipher= ssl_cipher ? strdup_root(&mem,ssl_cipher) : 0; + acl_user.x509_issuer= x509_issuer ? strdup_root(&mem,x509_issuer) : 0; + acl_user.x509_subject=x509_subject ? strdup_root(&mem,x509_subject) : 0; if (password) { acl_user.password=(char*) ""; // Just point at something @@ -524,7 +728,7 @@ static void acl_insert_user(const char *user, const char *host, static void acl_update_db(const char *user, const char *host, const char *db, - uint privileges) + ulong privileges) { for (uint i=0 ; i < acl_dbs.elements ; i++) { @@ -534,7 +738,7 @@ static void acl_update_db(const char *user, const char *host, const char *db, !strcmp(user,acl_db->user)) { if (!acl_db->host.hostname && !host[0] || - acl_db->host.hostname && !strcmp(host,acl_db->host.hostname)) + acl_db->host.hostname && !my_strcasecmp(host,acl_db->host.hostname)) { if (!acl_db->db && !db[0] || acl_db->db && !strcmp(db,acl_db->db)) @@ -550,11 +754,25 @@ static void acl_update_db(const char *user, const char *host, const char *db, } +/* + Insert a user/db/host combination into the global acl_cache + + SYNOPSIS + acl_insert_db() + user User name + host Host name + db Database name + privileges Bitmap of privileges + + NOTES + acl_cache->lock must be locked when calling this +*/ + static void acl_insert_db(const char *user, const char *host, const char *db, - uint privileges) + ulong privileges) { ACL_DB acl_db; - /* The acl_cache mutex is locked by mysql_grant */ + safe_mutex_assert_owner(&acl_cache->lock); acl_db.user=strdup_root(&mem,user); update_hostname(&acl_db.host,strdup_root(&mem,host)); acl_db.db=strdup_root(&mem,db); @@ -570,17 +788,23 @@ static void acl_insert_db(const char *user, const char *host, const char *db, ** Get privilege for a host, user and db combination *****************************************************************************/ -uint acl_get(const char *host, const char *ip, const char *bin_ip, +ulong acl_get(const char *host, const char *ip, const char *bin_ip, const char *user, const char *db) { - uint host_access,db_access,i,key_length; + ulong host_access,db_access; + uint i,key_length; db_access=0; host_access= ~0; - char key[ACL_KEY_LENGTH],*end; + char key[ACL_KEY_LENGTH],*tmp_db,*end; acl_entry *entry; VOID(pthread_mutex_lock(&acl_cache->lock)); memcpy_fixed(&key,bin_ip,sizeof(struct in_addr)); - end=strmov(strmov(key+sizeof(struct in_addr),user)+1,db); + end=strmov((tmp_db=strmov(key+sizeof(struct in_addr),user)+1),db); + if (lower_case_table_names) + { + casedn_str(tmp_db); + db=tmp_db; + } key_length=(uint) (end-key); if ((entry=(acl_entry*) acl_cache->search(key,key_length))) { @@ -646,7 +870,7 @@ int wild_case_compare(const char *str,const char *wildstr) { reg3 int flag; DBUG_ENTER("wild_case_compare"); - + DBUG_PRINT("enter",("str: '%s' wildstr: '%s'",str,wildstr)); while (*wildstr) { while (*wildstr && *wildstr != wild_many && *wildstr != wild_one) @@ -658,7 +882,7 @@ int wild_case_compare(const char *str,const char *wildstr) if (! *wildstr ) DBUG_RETURN (*str != 0); if (*wildstr++ == wild_one) { - if (! *str++) DBUG_RETURN (1); /* One char; skipp */ + if (! *str++) DBUG_RETURN (1); /* One char; skip */ } else { /* Found '*' */ @@ -762,44 +986,85 @@ bool acl_check_host(const char *host, const char *ip) } /***************************************************************************** -** Change password for the user if it's not an anonymous user -** Note: This should write the error directly to the client! + Change password for the user if it's not an anonymous user + Note: This should write the error directly to the client! *****************************************************************************/ -bool change_password(THD *thd, const char *host, const char *user, - char *new_password) +/* + Check if the user is allowed to change password + + SYNOPSIS: + check_change_password() + thd THD + host hostname for the user + user user name + + RETURN VALUE + 0 OK + 1 ERROR ; In this case the error is sent to the client. +*/ + +bool check_change_password(THD *thd, const char *host, const char *user) { - uint length=0; if (!initialized) { send_error(&thd->net, ER_PASSWORD_NOT_ALLOWED); /* purecov: inspected */ - return 1; /* purecov: inspected */ + return(1); /* purecov: inspected */ } - if (!host) - host=thd->ip; /* purecov: tested */ - /* password should always be 0 or 16 chars; simple hack to avoid cracking */ - length=(uint) strlen(new_password); - new_password[length & 16]=0; - if (!thd->slave_thread && (strcmp(thd->user,user) || my_strcasecmp(host,thd->host ? thd->host : thd->ip))) { if (check_access(thd, UPDATE_ACL, "mysql",0,1)) - return 1; + return(1); } if (!thd->slave_thread && !thd->user[0]) { send_error(&thd->net, ER_PASSWORD_ANONYMOUS_USER); - return 1; + return(1); } + return(0); +} + + +/* + Change a password for a user + + SYNOPSIS + change_password() + thd Thread handle + host Hostname + user User name + new_password New password for host@user + + RETURN VALUES + 0 ok + 1 ERROR; In this case the error is sent to the client. +*/ + +bool change_password(THD *thd, const char *host, const char *user, + char *new_password) +{ + uint length=0; + DBUG_ENTER("change_password"); + DBUG_PRINT("enter",("host: '%s' user: '%s' new_password: '%s'", + host,user,new_password)); + DBUG_ASSERT(host != 0); // Ensured by parent + + if (check_change_password(thd, host, user)) + DBUG_RETURN(1); + + /* password should always be 0 or 16 chars; simple hack to avoid cracking */ + length=(uint) strlen(new_password); + new_password[length & 16]=0; + VOID(pthread_mutex_lock(&acl_cache->lock)); ACL_USER *acl_user; if (!(acl_user= find_acl_user(host,user))) { send_error(&thd->net, ER_PASSWORD_NO_MATCH); VOID(pthread_mutex_unlock(&acl_cache->lock)); - return 1; + DBUG_RETURN(1); } if (update_user_table(thd, acl_user->host.hostname ? acl_user->host.hostname : "", @@ -808,7 +1073,7 @@ bool change_password(THD *thd, const char *host, const char *user, { VOID(pthread_mutex_unlock(&acl_cache->lock)); /* purecov: deadcode */ send_error(&thd->net,0); /* purecov: deadcode */ - return 1; /* purecov: deadcode */ + DBUG_RETURN(1); /* purecov: deadcode */ } get_salt_from_password(acl_user->salt,new_password); if (!new_password[0]) @@ -819,17 +1084,16 @@ bool change_password(THD *thd, const char *host, const char *user, VOID(pthread_mutex_unlock(&acl_cache->lock)); char buff[460]; - - Query_log_event qinfo(thd, buff); - qinfo.q_len = + ulong query_length= my_sprintf(buff, (buff,"SET PASSWORD FOR \"%-.120s\"@\"%-.120s\"=\"%-.120s\"", acl_user->user ? acl_user->user : "", acl_user->host.hostname ? acl_user->host.hostname : "", new_password)); - mysql_update_log.write(thd,buff,qinfo.q_len); + mysql_update_log.write(thd, buff, query_length); + Query_log_event qinfo(thd, buff, query_length, 0); mysql_bin_log.write(&qinfo); - return 0; + DBUG_RETURN(0); } @@ -840,17 +1104,23 @@ bool change_password(THD *thd, const char *host, const char *user, static ACL_USER * find_acl_user(const char *host, const char *user) { + DBUG_ENTER("find_acl_user"); + DBUG_PRINT("enter",("host: '%s' user: '%s'",host,user)); for (uint i=0 ; i < acl_users.elements ; i++) { ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*); + DBUG_PRINT("info",("strcmp('%s','%s'), compare_hostname('%s','%s'),", + user,acl_user->user,(host),(acl_user->host))); if (!acl_user->user && !user[0] || acl_user->user && !strcmp(user,acl_user->user)) { - if (compare_hostname(&acl_user->host,host,host)) - return acl_user; + if (compare_hostname(&(acl_user->host),host,host)) + { + DBUG_RETURN(acl_user); + } } } - return 0; + DBUG_RETURN(0); } /***************************************************************************** @@ -909,7 +1179,7 @@ static bool compare_hostname(const acl_host_and_ip *host, const char *hostname, /**************************************************************************** -** Code to update grants in the user and database privilege tables + Code to update grants in the user and database privilege tables ****************************************************************************/ static bool update_user_table(THD *thd, const char *host, const char *user, @@ -959,10 +1229,10 @@ static bool test_if_create_new_users(THD *thd) if (opt_safe_user_create && !(thd->master_access & INSERT_ACL)) { TABLE_LIST tl; - uint db_access; + ulong db_access; bzero((char*) &tl,sizeof(tl)); tl.db= (char*) "mysql"; - tl.real_name= (char*) "user"; + tl.real_name= (char*) "user"; db_access=acl_get(thd->host, thd->ip, (char*) &thd->remote.sin_addr, thd->priv_user, tl.db); if (!(db_access & INSERT_ACL)) @@ -979,14 +1249,16 @@ static bool test_if_create_new_users(THD *thd) ** Handle GRANT commands ****************************************************************************/ -static int replace_user_table(TABLE *table, const LEX_USER &combo, - uint rights, char what, bool create_user) +static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo, + ulong rights, bool revoke_grant, + bool create_user) { int error = -1; - uint i,j; bool old_row_exists=0; char *password,empty_string[1]; + char what= (revoke_grant) ? 'N' : 'Y'; DBUG_ENTER("replace_user_table"); + safe_mutex_assert_owner(&acl_cache->lock); password=empty_string; empty_string[0]=0; @@ -1012,7 +1284,6 @@ static int replace_user_table(TABLE *table, const LEX_USER &combo, { if (!create_user) { - THD *thd=current_thd; if (what == 'N') my_printf_error(ER_NONEXISTING_GRANT,ER(ER_NONEXISTING_GRANT), MYF(0),combo.user.str,combo.host.str); @@ -1020,7 +1291,7 @@ static int replace_user_table(TABLE *table, const LEX_USER &combo, my_printf_error(ER_NO_PERMISSION_TO_CREATE_USER, ER(ER_NO_PERMISSION_TO_CREATE_USER), MYF(0),thd->user, - thd->host ? thd->host : thd->ip ? thd->ip: ""); + thd->host_or_ip); error= -1; goto end; } @@ -1038,15 +1309,70 @@ static int replace_user_table(TABLE *table, const LEX_USER &combo, table->field[2]->store(password,(uint) strlen(password)); } - for (i = 3, j = SELECT_ACL; // starting from reload - i < table->fields; - i++, j <<= 1) + /* Update table columns with new privileges */ + + Field **tmp_field; + ulong priv; + for (tmp_field= table->field+3, priv = SELECT_ACL; + *tmp_field && (*tmp_field)->real_type() == FIELD_TYPE_ENUM && + ((Field_enum*) (*tmp_field))->typelib->count == 2 ; + tmp_field++, priv <<= 1) { - if (j & rights) // set requested privileges - table->field[i]->store(&what,1); + if (priv & rights) // set requested privileges + (*tmp_field)->store(&what,1); } rights=get_access(table,3); + DBUG_PRINT("info",("table->fields: %d",table->fields)); + if (table->fields >= 31) /* From 4.0.0 we have more fields */ + { + /* We write down SSL related ACL stuff */ + switch (thd->lex.ssl_type) { + case SSL_TYPE_ANY: + table->field[24]->store("ANY",3); + table->field[25]->store("",0); + table->field[26]->store("",0); + table->field[27]->store("",0); + break; + case SSL_TYPE_X509: + table->field[24]->store("X509",4); + table->field[25]->store("",0); + table->field[26]->store("",0); + table->field[27]->store("",0); + break; + case SSL_TYPE_SPECIFIED: + table->field[24]->store("SPECIFIED",9); + table->field[25]->store("",0); + table->field[26]->store("",0); + table->field[27]->store("",0); + if (thd->lex.ssl_cipher) + table->field[25]->store(thd->lex.ssl_cipher, + strlen(thd->lex.ssl_cipher)); + if (thd->lex.x509_issuer) + table->field[26]->store(thd->lex.x509_issuer, + strlen(thd->lex.x509_issuer)); + if (thd->lex.x509_subject) + table->field[27]->store(thd->lex.x509_subject, + strlen(thd->lex.x509_subject)); + break; + case SSL_TYPE_NOT_SPECIFIED: + break; + case SSL_TYPE_NONE: + table->field[24]->store("",0); + table->field[25]->store("",0); + table->field[26]->store("",0); + table->field[27]->store("",0); + break; + } + USER_RESOURCES mqh = thd->lex.mqh; + if (mqh.bits & 1) + table->field[28]->store((longlong) mqh.questions); + if (mqh.bits & 2) + table->field[29]->store((longlong) mqh.updates); + if (mqh.bits & 4) + table->field[30]->store((longlong) mqh.connections); + mqh_used = mqh_used || mqh.questions || mqh.updates || mqh.connections; + } if (old_row_exists) { /* @@ -1073,16 +1399,28 @@ static int replace_user_table(TABLE *table, const LEX_USER &combo, } error=0; // Privileges granted / revoked - end: +end: if (!error) { acl_cache->clear(1); // Clear privilege cache if (!combo.password.str) password=0; // No password given on command if (old_row_exists) - acl_update_user(combo.user.str,combo.host.str,password,rights); + acl_update_user(combo.user.str,combo.host.str,password, + thd->lex.ssl_type, + thd->lex.ssl_cipher, + thd->lex.x509_issuer, + thd->lex.x509_subject, + &thd->lex.mqh, + rights); else - acl_insert_user(combo.user.str,combo.host.str,password,rights); + acl_insert_user(combo.user.str,combo.host.str,password, + thd->lex.ssl_type, + thd->lex.ssl_cipher, + thd->lex.x509_issuer, + thd->lex.x509_subject, + &thd->lex.mqh, + rights); } table->file->index_end(); DBUG_RETURN(error); @@ -1090,16 +1428,18 @@ static int replace_user_table(TABLE *table, const LEX_USER &combo, /* -** change grants in the mysql.db table + change grants in the mysql.db table */ static int replace_db_table(TABLE *table, const char *db, const LEX_USER &combo, - uint rights, char what) + ulong rights, bool revoke_grant) { - uint i,j,store_rights; + uint i; + ulong priv,store_rights; bool old_row_exists=0; int error; + char what= (revoke_grant) ? 'N' : 'Y'; DBUG_ENTER("replace_db_table"); // is there such a user in user table in memory ???? @@ -1135,9 +1475,9 @@ static int replace_db_table(TABLE *table, const char *db, } store_rights=get_rights_for_db(rights); - for (i = 3, j = 1; i < table->fields; i++, j <<= 1) + for (i= 3, priv= 1; i < table->fields; i++, priv <<= 1) { - if (j & store_rights) // do it if priv is chosen + if (priv & store_rights) // do it if priv is chosen table->field [i]->store(&what,1); // set requested privileges } rights=get_access(table,3); @@ -1185,13 +1525,15 @@ class GRANT_COLUMN :public Sql_alloc { public: char *column; - uint rights, key_length; - GRANT_COLUMN(String &c, uint y) :rights (y) + ulong rights; + uint key_length; + GRANT_COLUMN(String &c, ulong y) :rights (y) { - column= memdup_root(&memex,c.ptr(),key_length=c.length()); + column= memdup_root(&memex,c.ptr(), key_length=c.length()); } }; + static byte* get_key_column(GRANT_COLUMN *buff,uint *length, my_bool not_used __attribute__((unused))) { @@ -1199,20 +1541,27 @@ static byte* get_key_column(GRANT_COLUMN *buff,uint *length, return (byte*) buff->column; } + class GRANT_TABLE :public Sql_alloc { public: char *host,*db,*user,*tname, *hash_key; - uint privs, cols, key_length; + ulong privs, cols; + uint key_length; HASH hash_columns; GRANT_TABLE (const char *h, const char *d,const char *u, const char *t, - uint p,uint c) + ulong p, ulong c) : privs(p), cols(c) { host = strdup_root(&memex,h); db = strdup_root(&memex,d); user = strdup_root(&memex,u); tname= strdup_root(&memex,t); + if (lower_case_table_names) + { + casedn_str(db); + casedn_str(tname); + } key_length =(uint) strlen(d)+(uint) strlen(u)+(uint) strlen(t)+3; hash_key = (char*) alloc_root(&memex,key_length); strmov(strmov(strmov(hash_key,user)+1,db)+1,tname); @@ -1226,7 +1575,9 @@ public: host = get_field(&memex,form,0); db = get_field(&memex,form,1); - user = get_field(&memex,form,2); if (!user) user=(char*) ""; + user = get_field(&memex,form,2); + if (!user) + user=(char*) ""; tname = get_field(&memex,form,3); if (!host || !db || !tname) { @@ -1234,11 +1585,17 @@ public: privs = cols = 0; /* purecov: inspected */ return; /* purecov: inspected */ } - key_length = (uint) strlen(db) + (uint) strlen(user) + (uint) strlen (tname) + 3; + if (lower_case_table_names) + { + casedn_str(db); + casedn_str(tname); + } + key_length = ((uint) strlen(db) + (uint) strlen(user) + + (uint) strlen(tname) + 3); hash_key = (char*) alloc_root(&memex,key_length); strmov(strmov(strmov(hash_key,user)+1,db)+1,tname); - privs = (uint) form->field[6]->val_int(); - cols = (uint) form->field[7]->val_int(); + privs = (ulong) form->field[6]->val_int(); + cols = (ulong) form->field[7]->val_int(); privs = fix_rights_for_table(privs); cols = fix_rights_for_column(cols); @@ -1271,7 +1628,7 @@ public: GRANT_COLUMN *mem_check; /* As column name is a string, we don't have to supply a buffer */ res=col_privs->field[4]->val_str(&column_name,&column_name); - uint priv= (uint) col_privs->field[6]->val_int(); + ulong priv= (ulong) col_privs->field[6]->val_int(); if (!(mem_check = new GRANT_COLUMN(*res, fix_rights_for_column(priv)))) { @@ -1287,6 +1644,7 @@ public: bool ok() { return privs != 0 || cols != 0; } }; + static byte* get_grant_table(GRANT_TABLE *buff,uint *length, my_bool not_used __attribute__((unused))) { @@ -1294,11 +1652,13 @@ static byte* get_grant_table(GRANT_TABLE *buff,uint *length, return (byte*) buff->hash_key; } + void free_grant_table(GRANT_TABLE *grant_table) { hash_free(&grant_table->hash_columns); } + /* Search after a matching grant. Prefer exact grants before not exact ones */ static GRANT_TABLE *table_hash_search(const char *host,const char* ip, @@ -1309,6 +1669,7 @@ static GRANT_TABLE *table_hash_search(const char *host,const char* ip, char helping [NAME_LEN*2+USERNAME_LENGTH+3]; uint len; GRANT_TABLE *grant_table,*found=0; + safe_mutex_assert_owner(&LOCK_grant); len = (uint) (strmov(strmov(strmov(helping,user)+1,db)+1,tname)-helping)+ 1; for (grant_table=(GRANT_TABLE*) hash_search(&hash_tables,(byte*) helping, @@ -1318,7 +1679,7 @@ static GRANT_TABLE *table_hash_search(const char *host,const char* ip, { if (exact) { - if ((host && !strcmp(host,grant_table->host)) || + if ((host && !my_strcasecmp(host,grant_table->host)) || (ip && !strcmp(ip,grant_table->host))) return grant_table; } @@ -1335,8 +1696,7 @@ static GRANT_TABLE *table_hash_search(const char *host,const char* ip, inline GRANT_COLUMN * -column_hash_search(GRANT_TABLE *t, const char *cname, - uint length) +column_hash_search(GRANT_TABLE *t, const char *cname, uint length) { return (GRANT_COLUMN*) hash_search(&t->hash_columns, (byte*) cname,length); } @@ -1346,7 +1706,7 @@ static int replace_column_table(GRANT_TABLE *g_t, TABLE *table, const LEX_USER &combo, List <LEX_COLUMN> &columns, const char *db, const char *table_name, - uint rights, bool revoke_grant) + ulong rights, bool revoke_grant) { int error=0,result=0; uint key_length; @@ -1370,7 +1730,7 @@ static int replace_column_table(GRANT_TABLE *g_t, table->file->index_init(0); while ((xx=iter++)) { - uint privileges = xx->rights; + ulong privileges = xx->rights; bool old_row_exists=0; key_restore(table,key,0,key_length); table->field[4]->store(xx->column.ptr(),xx->column.length()); @@ -1393,7 +1753,7 @@ static int replace_column_table(GRANT_TABLE *g_t, } else { - uint tmp= (uint) table->field[6]->val_int(); + ulong tmp= (ulong) table->field[6]->val_int(); tmp=fix_rights_for_column(tmp); if (revoke_grant) @@ -1453,7 +1813,7 @@ static int replace_column_table(GRANT_TABLE *g_t, // Scan trough all rows with the same host,db,user and table do { - uint privileges = (uint) table->field[6]->val_int(); + ulong privileges = (ulong) table->field[6]->val_int(); privileges=fix_rights_for_column(privileges); store_record(table,1); @@ -1509,19 +1869,22 @@ static int replace_column_table(GRANT_TABLE *g_t, static int replace_table_table(THD *thd, GRANT_TABLE *grant_table, TABLE *table, const LEX_USER &combo, const char *db, const char *table_name, - uint rights, uint kolone, bool revoke_grant) + ulong rights, ulong col_rights, + bool revoke_grant) { - char grantor[HOSTNAME_LENGTH+1+USERNAME_LENGTH]; + char grantor[HOSTNAME_LENGTH+USERNAME_LENGTH+2]; int old_row_exists = 1; int error=0; - uint store_table_rights,store_col_rights; + ulong store_table_rights, store_col_rights; DBUG_ENTER("replace_table_table"); + safe_mutex_assert_owner(&LOCK_grant); - strxmov(grantor,thd->user,"@",thd->host ? thd->host : thd->ip ? thd->ip :"", - NullS); + strxmov(grantor, thd->user, "@", thd->host_or_ip, NullS); - // The following should always succeed as new users are created before - // this function is called! + /* + The following should always succeed as new users are created before + this function is called! + */ if (!find_acl_user(combo.host.str,combo.user.str)) { my_error(ER_PASSWORD_NO_MATCH,MYF(0)); /* purecov: deadcode */ @@ -1556,14 +1919,14 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table, restore_record(table,1); // Get saved record } - store_table_rights=get_rights_for_table(rights); - store_col_rights=get_rights_for_column(kolone); + store_table_rights= get_rights_for_table(rights); + store_col_rights= get_rights_for_column(col_rights); if (old_row_exists) { - uint j,k; + ulong j,k; store_record(table,1); - j = (uint) table->field[6]->val_int(); - k = (uint) table->field[7]->val_int(); + j = (ulong) table->field[6]->val_int(); + k = (ulong) table->field[7]->val_int(); if (revoke_grant) { @@ -1572,8 +1935,8 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table, } else { - store_table_rights|=j; - store_col_rights|=k; + store_table_rights|= j; + store_col_rights|= k; } } @@ -1581,7 +1944,7 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table, table->field[6]->store((longlong) store_table_rights); table->field[7]->store((longlong) store_col_rights); rights=fix_rights_for_table(store_table_rights); - kolone=fix_rights_for_column(store_col_rights); + col_rights=fix_rights_for_column(store_col_rights); if (old_row_exists) { @@ -1600,10 +1963,10 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table, goto table_error; /* purecov: deadcode */ } - if (rights | kolone) + if (rights | col_rights) { - grant_table->privs = rights; - grant_table->cols = kolone; + grant_table->privs= rights; + grant_table->cols= col_rights; } else { @@ -1620,10 +1983,10 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table, int mysql_table_grant (THD *thd, TABLE_LIST *table_list, List <LEX_USER> &user_list, - List <LEX_COLUMN> &columns, uint rights, + List <LEX_COLUMN> &columns, ulong rights, bool revoke_grant) { - uint column_priv = 0; + ulong column_priv = 0; List_iterator <LEX_USER> str_list (user_list); LEX_USER *Str; TABLE_LIST tables[3]; @@ -1655,7 +2018,7 @@ int mysql_table_grant (THD *thd, TABLE_LIST *table_list, check->column.length(),0,0)) { my_printf_error(ER_BAD_FIELD_ERROR,ER(ER_BAD_FIELD_ERROR),MYF(0), - check->column.c_ptr(),table_list->alias); + check->column.c_ptr(), table_list->alias); DBUG_RETURN(-1); } column_priv |= check->rights | (rights & COL_ACLS); @@ -1665,12 +2028,12 @@ int mysql_table_grant (THD *thd, TABLE_LIST *table_list, else if (!(rights & CREATE_ACL) && !revoke_grant) { char buf[FN_REFLEN]; - sprintf(buf,"%s/%s/%s.frm",mysql_data_home,table_list->db, + sprintf(buf,"%s/%s/%s.frm",mysql_data_home, table_list->db, table_list->real_name); fn_format(buf,buf,"","",4+16+32); if (access(buf,F_OK)) { - my_error(ER_NO_SUCH_TABLE,MYF(0),table_list->db,table_list->real_name); + my_error(ER_NO_SUCH_TABLE,MYF(0),table_list->db, table_list->alias); DBUG_RETURN(-1); } } @@ -1720,11 +2083,8 @@ int mysql_table_grant (THD *thd, TABLE_LIST *table_list, } /* Create user if needed */ pthread_mutex_lock(&acl_cache->lock); - error=replace_user_table(tables[0].table, - *Str, - 0, - revoke_grant ? 'N' : 'Y', - create_new_users); + error=replace_user_table(thd, tables[0].table, *Str, + 0, revoke_grant, create_new_users); pthread_mutex_unlock(&acl_cache->lock); if (error) { @@ -1817,17 +2177,17 @@ int mysql_table_grant (THD *thd, TABLE_LIST *table_list, pthread_mutex_unlock(&LOCK_grant); if (!result) send_ok(&thd->net); - /* Tables are automaticly closed */ + /* Tables are automatically closed */ DBUG_RETURN(result); } -int mysql_grant (THD *thd, const char *db, List <LEX_USER> &list, uint rights, - bool revoke_grant) +int mysql_grant (THD *thd, const char *db, List <LEX_USER> &list, + ulong rights, bool revoke_grant) { List_iterator <LEX_USER> str_list (list); LEX_USER *Str; - char what; + char tmp_db[NAME_LEN+1]; bool create_new_users=0; TABLE_LIST tables[2]; DBUG_ENTER("mysql_grant"); @@ -1838,7 +2198,12 @@ int mysql_grant (THD *thd, const char *db, List <LEX_USER> &list, uint rights, return 1; /* purecov: tested */ } - what = (revoke_grant) ? 'N' : 'Y'; + if (lower_case_table_names && db) + { + strmov(tmp_db,db); + casedn_str(tmp_db); + db=tmp_db; + } /* open the mysql.user and mysql.db tables */ @@ -1878,14 +2243,16 @@ int mysql_grant (THD *thd, const char *db, List <LEX_USER> &list, uint rights, result= -1; continue; } - if ((replace_user_table(tables[0].table, + if ((replace_user_table(thd, + tables[0].table, *Str, - (!db ? rights : 0), what, create_new_users))) + (!db ? rights : 0), revoke_grant, + create_new_users))) result= -1; else { if (db && replace_db_table(tables[1].table, db, *Str, rights & DB_ACLS, - what)) + revoke_grant)) result= -1; } } @@ -1898,7 +2265,8 @@ int mysql_grant (THD *thd, const char *db, List <LEX_USER> &list, uint rights, DBUG_RETURN(result); } - /* Free grant array if possible */ + +/* Free grant array if possible */ void grant_free(void) { @@ -1912,11 +2280,12 @@ void grant_free(void) /* Init grant array if possible */ -int grant_init (void) +my_bool grant_init(THD *org_thd) { THD *thd; TABLE_LIST tables[2]; - int error = 0; + MYSQL_LOCK *lock; + my_bool return_val= 1; TABLE *t_table, *c_table; DBUG_ENTER("grant_init"); @@ -1925,16 +2294,15 @@ int grant_init (void) (hash_free_key) free_grant_table,0); init_sql_alloc(&memex,1024,0); + /* Don't do anything if running with --skip-grant */ if (!initialized) DBUG_RETURN(0); /* purecov: tested */ + if (!(thd=new THD)) DBUG_RETURN(1); /* purecov: deadcode */ - - thd->version=refresh_version; - thd->mysys_var=my_thread_var; - thd->current_tablenr=0; - thd->open_tables=0; - thd->db=my_strdup("mysql",MYF(0)); + thd->store_globals(); + thd->db= my_strdup("mysql",MYF(0)); + thd->db_length=5; // Safety bzero((char*) &tables,sizeof(tables)); tables[0].alias=tables[0].real_name= (char*) "tables_priv"; tables[1].alias=tables[1].real_name= (char*) "columns_priv"; @@ -1943,66 +2311,63 @@ int grant_init (void) tables[0].db=tables[1].db=thd->db; if (open_tables(thd,tables)) - { // No grant tables - close_thread_tables(thd); /* purecov: deadcode */ - delete thd; /* purecov: deadcode */ - DBUG_RETURN(1); /* purecov: deadcode */ - } + goto end; + TABLE *ptr[2]; // Lock tables for quick update ptr[0]= tables[0].table; ptr[1]= tables[1].table; - MYSQL_LOCK *lock=mysql_lock_tables(thd,ptr,2); - if (!lock) - { - close_thread_tables(thd); /* purecov: deadcode */ - delete thd; /* purecov: deadcode */ - DBUG_RETURN(1); /* purecov: deadcode */ - } + if (!(lock=mysql_lock_tables(thd,ptr,2))) + goto end; t_table = tables[0].table; c_table = tables[1].table; t_table->file->index_init(0); if (t_table->file->index_first(t_table->record[0])) { t_table->file->index_end(); - mysql_unlock_tables(thd, lock); - thd->version--; // Force close to free memory - close_thread_tables(thd); - delete thd; - DBUG_RETURN(0); // Empty table is ok! + return_val= 0; + goto end_unlock; } - grant_option = TRUE; + grant_option= TRUE; t_table->file->index_end(); - MEM_ROOT *old_root=my_pthread_getspecific_ptr(MEM_ROOT*,THR_MALLOC); + /* Will be restored by org_thd->store_globals() */ my_pthread_setspecific_ptr(THR_MALLOC,&memex); - while (!error) + do { GRANT_TABLE *mem_check; if (!(mem_check=new GRANT_TABLE(t_table,c_table)) || mem_check->ok() && hash_insert(&hash_tables,(byte*) mem_check)) { /* This could only happen if we are out memory */ - my_pthread_setspecific_ptr(THR_MALLOC,old_root); /* purecov: deadcode */ grant_option = FALSE; /* purecov: deadcode */ - mysql_unlock_tables(thd, lock); /* purecov: deadcode */ - close_thread_tables(thd); /* purecov: deadcode */ - delete thd; /* purecov: deadcode */ - DBUG_RETURN(1); /* purecov: deadcode */ + goto end_unlock; } - error = t_table->file->index_next(t_table->record[0]); } - my_pthread_setspecific_ptr(THR_MALLOC,old_root); + while (!t_table->file->index_next(t_table->record[0])); + + return_val=0; // Return ok + +end_unlock: mysql_unlock_tables(thd, lock); thd->version--; // Force close to free memory + +end: close_thread_tables(thd); delete thd; - DBUG_RETURN(0); + if (org_thd) + org_thd->store_globals(); + else + { + /* Remember that we don't have a THD */ + my_pthread_setspecific_ptr(THR_THD, 0); + } + DBUG_RETURN(return_val); } /* Reload grant array if possible */ -void grant_reload(void) +void grant_reload(THD *thd) { HASH old_hash_tables;bool old_grant_option; MEM_ROOT old_mem; @@ -2016,7 +2381,7 @@ void grant_reload(void) old_grant_option = grant_option; old_mem = memex; - if (grant_init()) + if (grant_init(thd)) { // Error. Revert to old hash grant_free(); /* purecov: deadcode */ hash_tables=old_hash_tables; /* purecov: deadcode */ @@ -2034,11 +2399,11 @@ void grant_reload(void) /**************************************************************************** -** Check grants -** All errors are written directly to the client if command name is given ! + Check grants + All errors are written directly to the client if command name is given ! ****************************************************************************/ -bool check_grant(THD *thd, uint want_access, TABLE_LIST *tables, +bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, uint show_table, bool no_errors) { TABLE_LIST *table; @@ -2056,8 +2421,8 @@ bool check_grant(THD *thd, uint want_access, TABLE_LIST *tables, table->grant.want_privilege=0; continue; // Already checked } - const char *db = table->db ? table->db : thd->db; - GRANT_TABLE *grant_table = table_hash_search(thd->host,thd->ip,db,user, + GRANT_TABLE *grant_table = table_hash_search(thd->host,thd->ip, + table->db,user, table->real_name,0); if (!grant_table) { @@ -2111,7 +2476,7 @@ bool check_grant(THD *thd, uint want_access, TABLE_LIST *tables, net_printf(&thd->net,ER_TABLEACCESS_DENIED_ERROR, command, thd->priv_user, - thd->host ? thd->host : (thd->ip ? thd->ip : "unknown"), + thd->host_or_ip, table ? table->real_name : "unknown"); } return 1; @@ -2124,7 +2489,7 @@ bool check_grant_column (THD *thd,TABLE *table, const char *name, GRANT_TABLE *grant_table; GRANT_COLUMN *grant_column; - uint want_access=table->grant.want_privilege; + ulong want_access=table->grant.want_privilege; if (!want_access) return 0; // Already checked @@ -2162,19 +2527,14 @@ bool check_grant_column (THD *thd,TABLE *table, const char *name, pthread_mutex_unlock(&LOCK_grant); if (!show_tables) { - const char *command=""; - if (want_access & SELECT_ACL) - command ="select"; - else if (want_access & INSERT_ACL) - command = "insert"; - else if (want_access & UPDATE_ACL) - command = "update"; + char command[128]; + get_privilege_desc(command, sizeof(command), want_access); my_printf_error(ER_COLUMNACCESS_DENIED_ERROR, ER(ER_COLUMNACCESS_DENIED_ERROR), MYF(0), command, thd->priv_user, - thd->host ? thd->host : (thd->ip ? thd->ip : "unknown"), + thd->host_or_ip, name, table ? table->real_name : "unknown"); } @@ -2182,7 +2542,7 @@ bool check_grant_column (THD *thd,TABLE *table, const char *name, } -bool check_grant_all_columns(THD *thd,uint want_access, TABLE *table) +bool check_grant_all_columns(THD *thd, ulong want_access, TABLE *table) { GRANT_TABLE *grant_table; GRANT_COLUMN *grant_column; @@ -2232,7 +2592,7 @@ bool check_grant_all_columns(THD *thd,uint want_access, TABLE *table) MYF(0), command, thd->priv_user, - thd->host ? thd->host : (thd->ip ? thd->ip : "unknown"), + thd->host_or_ip, field ? field->field_name : "unknown", table->real_name); return 1; @@ -2240,9 +2600,9 @@ bool check_grant_all_columns(THD *thd,uint want_access, TABLE *table) /**************************************************************************** -** Check if a user has the right to access a database -** Access is accepted if he has a grant for any table in the database -** Return 1 if access is denied + Check if a user has the right to access a database + Access is accepted if he has a grant for any table in the database + Return 1 if access is denied ****************************************************************************/ bool check_grant_db(THD *thd,const char *db) @@ -2271,10 +2631,10 @@ bool check_grant_db(THD *thd,const char *db) } /***************************************************************************** -** Functions to retrieve the grant for a table/column (for SHOW functions) + Functions to retrieve the grant for a table/column (for SHOW functions) *****************************************************************************/ -uint get_table_grant(THD *thd, TABLE_LIST *table) +ulong get_table_grant(THD *thd, TABLE_LIST *table) { uint privilege; char *user = thd->priv_user; @@ -2294,11 +2654,11 @@ uint get_table_grant(THD *thd, TABLE_LIST *table) } -uint get_column_grant(THD *thd, TABLE_LIST *table, Field *field) +ulong get_column_grant(THD *thd, TABLE_LIST *table, Field *field) { GRANT_TABLE *grant_table; GRANT_COLUMN *grant_column; - uint priv; + ulong priv; pthread_mutex_lock(&LOCK_grant); // reload table if someone has modified any grants @@ -2328,18 +2688,27 @@ uint get_column_grant(THD *thd, TABLE_LIST *table, Field *field) /***************************************************************************** -** SHOW GRANTS : send to client grant-like strings depicting user@host -** privileges + SHOW GRANTS : send to client grant-like strings depicting user@host + privileges *****************************************************************************/ static const char *command_array[]= -{"SELECT", "INSERT","UPDATE","DELETE","CREATE", "DROP","RELOAD","SHUTDOWN", - "PROCESS","FILE","GRANT","REFERENCES","INDEX","ALTER"}; -static int command_lengths[]={6,6,6,6,6,4,6,8,7,4,5,10,5,5}; +{ + "SELECT", "INSERT","UPDATE","DELETE","CREATE", "DROP", "RELOAD","SHUTDOWN", + "PROCESS","FILE","GRANT","REFERENCES","INDEX", "ALTER", "SHOW DATABASES", + "SUPER", "CREATE TEMPORARY TABLES", "LOCK TABLES", "EXECUTE", + "REPLICATION SLAVE", "REPLICATION CLIENT", +}; +static uint command_lengths[]= +{ + 6,6,6,6,6,4,6,8,7,4,5,10,5,5,14,5,23,11,7,17,18 +}; + int mysql_show_grants(THD *thd,LEX_USER *lex_user) { - uint counter, want_access,index; + ulong want_access; + uint counter,index; int error = 0; ACL_USER *acl_user; ACL_DB *acl_db; char buff[1024]; @@ -2372,7 +2741,7 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) if (!(host=acl_user->host.hostname)) host="%"; if (!strcmp(lex_user->user.str,user) && - !strcmp(lex_user->host.str,host)) + !my_strcasecmp(lex_user->host.str,host)) break; } if (counter == acl_users.elements) @@ -2396,7 +2765,6 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) VOID(pthread_mutex_lock(&acl_cache->lock)); /* Add first global access grants */ - if (acl_user->access || acl_user->password) { want_access=acl_user->access; String global(buff,sizeof(buff)); @@ -2410,7 +2778,7 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) else { bool found=0; - uint j,test_access= want_access & ~GRANT_ACL; + ulong j,test_access= want_access & ~GRANT_ACL; for (counter=0, j = SELECT_ACL;j <= GLOBAL_ACLS;counter++,j <<= 1) { if (test_access & j) @@ -2435,8 +2803,68 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) global.append(passd_buff); global.append('\''); } - if (want_access & GRANT_ACL) - global.append(" WITH GRANT OPTION",18); + /* "show grants" SSL related stuff */ + if (acl_user->ssl_type == SSL_TYPE_ANY) + global.append(" REQUIRE SSL",12); + else if (acl_user->ssl_type == SSL_TYPE_X509) + global.append(" REQUIRE X509",13); + else if (acl_user->ssl_type == SSL_TYPE_SPECIFIED) + { + int ssl_options = 0; + global.append(" REQUIRE ",9); + if (acl_user->x509_issuer) + { + ssl_options++; + global.append("ISSUER \'",8); + global.append(acl_user->x509_issuer,strlen(acl_user->x509_issuer)); + global.append('\''); + } + if (acl_user->x509_subject) + { + if (ssl_options++) + global.append(' '); + global.append("SUBJECT \'",9); + global.append(acl_user->x509_subject,strlen(acl_user->x509_subject)); + global.append('\''); + } + if (acl_user->ssl_cipher) + { + if (ssl_options++) + global.append(' '); + global.append("CIPHER '",8); + global.append(acl_user->ssl_cipher,strlen(acl_user->ssl_cipher)); + global.append('\''); + } + } + if ((want_access & GRANT_ACL) || + (acl_user->user_resource.questions | acl_user->user_resource.updates | + acl_user->user_resource.connections)) + { + global.append(" WITH",5); + if (want_access & GRANT_ACL) + global.append(" GRANT OPTION",13); + if (acl_user->user_resource.questions) + { + char buff[22], *p; // just as in int2str + global.append(" MAX_QUERIES_PER_HOUR ",22); + p=int10_to_str(acl_user->user_resource.questions,buff,10); + global.append(buff,p-buff); + } + if (acl_user->user_resource.updates) + { + char buff[22], *p; // just as in int2str + global.append(" MAX_UPDATES_PER_HOUR ",22); + p=int10_to_str(acl_user->user_resource.updates,buff,10); + global.append(buff,p-buff); + } + if (acl_user->user_resource.connections) + { + char buff[22], *p; // just as in int2str + global.append(" MAX_CONNECTIONS_PER_HOUR ",26); + p=int10_to_str(acl_user->user_resource.connections,buff,10); + global.append(buff,p-buff); + } + } thd->packet.length(0); net_store_data(&thd->packet,global.ptr(),global.length()); if (my_net_write(&thd->net,(char*) thd->packet.ptr(), @@ -2458,7 +2886,7 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) host=""; if (!strcmp(lex_user->user.str,user) && - !strcmp(lex_user->host.str,host)) + !my_strcasecmp(lex_user->host.str,host)) { want_access=acl_db->access; if (want_access) @@ -2469,10 +2897,12 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) if (test_all_bits(want_access,(DB_ACLS & ~GRANT_ACL))) db.append("ALL PRIVILEGES",14); + else if (!(want_access & ~GRANT_ACL)) + db.append("USAGE",5); else { int found=0, cnt; - uint j,test_access= want_access & ~GRANT_ACL; + ulong j,test_access= want_access & ~GRANT_ACL; for (cnt=0, j = SELECT_ACL; j <= DB_ACLS; cnt++,j <<= 1) { if (test_access & j) @@ -2492,7 +2922,7 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) db.append(lex_user->host.str, lex_user->host.length); db.append ('\''); if (want_access & GRANT_ACL) - db.append(" WITH GRANT OPTION",18); + db.append(" WITH GRANT OPTION",18); thd->packet.length(0); net_store_data(&thd->packet,db.ptr(),db.length()); if (my_net_write(&thd->net,(char*) thd->packet.ptr(), @@ -2517,7 +2947,7 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) host=""; if (!strcmp(lex_user->user.str,user) && - !strcmp(lex_user->host.str,host)) + !my_strcasecmp(lex_user->host.str,host)) { want_access=grant_table->privs; if ((want_access | grant_table->cols) != 0) @@ -2531,7 +2961,7 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) else { int found=0; - uint j,test_access= (want_access | grant_table->cols) & ~GRANT_ACL; + ulong j,test_access= (want_access | grant_table->cols) & ~GRANT_ACL; for (counter=0, j = SELECT_ACL;j <= TABLE_ACLS; counter++,j <<= 1) { @@ -2602,8 +3032,47 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) } +/* + Make a clear-text version of the requested privilege. +*/ + +void get_privilege_desc(char *to, uint max_length, ulong access) +{ + uint pos; + char *start=to; + DBUG_ASSERT(max_length >= 30); // For end ',' removal + + if (access) + { + max_length--; // Reserve place for end-zero + for (pos=0 ; access ; pos++, access>>=1) + { + if ((access & 1) && + command_lengths[pos] + (uint) (to-start) < max_length) + { + to= strmov(to, command_array[pos]); + *to++=','; + } + } + to--; // Remove end ',' + } + *to=0; +} + + +void get_mqh(const char *user, const char *host, USER_CONN *uc) +{ + ACL_USER *acl_user; + if (initialized && (acl_user= find_acl_user(host,user))) + uc->user_resources= acl_user->user_resource; + else + bzero((char*) &uc->user_resources, sizeof(uc->user_resources)); +} + + + /***************************************************************************** -** Instantiate used templates + Instantiate used templates *****************************************************************************/ #ifdef __GNUC__ diff --git a/sql/sql_acl.h b/sql/sql_acl.h index cf9696d51e7..6925b6b406c 100644 --- a/sql/sql_acl.h +++ b/sql/sql_acl.h @@ -1,52 +1,79 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#define SELECT_ACL 1 -#define INSERT_ACL 2 -#define UPDATE_ACL 4 -#define DELETE_ACL 8 -#define CREATE_ACL 16 -#define DROP_ACL 32 -#define RELOAD_ACL 64 -#define SHUTDOWN_ACL 128 -#define PROCESS_ACL 256 -#define FILE_ACL 512 -#define GRANT_ACL 1024 -#define REFERENCES_ACL 2048 -#define INDEX_ACL 4096 -#define ALTER_ACL 8192 -#define EXTRA_ACL 16384 -#define DB_ACLS (UPDATE_ACL | SELECT_ACL | INSERT_ACL | \ - DELETE_ACL | CREATE_ACL | DROP_ACL | GRANT_ACL | \ - REFERENCES_ACL | INDEX_ACL | ALTER_ACL) -#define TABLE_ACLS (SELECT_ACL | INSERT_ACL | UPDATE_ACL | \ - DELETE_ACL | CREATE_ACL | DROP_ACL | GRANT_ACL | \ - REFERENCES_ACL | INDEX_ACL | ALTER_ACL) -#define COL_ACLS (SELECT_ACL | INSERT_ACL | UPDATE_ACL | REFERENCES_ACL) -#define GLOBAL_ACLS (SELECT_ACL | INSERT_ACL | UPDATE_ACL | DELETE_ACL |\ - CREATE_ACL | DROP_ACL | RELOAD_ACL | SHUTDOWN_ACL |\ - PROCESS_ACL | FILE_ACL | GRANT_ACL | REFERENCES_ACL |\ - INDEX_ACL | ALTER_ACL) -#define NO_ACCESS 32768 - -/* defines to change the above bits to how things are stored in tables */ - -#define fix_rights_for_db(A) (((A) & 63) | (((A) & ~63) << 4)) -#define get_rights_for_db(A) (((A) & 63) | (((A) & ~63) >> 4)) +#define SELECT_ACL (1L << 0) +#define INSERT_ACL (1L << 1) +#define UPDATE_ACL (1L << 2) +#define DELETE_ACL (1L << 3) +#define CREATE_ACL (1L << 4) +#define DROP_ACL (1L << 5) +#define RELOAD_ACL (1L << 6) +#define SHUTDOWN_ACL (1L << 7) +#define PROCESS_ACL (1L << 8) +#define FILE_ACL (1L << 9) +#define GRANT_ACL (1L << 10) +#define REFERENCES_ACL (1L << 11) +#define INDEX_ACL (1L << 12) +#define ALTER_ACL (1L << 13) +#define SHOW_DB_ACL (1L << 14) +#define SUPER_ACL (1L << 15) +#define CREATE_TMP_ACL (1L << 16) +#define LOCK_TABLES_ACL (1L << 17) +#define EXECUTE_ACL (1L << 18) +#define REPL_SLAVE_ACL (1L << 19) +#define REPL_CLIENT_ACL (1L << 20) + + +#define DB_ACLS \ +(UPDATE_ACL | SELECT_ACL | INSERT_ACL | DELETE_ACL | CREATE_ACL | DROP_ACL | \ + GRANT_ACL | REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_TMP_ACL | LOCK_TABLES_ACL) + +#define TABLE_ACLS \ +(SELECT_ACL | INSERT_ACL | UPDATE_ACL | DELETE_ACL | CREATE_ACL | DROP_ACL | \ + GRANT_ACL | REFERENCES_ACL | INDEX_ACL | ALTER_ACL) + +#define COL_ACLS \ +(SELECT_ACL | INSERT_ACL | UPDATE_ACL | REFERENCES_ACL) + +#define GLOBAL_ACLS \ +(SELECT_ACL | INSERT_ACL | UPDATE_ACL | DELETE_ACL | CREATE_ACL | DROP_ACL | \ + RELOAD_ACL | SHUTDOWN_ACL | PROCESS_ACL | FILE_ACL | GRANT_ACL | \ + REFERENCES_ACL | INDEX_ACL | ALTER_ACL | SHOW_DB_ACL | SUPER_ACL | \ + CREATE_TMP_ACL | LOCK_TABLES_ACL | REPL_SLAVE_ACL | REPL_CLIENT_ACL | \ + EXECUTE_ACL) + +#define EXTRA_ACL (1L << 29) +#define NO_ACCESS (1L << 30) + +/* + Defines to change the above bits to how things are stored in tables + This is needed as the 'host' and 'db' table is missing a few privileges +*/ + +/* Continius bit-segments that needs to be shifted */ +#define DB_REL1 (RELOAD_ACL | SHUTDOWN_ACL | PROCESS_ACL | FILE_ACL) +#define DB_REL2 (GRANT_ACL | REFERENCES_ACL) + +/* Privileges that needs to be reallocated (in continous chunks) */ +#define DB_CHUNK1 (GRANT_ACL | REFERENCES_ACL | INDEX_ACL | ALTER_ACL) +#define DB_CHUNK2 (CREATE_TMP_ACL | LOCK_TABLES_ACL) + +#define fix_rights_for_db(A) (((A) & 63) | (((A) & DB_REL1) << 4) | (((A) & DB_REL2) << 6)) +#define get_rights_for_db(A) (((A) & 63) | (((A) & DB_CHUNK1) >> 4) | (((A) & DB_CHUNK2) >> 6)) #define fix_rights_for_table(A) (((A) & 63) | (((A) & ~63) << 4)) #define get_rights_for_table(A) (((A) & 63) | (((A) & ~63) >> 4)) #define fix_rights_for_column(A) (((A) & COL_ACLS) | ((A & ~COL_ACLS) << 7)) @@ -54,31 +81,34 @@ /* prototypes */ -int acl_init(bool dont_read_acl_tables); -void acl_reload(void); +my_bool acl_init(THD *thd, bool dont_read_acl_tables); +void acl_reload(THD *thd); void acl_free(bool end=0); -uint acl_get(const char *host, const char *ip, const char *bin_ip, - const char *user, const char *db); -uint acl_getroot(const char *host, const char *ip, const char *user, - const char *password,const char *scramble,char **priv_user, - bool old_ver); +ulong acl_get(const char *host, const char *ip, const char *bin_ip, + const char *user, const char *db); +ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user, + const char *password,const char *scramble,char **priv_user, + bool old_ver, USER_RESOURCES *max); bool acl_check_host(const char *host, const char *ip); +bool check_change_password(THD *thd, const char *host, const char *user); bool change_password(THD *thd, const char *host, const char *user, char *password); int mysql_grant(THD *thd, const char *db, List <LEX_USER> &user_list, - uint rights, bool revoke); + ulong rights, bool revoke); int mysql_table_grant(THD *thd, TABLE_LIST *table, List <LEX_USER> &user_list, - List <LEX_COLUMN> &column_list, uint rights, + List <LEX_COLUMN> &column_list, ulong rights, bool revoke); -int grant_init(void); +my_bool grant_init(THD *thd); void grant_free(void); -void grant_reload(void); -bool check_grant(THD *thd, uint want_access, TABLE_LIST *tables, +void grant_reload(THD *thd); +bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, uint show_command=0, bool dont_print_error=0); -bool check_grant_column (THD *thd,TABLE *table, const char *name,uint length, +bool check_grant_column (THD *thd,TABLE *table, const char *name, uint length, uint show_command=0); -bool check_grant_all_columns(THD *thd, uint want_access, TABLE *table); +bool check_grant_all_columns(THD *thd, ulong want_access, TABLE *table); bool check_grant_db(THD *thd,const char *db); -uint get_table_grant(THD *thd, TABLE_LIST *table); -uint get_column_grant(THD *thd, TABLE_LIST *table, Field *field); +ulong get_table_grant(THD *thd, TABLE_LIST *table); +ulong get_column_grant(THD *thd, TABLE_LIST *table, Field *field); int mysql_show_grants(THD *thd, LEX_USER *user); +void get_privilege_desc(char *to, uint max_length, ulong access); +void get_mqh(const char *user, const char *host, USER_CONN *uc); diff --git a/sql/sql_analyse.cc b/sql/sql_analyse.cc index 6227b1251db..a75b7e304d1 100644 --- a/sql/sql_analyse.cc +++ b/sql/sql_analyse.cc @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -38,6 +38,37 @@ #define UINT_MAX24 0xffffff #define UINT_MAX32 0xffffffff +int sortcmp2(void* cmp_arg __attribute__((unused)), + const String *a,const String *b) +{ + return sortcmp(a,b); +} + +int stringcmp2(void* cmp_arg __attribute__((unused)), + const String *a,const String *b) +{ + return stringcmp(a,b); +} + +int compare_double2(void* cmp_arg __attribute__((unused)), + const double *s, const double *t) +{ + return compare_double(s,t); +} + +int compare_longlong2(void* cmp_arg __attribute__((unused)), + const longlong *s, const longlong *t) +{ + return compare_longlong(s,t); +} + +int compare_ulonglong2(void* cmp_arg __attribute__((unused)), + const ulonglong *s, const ulonglong *t) +{ + return compare_ulonglong(s,t); +} + + Procedure * proc_analyse_init(THD *thd, ORDER *param, select_result *result, List<Item> &field_list) @@ -60,7 +91,7 @@ proc_analyse_init(THD *thd, ORDER *param, select_result *result, { delete pc; net_printf(&thd->net, ER_WRONG_PARAMETERS_TO_PROCEDURE, proc_name); - return 0; + DBUG_RETURN(0); } pc->max_tree_elements = (uint) (*param->item)->val_int(); param = param->next; @@ -68,7 +99,7 @@ proc_analyse_init(THD *thd, ORDER *param, select_result *result, { delete pc; net_printf(&thd->net, ER_WRONG_PARAMCOUNT_TO_PROCEDURE, proc_name); - return 0; + DBUG_RETURN(0); } // second parameter if ((*param->item)->type() != Item::INT_ITEM || @@ -76,7 +107,7 @@ proc_analyse_init(THD *thd, ORDER *param, select_result *result, { delete pc; net_printf(&thd->net, ER_WRONG_PARAMETERS_TO_PROCEDURE, proc_name); - return 0; + DBUG_RETURN(0); } pc->max_treemem = (uint) (*param->item)->val_int(); } @@ -85,7 +116,7 @@ proc_analyse_init(THD *thd, ORDER *param, select_result *result, { delete pc; net_printf(&thd->net, ER_WRONG_PARAMETERS_TO_PROCEDURE, proc_name); - return 0; + DBUG_RETURN(0); } // if only one parameter was given, it will be the value of max_tree_elements else @@ -100,7 +131,7 @@ proc_analyse_init(THD *thd, ORDER *param, select_result *result, pc->f_end = pc->f_info + field_list.elements; pc->fields = field_list; - List_iterator<Item> it(pc->fields); + List_iterator_fast<Item> it(pc->fields); f_info = pc->f_info; Item *item; @@ -121,21 +152,26 @@ proc_analyse_init(THD *thd, ORDER *param, select_result *result, if (item->result_type() == STRING_RESULT) *f_info++ = new field_str(item, pc); } - return pc; -} // proc_analyse_init + DBUG_RETURN(pc); +} -// return 1 if number, else return 0 -// store info about found number in info -// NOTE:It is expected, that elements of 'info' are all zero! +/* + Return 1 if number, else return 0 + store info about found number in info + NOTE:It is expected, that elements of 'info' are all zero! +*/ + bool test_if_number(NUM_INFO *info, const char *str, uint str_len) { const char *begin, *end = str + str_len; DBUG_ENTER("test_if_number"); - // MySQL removes any endspaces of a string, so we must take care only of - // spaces in front of a string + /* + MySQL removes any endspaces of a string, so we must take care only of + spaces in front of a string + */ for (; str != end && isspace(*str); str++) ; if (str == end) return 0; @@ -278,30 +314,6 @@ void field_str::add() was_maybe_zerofill = num_info.maybe_zerofill; } - if (room_in_tree) - { - if (res != &s) - s.copy(*res); - if (!tree_search(&tree, (void*) &s)) // If not in tree - { - s.copy(); // slow, when SAFE_MALLOC is in use - if (!tree_insert(&tree, (void*) &s, 0)) - { - room_in_tree = 0; // Remove tree, out of RAM ? - delete_tree(&tree); - } - else - { - bzero((char*) &s, sizeof(s)); // Let tree handle free of this - if ((treemem += length) > pc->max_treemem) - { - room_in_tree = 0; // Remove tree, too big tree - delete_tree(&tree); - } - } - } - } - if (!found) { found = 1; @@ -332,6 +344,31 @@ void field_str::add() max_arg.copy(*res); } } + + if (room_in_tree) + { + if (res != &s) + s.copy(*res); + if (!tree_search(&tree, (void*) &s)) // If not in tree + { + s.copy(); // slow, when SAFE_MALLOC is in use + if (!tree_insert(&tree, (void*) &s, 0)) + { + room_in_tree = 0; // Remove tree, out of RAM ? + delete_tree(&tree); + } + else + { + bzero((char*) &s, sizeof(s)); // Let tree handle free of this + if ((treemem += length) > pc->max_treemem) + { + room_in_tree = 0; // Remove tree, too big tree + delete_tree(&tree); + } + } + } + } + if ((num_info.zerofill && (max_length != min_length)) || (was_zero_fill && (max_length != min_length))) can_be_still_num = 0; // zerofilled numbers must be of same length @@ -863,7 +900,7 @@ int collect_string(String *element, int collect_real(double *element, element_count count __attribute__((unused)), TREE_INFO *info) { - char buff[255]; + char buff[MAX_FIELD_WIDTH]; String s(buff, sizeof(buff)); if (info->found) @@ -882,7 +919,7 @@ int collect_longlong(longlong *element, element_count count __attribute__((unused)), TREE_INFO *info) { - char buff[255]; + char buff[MAX_FIELD_WIDTH]; String s(buff, sizeof(buff)); if (info->found) @@ -901,7 +938,7 @@ int collect_ulonglong(ulonglong *element, element_count count __attribute__((unused)), TREE_INFO *info) { - char buff[255]; + char buff[MAX_FIELD_WIDTH]; String s(buff, sizeof(buff)); if (info->found) diff --git a/sql/sql_analyse.h b/sql/sql_analyse.h index faf79508f60..aa6d0dbb2d1 100644 --- a/sql/sql_analyse.h +++ b/sql/sql_analyse.h @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -21,8 +21,6 @@ #pragma interface /* gcc class implementation */ #endif -#include <my_tree.h> - #define DEC_IN_AVG 4 typedef struct st_number_info @@ -53,8 +51,14 @@ uint check_ulonglong(const char *str, uint length); bool get_ev_num_info(EV_NUM_INFO *ev_info, NUM_INFO *info, const char *num); bool test_if_number(NUM_INFO *info, const char *str, uint str_len); int compare_double(const double *s, const double *t); +int compare_double2(void* cmp_arg __attribute__((unused)), + const double *s, const double *t); int compare_longlong(const longlong *s, const longlong *t); +int compare_longlong2(void* cmp_arg __attribute__((unused)), + const longlong *s, const longlong *t); int compare_ulonglong(const ulonglong *s, const ulonglong *t); +int compare_ulonglong2(void* cmp_arg __attribute__((unused)), + const ulonglong *s, const ulonglong *t); Procedure *proc_analyse_init(THD *thd, ORDER *param, select_result *result, List<Item> &field_list); void free_string(String*); @@ -91,6 +95,11 @@ public: int collect_string(String *element, element_count count, TREE_INFO *info); +int sortcmp2(void* cmp_arg __attribute__((unused)), + const String *a,const String *b); +int stringcmp2(void* cmp_arg __attribute__((unused)), + const String *a,const String *b); + class field_str :public field_info { String min_arg, max_arg; @@ -105,9 +114,9 @@ public: max_arg(""), sum(0), must_be_blob(0), was_zero_fill(0), was_maybe_zerofill(0), can_be_still_num(1) - { init_tree(&tree, 0, sizeof(String), a->binary ? - (qsort_cmp) stringcmp : (qsort_cmp) sortcmp, - 0, (void (*)(void*)) free_string); }; + { init_tree(&tree, 0, 0, sizeof(String), a->binary ? + (qsort_cmp2) stringcmp2 : (qsort_cmp2) sortcmp2, + 0, (tree_element_free) free_string, NULL); }; void add(); void get_opt_type(String*, ha_rows); @@ -145,8 +154,8 @@ class field_real: public field_info public: field_real(Item* a, analyse* b) :field_info(a,b), min_arg(0), max_arg(0), sum(0), sum_sqr(0), max_notzero_dec_len(0) - { init_tree(&tree, 0, sizeof(double), - (qsort_cmp) compare_double, 0, NULL); } + { init_tree(&tree, 0, 0, sizeof(double), + (qsort_cmp2) compare_double2, 0, NULL, NULL); } void add(); void get_opt_type(String*, ha_rows); @@ -191,8 +200,8 @@ class field_longlong: public field_info public: field_longlong(Item* a, analyse* b) :field_info(a,b), min_arg(0), max_arg(0), sum(0), sum_sqr(0) - { init_tree(&tree, 0, sizeof(longlong), - (qsort_cmp) compare_longlong, 0, NULL); } + { init_tree(&tree, 0, 0, sizeof(longlong), + (qsort_cmp2) compare_longlong2, 0, NULL, NULL); } void add(); void get_opt_type(String*, ha_rows); @@ -236,8 +245,8 @@ class field_ulonglong: public field_info public: field_ulonglong(Item* a, analyse * b) :field_info(a,b), min_arg(0), max_arg(0), sum(0),sum_sqr(0) - { init_tree(&tree, 0, sizeof(ulonglong), - (qsort_cmp) compare_ulonglong, 0, NULL); } + { init_tree(&tree, 0, 0, sizeof(ulonglong), + (qsort_cmp2) compare_ulonglong2, 0, NULL, NULL); } void add(); void get_opt_type(String*, ha_rows); String *get_min_arg(String *s) { s->set(min_arg); return s; } diff --git a/sql/sql_base.cc b/sql/sql_base.cc index bab4c151ef3..4d1e57f0c1e 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -15,11 +15,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -/* Basic functions neaded by many modules */ +/* Basic functions needed by many modules */ #include "mysql_priv.h" #include "sql_acl.h" -#include <thr_alarm.h> #include <m_ctype.h> #include <my_dir.h> #include <hash.h> @@ -34,16 +33,14 @@ HASH open_cache; /* Used by mysql_test */ static int open_unireg_entry(THD *thd,TABLE *entry,const char *db, const char *name, const char *alias); -static bool insert_fields(THD *thd,TABLE_LIST *tables, const char *db_name, - const char *table_name, List_iterator<Item> *it); static void free_cache_entry(TABLE *entry); static void mysql_rm_tmp_tables(void); static key_map get_key_map_from_key_list(TABLE *table, List<String> *index_list); -static byte *cache_key(const byte *record,uint *length, - my_bool not_used __attribute__((unused))) +extern "C" byte *table_cache_key(const byte *record,uint *length, + my_bool not_used __attribute__((unused))) { TABLE *entry=(TABLE*) record; *length=entry->key_length; @@ -52,8 +49,8 @@ static byte *cache_key(const byte *record,uint *length, void table_cache_init(void) { - VOID(hash_init(&open_cache,table_cache_size+16,0,0,cache_key, - (void (*)(void*)) free_cache_entry,0)); + VOID(hash_init(&open_cache,table_cache_size+16,0,0,table_cache_key, + (hash_free_key) free_cache_entry,0)); mysql_rm_tmp_tables(); } @@ -111,75 +108,90 @@ static void check_unused(void) #define check_unused() #endif -int list_open_tables(THD *thd,List<char> *tables, const char *db, - const char *wild) +/* + Create a list for all open tables matching SQL expression + + SYNOPSIS + list_open_tables() + thd Thread THD + wild SQL like expression + + NOTES + One gets only a list of tables for which one has any kind of privilege. + db and table names are allocated in result struct, so one doesn't need + a lock on LOCK_open when traversing the return list. + + RETURN VALUES + NULL Error (Probably OOM) + # Pointer to list of names of open tables. +*/ + +OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *wild) { int result = 0; - uint col_access=thd->col_access; + OPEN_TABLE_LIST **start_list, *open_list; TABLE_LIST table_list; + char name[NAME_LEN*2]; DBUG_ENTER("list_open_tables"); + VOID(pthread_mutex_lock(&LOCK_open)); bzero((char*) &table_list,sizeof(table_list)); + start_list= &open_list; + open_list=0; - for (uint idx=0 ; idx < open_cache.records; idx++) + for (uint idx=0 ; result == 0 && idx < open_cache.records; idx++) { + OPEN_TABLE_LIST *table; TABLE *entry=(TABLE*) hash_element(&open_cache,idx); - if ((!entry->real_name) || strcmp(entry->table_cache_key,db)) - continue; - if (wild && wild[0] && wild_compare(entry->real_name,wild)) - continue; - if (db && !(col_access & TABLE_ACLS)) + + if ((!entry->real_name)) + continue; // Shouldn't happen + if (wild) { - table_list.db= (char*) db; - table_list.real_name= entry->real_name;/*real name*/ - table_list.grant.privilege=col_access; - if (check_grant(thd,TABLE_ACLS,&table_list,1,1)) - continue; + strxmov(name,entry->table_cache_key,".",entry->real_name,NullS); + if (wild_compare(name,wild)) + continue; } - /* need to check if he have't already listed it */ - List_iterator<char> it(*tables); - char *table_name; - int check = 0; - while (check == 0 && (table_name=it++)) + /* Check if user has SELECT privilege for any column in the table */ + table_list.db= (char*) entry->table_cache_key; + table_list.real_name= entry->real_name; + table_list.grant.privilege=0; + if (check_table_access(thd,SELECT_ACL | EXTRA_ACL,&table_list,1)) + continue; + + /* need to check if we haven't already listed it */ + for (table= open_list ; table ; table=table->next) { - if (!strcmp(table_name,entry->real_name)) - check++; + if (!strcmp(table->table,entry->real_name) && + !strcmp(table->db,entry->table_cache_key)) + { + if (entry->in_use) + table->in_use++; + if (entry->locked_by_name) + table->locked++; + break; + } } - if (check) + if (table) continue; - - if (tables->push_back(thd->strdup(entry->real_name))) + if (!(*start_list = (OPEN_TABLE_LIST *) + sql_alloc(sizeof(**start_list)+entry->key_length))) { - result = -1; + open_list=0; // Out of memory break; } + strmov((*start_list)->table= + strmov(((*start_list)->db= (char*) ((*start_list)+1)), + entry->table_cache_key)+1, + entry->real_name); + (*start_list)->in_use= entry->in_use ? 1 : 0; + (*start_list)->locked= entry->locked_by_name ? 1 : 0; + start_list= &(*start_list)->next; + *start_list=0; } - VOID(pthread_mutex_unlock(&LOCK_open)); - DBUG_RETURN(result); -} - -char* -query_table_status(THD *thd,const char *db,const char *table_name) -{ - int cached = 0, in_use = 0; - char info[256]; - - for (uint idx=0 ; idx < open_cache.records; idx++) - { - TABLE *entry=(TABLE*) hash_element(&open_cache,idx); - if (strcmp(entry->table_cache_key,db) || - strcmp(entry->real_name,table_name)) - continue; - - cached++; - if (entry->in_use) - in_use++; - } - - sprintf(info, "cached=%d, in_use=%d", cached, in_use); - return thd->strdup(info); + DBUG_RETURN(open_list); } @@ -195,10 +207,11 @@ query_table_status(THD *thd,const char *db,const char *table_name) bool send_fields(THD *thd,List<Item> &list,uint flag) { - List_iterator<Item> it(list); + List_iterator_fast<Item> it(list); Item *item; char buff[80]; - CONVERT *convert= (flag & 4) ? (CONVERT*) 0 : thd->convert_set; + CONVERT *convert= (flag & 4) ? (CONVERT*) 0 : thd->variables.convert_set; + DBUG_ENTER("send_fields"); String tmp((char*) buff,sizeof(buff)),*res,*packet= &thd->packet; @@ -259,11 +272,11 @@ send_fields(THD *thd,List<Item> &list,uint flag) if (my_net_write(&thd->net, (char*) packet->ptr(),packet->length())) break; /* purecov: inspected */ } - send_eof(&thd->net,(test_flags & TEST_MIT_THREAD) ? 0: 1); - return 0; + send_eof(&thd->net,1); + DBUG_RETURN(0); err: send_error(&thd->net,ER_OUT_OF_RESOURCES); /* purecov: inspected */ - return 1; /* purecov: inspected */ + DBUG_RETURN(1); /* purecov: inspected */ } @@ -293,6 +306,7 @@ void intern_close_table(TABLE *table) static void free_cache_entry(TABLE *table) { DBUG_ENTER("free_cache_entry"); + safe_mutex_assert_owner(&LOCK_open); intern_close_table(table); if (!table->in_use) @@ -314,6 +328,7 @@ static void free_cache_entry(TABLE *table) void free_io_cache(TABLE *table) { + DBUG_ENTER("free_io_cache"); if (table->io_cache) { close_cached_file(table->io_cache); @@ -325,6 +340,7 @@ void free_io_cache(TABLE *table) my_free((gptr) table->record_pointers,MYF(0)); table->record_pointers=0; } + DBUG_VOID_RETURN; } /* Close all tables which aren't in use by any thread */ @@ -360,14 +376,14 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh, if (!found) if_wait_for_refresh=0; // Nothing to wait for } + if (!tables) + kill_delayed_threads(); if (if_wait_for_refresh) { /* If there is any table that has a lower refresh_version, wait until this is closed (or this thread is killed) before returning */ - if (!tables) - kill_delayed_threads(); thd->mysys_var->current_mutex= &LOCK_open; thd->mysys_var->current_cond= &COND_refresh; thd->proc_info="Flushing tables"; @@ -437,6 +453,7 @@ void close_thread_tables(THD *thd, bool locked) /* VOID(pthread_sigmask(SIG_SETMASK,&thd->block_signals,NULL)); */ if (!locked) VOID(pthread_mutex_lock(&LOCK_open)); + safe_mutex_assert_owner(&LOCK_open); DBUG_PRINT("info", ("thd->open_tables=%p", thd->open_tables)); @@ -524,17 +541,18 @@ void close_temporary_tables(THD *thd) char *query, *end; uint query_buf_size; bool found_user_tables = 0; - LINT_INIT(end); if (!thd->temporary_tables) - return; // no tables to close + return; + + LINT_INIT(end); + query_buf_size= 50; // Enough for DROP ... TABLE - query_buf_size= 11; // "drop table " for (table=thd->temporary_tables ; table ; table=table->next) - query_buf_size+= table->key_length +1; + query_buf_size+= table->key_length+1; if ((query = alloc_root(&thd->mem_root, query_buf_size))) - end= strmov(query, "drop table "); + end=strmov(query, "DROP /*!40005 TEMPORARY */ TABLE "); for (table=thd->temporary_tables ; table ; table=next) { @@ -543,25 +561,24 @@ void close_temporary_tables(THD *thd) // skip temporary tables not created directly by the user if (table->real_name[0] != '#') { - end = strxmov(end,table->table_cache_key,".", - table->real_name,",", NullS); - // here we assume table_cache_key always starts - // with \0 terminated db name + /* + Here we assume table_cache_key always starts + with \0 terminated db name + */ found_user_tables = 1; } + end = strxmov(end,table->table_cache_key,".", + table->real_name,",", NullS); } next=table->next; close_temporary(table); } if (query && found_user_tables && mysql_bin_log.is_open()) { - uint save_query_len = thd->query_length; - *--end = 0; // Remove last ',' - thd->query_length = (uint)(end-query); - Query_log_event qinfo(thd, query); + /* The -1 is to remove last ',' */ + Query_log_event qinfo(thd, query, (ulong)(end-query)-1, 0); qinfo.error_code=0; mysql_bin_log.write(&qinfo); - thd->query_length = save_query_len; } thd->temporary_tables=0; } @@ -596,7 +613,7 @@ bool close_temporary_table(THD *thd, const char *db, const char *table_name) table= *prev; *prev= table->next; close_temporary(table); - if(thd->slave_thread) + if (thd->slave_thread) --slave_open_temp_tables; return 0; } @@ -619,8 +636,6 @@ bool rename_temporary_table(THD* thd, TABLE *table, const char *db, } - - /* move table first in unused links */ static void relink_unused(TABLE *table) @@ -676,11 +691,13 @@ TABLE *unlink_open_table(THD *thd, TABLE *list, TABLE *find) /* When we call the following function we must have a lock on - LOCK_OPEN ; This lock will be unlocked on return. + LOCK_open ; This lock will be unlocked on return. */ void wait_for_refresh(THD *thd) { + safe_mutex_assert_owner(&LOCK_open); + /* Wait until the current table is up to date */ const char *proc_info; thd->mysys_var->current_mutex= &LOCK_open; @@ -698,13 +715,14 @@ void wait_for_refresh(THD *thd) pthread_mutex_unlock(&thd->mysys_var->mutex); } + TABLE *reopen_name_locked_table(THD* thd, TABLE_LIST* table_list) { DBUG_ENTER("reopen_name_locked_table"); if (thd->killed) DBUG_RETURN(0); TABLE* table; - if(!(table = table_list->table)) + if (!(table = table_list->table)) DBUG_RETURN(0); char* db = thd->db ? thd->db : table_list->db; @@ -717,11 +735,11 @@ TABLE *reopen_name_locked_table(THD* thd, TABLE_LIST* table_list) if (open_unireg_entry(thd, table, db, table_name, table_name) || !(table->table_cache_key =memdup_root(&table->mem_root,(char*) key, key_length))) - { - closefrm(table); - pthread_mutex_unlock(&LOCK_open); - DBUG_RETURN(0); - } + { + closefrm(table); + pthread_mutex_unlock(&LOCK_open); + DBUG_RETURN(0); + } table->key_length=key_length; table->version=0; @@ -734,9 +752,10 @@ TABLE *reopen_name_locked_table(THD* thd, TABLE_LIST* table_list) table->tablenr=thd->current_tablenr++; table->used_fields=0; table->const_table=0; - table->outer_join=table->null_row=table->maybe_null=0; + table->outer_join= table->null_row= table->maybe_null= table->force_index= 0; table->status=STATUS_NO_RECORD; - table->keys_in_use_for_query=table->used_keys= table->keys_in_use; + table->keys_in_use_for_query= table->keys_in_use; + table->used_keys= table->keys_for_keyread; DBUG_RETURN(table); } @@ -854,25 +873,6 @@ TABLE *open_table(THD *thd,const char *db,const char *table_name, !(table->table_cache_key=memdup_root(&table->mem_root,(char*) key, key_length))) { - MEM_ROOT* glob_alloc; - LINT_INIT(glob_alloc); - - if (errno == ENOENT && - (glob_alloc = my_pthread_getspecific_ptr(MEM_ROOT*,THR_MALLOC))) - // Sasha: needed for replication - // remember the name of the non-existent table - // so we can try to download it from the master - { - int table_name_len = (uint) strlen(table_name); - int db_len = (uint) strlen(db); - thd->last_nx_db = alloc_root(glob_alloc,db_len + table_name_len + 2); - if(thd->last_nx_db) - { - thd->last_nx_table = thd->last_nx_db + db_len + 1; - memcpy(thd->last_nx_table, table_name, table_name_len + 1); - memcpy(thd->last_nx_db, db, db_len + 1); - } - } table->next=table->prev=table; free_cache_entry(table); VOID(pthread_mutex_unlock(&LOCK_open)); @@ -910,10 +910,11 @@ TABLE *open_table(THD *thd,const char *db,const char *table_name, table->tablenr=thd->current_tablenr++; table->used_fields=0; table->const_table=0; - table->outer_join=table->null_row=table->maybe_null=0; + table->outer_join= table->null_row= table->maybe_null= table->force_index= 0; table->status=STATUS_NO_RECORD; - table->keys_in_use_for_query=table->used_keys= table->keys_in_use; - dbug_assert(table->key_read == 0); + table->keys_in_use_for_query= table->keys_in_use; + table->used_keys= table->keys_for_keyread; + DBUG_ASSERT(table->key_read == 0); DBUG_RETURN(table); } @@ -957,6 +958,7 @@ bool reopen_table(TABLE *table,bool locked) #endif if (!locked) VOID(pthread_mutex_lock(&LOCK_open)); + safe_mutex_assert_owner(&LOCK_open); if (open_unireg_entry(current_thd,&tmp,db,table_name,table->table_name)) goto end; @@ -977,7 +979,9 @@ bool reopen_table(TABLE *table,bool locked) tmp.null_row= table->null_row; tmp.maybe_null= table->maybe_null; tmp.status= table->status; - tmp.keys_in_use_for_query=tmp.used_keys=tmp.keys_in_use; + tmp.keys_in_use_for_query= tmp.keys_in_use; + tmp.used_keys= tmp.keys_for_keyread; + tmp.force_index= tmp.force_index; /* Get state */ tmp.key_length= table->key_length; @@ -1046,6 +1050,8 @@ bool close_data_tables(THD *thd,const char *db, const char *table_name) bool reopen_tables(THD *thd,bool get_locks,bool in_refresh) { DBUG_ENTER("reopen_tables"); + safe_mutex_assert_owner(&LOCK_open); + if (!thd->open_tables) DBUG_RETURN(0); @@ -1238,25 +1244,44 @@ bool drop_locked_tables(THD *thd,const char *db, const char *table_name) } -/* lock table to force abort of any threads trying to use table */ +/* + If we have the table open, which only happens when a LOCK TABLE has been + done on the table, change the lock type to a lock that will abort all + other threads trying to get the lock. +*/ void abort_locked_tables(THD *thd,const char *db, const char *table_name) { TABLE *table; - for (table=thd->open_tables; table ; table=table->next) + for (table= thd->open_tables; table ; table= table->next) { if (!strcmp(table->real_name,table_name) && !strcmp(table->table_cache_key,db)) + { mysql_lock_abort(thd,table); + break; + } } } -/**************************************************************************** -** open_unireg_entry -** Purpose : Load a table definition from file and open unireg table -** Args : entry with DB and table given -** Returns : 0 if ok -** Note that the extra argument for open is taken from thd->open_options + +/* + Load a table definition from file and open unireg table + + SYNOPSIS + open_unireg_entry() + thd Thread handle + entry Store open table definition here + db Database name + name Table name + alias Alias name + + NOTES + Extra argument for open is taken from thd->open_options + + RETURN + 0 ok + # Error */ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db, @@ -1266,7 +1291,7 @@ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db, int error; DBUG_ENTER("open_unireg_entry"); - (void) sprintf(path,"%s/%s/%s",mysql_data_home,db,name); + strxmov(path, mysql_data_home, "/", db, "/", name, NullS); if (openfrm(path,alias, (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE | HA_GET_INDEX | HA_TRY_READ_ONLY), @@ -1280,6 +1305,8 @@ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db, table_list.db=(char*) db; table_list.real_name=(char*) name; table_list.next=0; + safe_mutex_assert_owner(&LOCK_open); + if ((error=lock_table_name(thd,&table_list))) { if (error < 0) @@ -1324,7 +1351,6 @@ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db, if (error) goto err; } - (void) entry->file->extra(HA_EXTRA_NO_READCHECK); // Not needed in SQL DBUG_RETURN(0); err: DBUG_RETURN(1); @@ -1347,7 +1373,7 @@ int open_tables(THD *thd,TABLE_LIST *start) { if (!tables->table && !(tables->table=open_table(thd, - tables->db ? tables->db : thd->db, + tables->db, tables->real_name, tables->alias, &refresh))) { @@ -1397,6 +1423,61 @@ int open_tables(THD *thd,TABLE_LIST *start) } +/* + Check that lock is ok for tables; Call start stmt if ok + + SYNOPSIS + check_lock_and_start_stmt() + thd Thread handle + table_list Table to check + lock_type Lock used for table + + RETURN VALUES + 0 ok + 1 error +*/ + +static bool check_lock_and_start_stmt(THD *thd, TABLE *table, + thr_lock_type lock_type) +{ + int error; + DBUG_ENTER("check_lock_and_start_stmt"); + + if ((int) lock_type >= (int) TL_WRITE_ALLOW_READ && + (int) table->reginfo.lock_type < (int) TL_WRITE_ALLOW_READ) + { + my_printf_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, + ER(ER_TABLE_NOT_LOCKED_FOR_WRITE), + MYF(0),table->table_name); + DBUG_RETURN(1); + } + if ((error=table->file->start_stmt(thd))) + { + table->file->print_error(error,MYF(0)); + DBUG_RETURN(1); + } + DBUG_RETURN(0); +} + + +/* + Open and lock one table + + SYNOPSIS + open_ltable() + thd Thread handler + table_list Table to open is first table in this list + lock_type Lock to use for open + + RETURN VALUES + table Opened table + 0 Error + + If ok, the following are also set: + table_list->lock_type lock_type + table_list->table table +*/ + TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type) { TABLE *table; @@ -1404,57 +1485,41 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type) DBUG_ENTER("open_ltable"); thd->proc_info="Opening table"; - while (!(table=open_table(thd,table_list->db ? table_list->db : thd->db, - table_list->real_name, table_list->alias, + while (!(table=open_table(thd,table_list->db, + table_list->real_name,table_list->alias, &refresh)) && refresh) ; if (table) { - int error; - #if defined( __WIN__) || defined(OS2) /* 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 */ - ) + if (lock_type == TL_WRITE_ALLOW_READ) { lock_type= TL_WRITE; } #endif /* __WIN__ || OS2 */ - - table_list->table=table; + table_list->lock_type= lock_type; + table_list->table= table; table->grant= table_list->grant; if (thd->locked_tables) { - thd->proc_info=0; - if ((int) lock_type >= (int) TL_WRITE_ALLOW_READ && - (int) table->reginfo.lock_type < (int) TL_WRITE_ALLOW_READ) - { - my_printf_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, - ER(ER_TABLE_NOT_LOCKED_FOR_WRITE), - MYF(0),table_list->alias); - table=0; - } - else if ((error=table->file->start_stmt(thd))) - { - table->file->print_error(error,MYF(0)); - table=0; - } - thd->proc_info=0; - DBUG_RETURN(table); + if (check_lock_and_start_stmt(thd, table, lock_type)) + table= 0; + } + else + { + if ((table->reginfo.lock_type= lock_type) != TL_UNLOCK) + if (!(thd->lock=mysql_lock_tables(thd,&table_list->table,1))) + table= 0; } - if ((table->reginfo.lock_type=lock_type) != TL_UNLOCK) - if (!(thd->lock=mysql_lock_tables(thd,&table_list->table,1))) - DBUG_RETURN(0); } thd->proc_info=0; DBUG_RETURN(table); } + /* -** Open all tables in list and locks them for read. -** The lock will automaticly be freed by the close_thread_tables + Open all tables in list and locks them for read. + The lock will automaticly be freed by close_thread_tables() */ int open_and_lock_tables(THD *thd,TABLE_LIST *tables) @@ -1464,10 +1529,27 @@ int open_and_lock_tables(THD *thd,TABLE_LIST *tables) return 0; } + +/* + Lock all tables in list + + SYNOPSIS + lock_tables() + thd Thread handler + tables Tables to lock + + RETURN VALUES + 0 ok + -1 Error +*/ + int lock_tables(THD *thd,TABLE_LIST *tables) { TABLE_LIST *table; - if (tables && !thd->locked_tables) + if (!tables) + return 0; + + if (!thd->locked_tables) { uint count=0; for (table = tables ; table ; table=table->next) @@ -1484,10 +1566,9 @@ int lock_tables(THD *thd,TABLE_LIST *tables) { for (table = tables ; table ; table=table->next) { - int error; - if ((error=table->table->file->start_stmt(thd))) + if (check_lock_and_start_stmt(thd, table->table, table->lock_type)) { - table->table->file->print_error(error,MYF(0)); + ha_rollback_stmt(thd); return -1; } } @@ -1495,10 +1576,11 @@ int lock_tables(THD *thd,TABLE_LIST *tables) return 0; } + /* -** Open a single table without table caching and don't set it in open_list -** Used by alter_table to open a temporary table and when creating -** a temporary table with CREATE TEMPORARY ... + Open a single table without table caching and don't set it in open_list + Used by alter_table to open a temporary table and when creating + a temporary table with CREATE TEMPORARY ... */ TABLE *open_temporary_table(THD *thd, const char *path, const char *db, @@ -1507,11 +1589,13 @@ TABLE *open_temporary_table(THD *thd, const char *path, const char *db, TABLE *tmp_table; DBUG_ENTER("open_temporary_table"); - // the extra size in my_malloc() is for table_cache_key - // 4 bytes for master thread id if we are in the slave - // 1 byte to terminate db - // 1 byte to terminate table_name - // total of 6 extra bytes in my_malloc in addition to table/db stuff + /* + The extra size in my_malloc() is for table_cache_key + 4 bytes for master thread id if we are in the slave + 1 byte to terminate db + 1 byte to terminate table_name + total of 6 extra bytes in my_malloc in addition to table/db stuff + */ if (!(tmp_table=(TABLE*) my_malloc(sizeof(*tmp_table)+(uint) strlen(db)+ (uint) strlen(table_name)+6, MYF(MY_WME)))) @@ -1527,7 +1611,6 @@ TABLE *open_temporary_table(THD *thd, const char *path, const char *db, DBUG_RETURN(0); } - tmp_table->file->extra(HA_EXTRA_NO_READCHECK); // Not needed in SQL tmp_table->reginfo.lock_type=TL_WRITE; // Simulate locked tmp_table->tmp_table = (tmp_table->file->has_transactions() ? TRANSACTIONAL_TMP_TABLE : TMP_TABLE); @@ -1544,8 +1627,8 @@ TABLE *open_temporary_table(THD *thd, const char *path, const char *db, { tmp_table->next=thd->temporary_tables; thd->temporary_tables=tmp_table; - if(thd->slave_thread) - ++slave_open_temp_tables; + if (thd->slave_thread) + slave_open_temp_tables++; } DBUG_RETURN(tmp_table); } @@ -1605,18 +1688,13 @@ Field *find_field_in_table(THD *thd,TABLE *table,const char *name,uint length, { field->query_id=thd->query_id; table->used_fields++; - if (field->part_of_key) - { - if (!(field->part_of_key & table->ref_primary_key)) - table->used_keys&=field->part_of_key; - } - else - table->used_keys=0; + table->used_keys&= field->part_of_key; } else thd->dupp_field=field; } - if (check_grants && !thd->master_access && check_grant_column(thd,table,name,length)) + if (check_grants && !thd->master_access && + check_grant_column(thd,table,name,length)) return WRONG_GRANT; return field; } @@ -1637,9 +1715,7 @@ find_field_in_tables(THD *thd,Item_field *item,TABLE_LIST *tables) for (; tables ; tables=tables->next) { if (!strcmp(tables->alias,table_name) && - (!db || - (tables->db && !strcmp(db,tables->db)) || - (!tables->db && !strcmp(db,thd->db)))) + (!db || !strcmp(db,tables->db))) { found_table=1; Field *find=find_field_in_table(thd,tables->table,name,length, @@ -1682,7 +1758,8 @@ find_field_in_tables(THD *thd,Item_field *item,TABLE_LIST *tables) for (; tables ; tables=tables->next) { Field *field=find_field_in_table(thd,tables->table,name,length, - grant_option && !thd->master_access, allow_rowid); + grant_option && + !thd->master_access, allow_rowid); if (field) { if (field == WRONG_GRANT) @@ -1763,14 +1840,15 @@ find_item_in_list(Item *find,List<Item> &items) ****************************************************************************/ int setup_fields(THD *thd, TABLE_LIST *tables, List<Item> &fields, - bool set_query_id, List<Item> *sum_func_list) + bool set_query_id, List<Item> *sum_func_list, + bool allow_sum_func) { reg2 Item *item; List_iterator<Item> it(fields); DBUG_ENTER("setup_fields"); thd->set_query_id=set_query_id; - thd->allow_sum_func= test(sum_func_list); + thd->allow_sum_func= allow_sum_func; thd->where="field list"; while ((item=it++)) @@ -1778,15 +1856,26 @@ int setup_fields(THD *thd, TABLE_LIST *tables, List<Item> &fields, if (item->type() == Item::FIELD_ITEM && ((Item_field*) item)->field_name[0] == '*') { + uint elem=fields.elements; if (insert_fields(thd,tables,((Item_field*) item)->db_name, ((Item_field*) item)->table_name,&it)) DBUG_RETURN(-1); /* purecov: inspected */ + if (sum_func_list) + { + /* + sum_func_list is a list that has the fields list as a tail. + Because of this we have to update the element count also for this + list after expanding the '*' entry. + */ + sum_func_list->elements+= fields.elements - elem; + } } else { if (item->fix_fields(thd,tables)) DBUG_RETURN(-1); /* purecov: inspected */ - if (item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM) + if (item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM && + sum_func_list) item->split_sum_func(*sum_func_list); thd->used_tables|=item->used_tables(); } @@ -1805,29 +1894,44 @@ bool setup_tables(TABLE_LIST *tables) { DBUG_ENTER("setup_tables"); uint tablenr=0; - for (TABLE_LIST *table=tables ; table ; table=table->next,tablenr++) - { - table->table->tablenr=tablenr; - table->table->map= (table_map) 1 << tablenr; - if ((table->table->outer_join=table->outer_join)) - table->table->maybe_null=1; // LEFT OUTER JOIN ... - if (table->use_index) + for (TABLE_LIST *table_list=tables ; table_list ; + table_list=table_list->next,tablenr++) + { + TABLE *table=table_list->table; + + table->used_fields=0; + table->const_table=0; + table->outer_join=table->null_row=0; + table->status=STATUS_NO_RECORD; + table->keys_in_use_for_query= table->keys_in_use; + table->used_keys= table->keys_for_keyread; + table->maybe_null=test(table->outer_join=table_list->outer_join); + table->tablenr=tablenr; + table->map= (table_map) 1 << tablenr; + table->force_index= table_list->force_index; + if (table_list->use_index) { - key_map map= get_key_map_from_key_list(table->table, - table->use_index); + key_map map= get_key_map_from_key_list(table, + table_list->use_index); if (map == ~(key_map) 0) DBUG_RETURN(1); - table->table->keys_in_use_for_query=map; + table->keys_in_use_for_query=map; } - if (table->ignore_index) + if (table_list->ignore_index) { - key_map map= get_key_map_from_key_list(table->table, - table->ignore_index); + key_map map= get_key_map_from_key_list(table, + table_list->ignore_index); if (map == ~(key_map) 0) DBUG_RETURN(1); - table->table->keys_in_use_for_query &= ~map; + table->keys_in_use_for_query &= ~map; + } + table->used_keys &= table->keys_in_use_for_query; + if (table_list->shared) + { + /* Clear query_id that may have been set by previous select */ + for (Field **ptr=table->field ; *ptr ; ptr++) + (*ptr)->query_id=0; } - table->table->used_keys &= table->table->keys_in_use_for_query; } if (tablenr > MAX_TABLES) { @@ -1842,7 +1946,7 @@ static key_map get_key_map_from_key_list(TABLE *table, List<String> *index_list) { key_map map=0; - List_iterator<String> it(*index_list); + List_iterator_fast<String> it(*index_list); String *name; uint pos; while ((name=it++)) @@ -1859,11 +1963,11 @@ static key_map get_key_map_from_key_list(TABLE *table, } /**************************************************************************** -** This just drops in all fields instead of current '*' field -** Returns pointer to last inserted field if ok + This just drops in all fields instead of current '*' field + Returns pointer to last inserted field if ok ****************************************************************************/ -static bool +bool insert_fields(THD *thd,TABLE_LIST *tables, const char *db_name, const char *table_name, List_iterator<Item> *it) { @@ -1874,33 +1978,30 @@ insert_fields(THD *thd,TABLE_LIST *tables, const char *db_name, for (; tables ; tables=tables->next) { TABLE *table=tables->table; - if (grant_option && !thd->master_access && - check_grant_all_columns(thd,SELECT_ACL,table) ) - DBUG_RETURN(-1); if (!table_name || (!strcmp(table_name,tables->alias) && - (!db_name || !tables->db || - !strcmp(tables->db,db_name)))) + (!db_name || !strcmp(tables->db,db_name)))) { + /* Ensure that we have access right to all columns */ + if (grant_option && !thd->master_access && + check_grant_all_columns(thd,SELECT_ACL,table) ) + DBUG_RETURN(-1); Field **ptr=table->field,*field; thd->used_tables|=table->map; while ((field = *ptr++)) { Item_field *item= new Item_field(field); if (!found++) - (void) it->replace(item); + (void) it->replace(item); // Replace '*' else it->after(item); + /* + Mark if field used before in this select. + Used by 'insert' to verify if a field name is used twice + */ if (field->query_id == thd->query_id) thd->dupp_field=field; field->query_id=thd->query_id; - - if (field->part_of_key) - { - if (!(field->part_of_key & table->ref_primary_key)) - table->used_keys&=field->part_of_key; - } - else - table->used_keys=0; + table->used_keys&= field->part_of_key; } /* All fields are used */ table->used_fields=table->fields; @@ -1961,6 +2062,7 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) Item_cond_and *cond_and=new Item_cond_and(); if (!cond_and) // If not out of memory DBUG_RETURN(1); + cond_and->top_level_item(); uint i,j; for (i=0 ; i < t1->fields ; i++) @@ -1968,7 +2070,6 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) // TODO: This could be optimized to use hashed names if t2 had a hash for (j=0 ; j < t2->fields ; j++) { - key_map tmp_map; if (!my_strcasecmp(t1->field[i]->field_name, t2->field[j]->field_name)) { @@ -1981,20 +2082,8 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) /* Mark field used for table cache */ t1->field[i]->query_id=t2->field[j]->query_id=thd->query_id; cond_and->list.push_back(tmp); - if ((tmp_map=t1->field[i]->part_of_key)) - { - if (!(tmp_map & t1->ref_primary_key)) - t1->used_keys&=tmp_map; - } - else - t1->used_keys=0; - if ((tmp_map=t2->field[j]->part_of_key)) - { - if (!(tmp_map & t2->ref_primary_key)) - t2->used_keys&=tmp_map; - } - else - t2->used_keys=0; + t1->used_keys&= t1->field[i]->part_of_key; + t2->used_keys&= t2->field[j]->part_of_key; break; } } @@ -2022,7 +2111,7 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds) int fill_record(List<Item> &fields,List<Item> &values) { - List_iterator<Item> f(fields),v(values); + List_iterator_fast<Item> f(fields),v(values); Item *value; Item_field *field; DBUG_ENTER("fill_record"); @@ -2030,7 +2119,7 @@ fill_record(List<Item> &fields,List<Item> &values) while ((field=(Item_field*) f++)) { value=v++; - if (value->save_in_field(field->field)) + if (value->save_in_field(field->field, 0)) DBUG_RETURN(1); } DBUG_RETURN(0); @@ -2040,7 +2129,7 @@ fill_record(List<Item> &fields,List<Item> &values) int fill_record(Field **ptr,List<Item> &values) { - List_iterator<Item> v(values); + List_iterator_fast<Item> v(values); Item *value; DBUG_ENTER("fill_record"); @@ -2048,7 +2137,7 @@ fill_record(Field **ptr,List<Item> &values) while ((field = *ptr++)) { value=v++; - if (value->save_in_field(field)) + if (value->save_in_field(field, 0)) DBUG_RETURN(1); } DBUG_RETURN(0); @@ -2103,7 +2192,8 @@ int mysql_create_index(THD *thd, TABLE_LIST *table_list, List<Key> &keys) create_info.db_type=DB_TYPE_DEFAULT; DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->real_name, &create_info, table_list, - fields, keys, drop, alter, (ORDER*)0, FALSE, DUP_ERROR)); + fields, keys, drop, alter, (ORDER*)0, FALSE, + DUP_ERROR)); } @@ -2118,7 +2208,8 @@ int mysql_drop_index(THD *thd, TABLE_LIST *table_list, List<Alter_drop> &drop) create_info.db_type=DB_TYPE_DEFAULT; DBUG_RETURN(mysql_alter_table(thd,table_list->db,table_list->real_name, &create_info, table_list, - fields, keys, drop, alter, (ORDER*)0, FALSE, DUP_ERROR)); + fields, keys, drop, alter, (ORDER*)0, FALSE, + DUP_ERROR)); } /***************************************************************************** @@ -2183,7 +2274,10 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name, THD *in_use; table->version=0L; /* Free when thread is ready */ if (!(in_use=table->in_use)) + { + DBUG_PRINT("info",("Table was not in use")); relink_unused(table); + } else if (in_use != thd) { in_use->some_tables_deleted=1; @@ -2202,6 +2296,17 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name, } pthread_mutex_unlock(&in_use->mysys_var->mutex); } + /* + Now we must abort all tables locks used by this thread + as the thread may be waiting to get a lock for another table + */ + for (TABLE *thd_table= in_use->open_tables; + thd_table ; + thd_table= thd_table->next) + { + if (thd_table->db_stat) // If table is open + mysql_lock_abort_for_thread(thd, thd_table); + } } else result= result || return_if_owned_by_thd; @@ -2213,8 +2318,8 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name, int setup_ftfuncs(THD *thd) { - List_iterator<Item_func_match> li(thd->lex.ftfunc_list), - lj(thd->lex.ftfunc_list); + List_iterator<Item_func_match> li(thd->lex.select->ftfunc_list), + lj(thd->lex.select->ftfunc_list); Item_func_match *ftf, *ftf2; while ((ftf=li++)) @@ -2235,9 +2340,9 @@ int setup_ftfuncs(THD *thd) int init_ftfuncs(THD *thd, bool no_order) { - if (thd->lex.ftfunc_list.elements) + if (thd->lex.select->ftfunc_list.elements) { - List_iterator<Item_func_match> li(thd->lex.ftfunc_list); + List_iterator<Item_func_match> li(thd->lex.select->ftfunc_list); Item_func_match *ifm; DBUG_PRINT("info",("Performing FULLTEXT search")); thd->proc_info="FULLTEXT initialization"; diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 09d436c0c9c..64c62345182 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -1,98 +1,3537 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - +/* Copyright (C) 2000 MySQL AB + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* + Description of the query cache: + +1. Query_cache object consists of + - query cache memory pool (cache) + - queries hash (queries) + - tables hash (tables) + - list of blocks ordered as they allocated in memory +(first_block) + - list of queries block (queries_blocks) + - list of used tables (tables_blocks) + +2. Query cache memory pool (cache) consists of + - table of steps of memory bins allocation + - table of free memory bins + - blocks of memory + +3. Memory blocks + +Every memory block has the following structure: + ++----------------------------------------------------------+ +| Block header (Query_cache_block structure) | ++----------------------------------------------------------+ +|Table of database table lists (used for queries & tables) | ++----------------------------------------------------------+ +| Type depended header | +|(Query_cache_query, Query_cache_table, Query_cache_result)| ++----------------------------------------------------------+ +| Data ... | ++----------------------------------------------------------+ + +Block header consists of: +- type: + FREE Free memory block + QUERY Query block + RESULT Ready to send result + RES_CONT Result's continuation + RES_BEG First block of results, that is not yet complete, + written to cache + RES_INCOMPLETE Allocated for results data block + TABLE Block with database table description + INCOMPLETE The destroyed block +- length of block (length) +- length of data & headers (used) +- physical list links (pnext/pprev) - used for the list of + blocks ordered as they are allocated in physical memory +- logical list links (next/prev) - used for queries block list, tables block + list, free memory block lists and list of results block in query +- number of elements in table of database table list (n_tables) + +4. Query & results blocks + +Query stored in cache consists of following blocks: + +more more +recent+-------------+ old +<-----|Query block 1|------> double linked list of queries block + prev | | next + +-------------+ + <-| table 0 |-> (see "Table of database table lists" description) + <-| table 1 |-> + | ... | +--------------------------+ + +-------------+ +-------------------------+ | +NET | | | V V | +struct| | +-+------------+ +------------+ | +<-----|query header |----->|Result block|-->|Result block|-+ doublelinked +writer| |result| |<--| | list of results + +-------------+ +------------+ +------------+ + |charset | +------------+ +------------+ no table of dbtables + |encoding + | | result | | result | + |query text |<-----| header | | header |------+ + +-------------+parent| | | |parent| + ^ +------------+ +------------+ | + | |result data | |result data | | + | +------------+ +------------+ | + +---------------------------------------------------+ + +First query is registered. During the registration query block is +allocated. This query block is included in query hash and is linked +with appropriate database tables lists (if there is no appropriate +list exists it will be created). + +Later when query has performed results is written into the result blocks. +A result block cannot be smaller then QUERY_CACHE_MIN_RESULT_DATA_SIZE. + +When new result is written to cache it is appended to the last result +block, if no more free space left in the last block, new block is +allocated. + +5. Table of database table lists. + +For quick invalidation of queries all query are linked in lists on used +database tables basis (when table will be changed (insert/delete/...) +this queries will be removed from cache). + +Root of such list is table block: + + +------------+ list of used tables (used while invalidation of +<----| Table |-----> whole database) + prev| block |next +-----------+ + | | +-----------+ |Query block| + | | |Query block| +-----------+ + +------------+ +-----------+ | ... | + +->| table 0 |------>|table 0 |----->| table N |---+ + |+-| |<------| |<-----| |<-+| + || +------------+ | ... | | ... | || + || |table header| +-----------+ +-----------+ || + || +------------+ | ... | | ... | || + || |db name + | +-----------+ +-----------+ || + || |table name | || + || +------------+ || + |+--------------------------------------------------------+| + +----------------------------------------------------------+ + +Table block is included into the tables hash (tables). + +6. Free blocks, free blocks bins & steps of freeblock bins. + +When we just started only one free memory block existed. All query +cache memory (that will be used for block allocation) were +containing in this block. +When a new block is allocated we find most suitable memory block +(minimal of >= required size). If such a block can not be found, we try +to find max block < required size (if we allocate block for results). +If there is no free memory, oldest query is removed from cache, and then +we try to allocate memory. Last step should be repeated until we find +suitable block or until there is no unlocked query found. + +If the block is found and its length more then we need, it should be +split into 2 blocks. +New blocks cannot be smaller then min_allocation_unit_bytes. + +When a block becomes free, its neighbor-blocks should be tested and if +there are free blocks among them, they should be joined into one block. + +Free memory blocks are stored in bins according to their sizes. +The bins are stored in size-descending order. +These bins are distributed (by size) approximately logarithmically. + +First bin (number 0) stores free blocks with +size <= query_cache_size>>QUERY_CACHE_MEM_BIN_FIRST_STEP_PWR2. +It is first (number 0) step. +On the next step distributed (1 + QUERY_CACHE_MEM_BIN_PARTS_INC) * +QUERY_CACHE_MEM_BIN_PARTS_MUL bins. This bins allocated in interval from +query_cache_size>>QUERY_CACHE_MEM_BIN_FIRST_STEP_PWR2 to +query_cache_size>>QUERY_CACHE_MEM_BIN_FIRST_STEP_PWR2 >> +QUERY_CACHE_MEM_BIN_STEP_PWR2 +... +On each step interval decreases in 2 power of +QUERY_CACHE_MEM_BIN_STEP_PWR2 +times, number of bins (that distributed on this step) increases. If on +the previous step there were N bins distributed , on the current there +would be distributed +(N + QUERY_CACHE_MEM_BIN_PARTS_INC) * QUERY_CACHE_MEM_BIN_PARTS_MUL +bins. +Last distributed bin stores blocks with size near min_allocation_unit +bytes. + +For example: + query_cache_size>>QUERY_CACHE_MEM_BIN_FIRST_STEP_PWR2 = 100, + min_allocation_unit = 17, + QUERY_CACHE_MEM_BIN_STEP_PWR2 = 1, + QUERY_CACHE_MEM_BIN_PARTS_INC = 1, + QUERY_CACHE_MEM_BIN_PARTS_MUL = 1 + (in followed picture showed right (low) bound of bin): + + | 100>>1 50>>1 |25>>1| + | | | | | | + | 100 75 50 41 33 25 21 18 15| 12 | - bins right (low) bounds + + |\---/\-----/\--------/\--------|---/ | + | 0 1 2 3 | | - steps + \-----------------------------/ \---/ + bins that we store in cache this bin showed for example only + + +Calculation of steps/bins distribution is performed only when query cache +is resized. + +When we need to find appropriate bin, first we should find appropriate +step, then we should calculate number of bins that are using data +stored in Query_cache_memory_bin_step structure. + +Free memory blocks are sorted in bins in lists with size-ascending order +(more small blocks needed frequently then bigger one). + +7. Packing cache. + +Query cache packing is divided into two operation: + - pack_cache + - join_results + +pack_cache moved all blocks to "top" of cache and create one block of free +space at the "bottom": + + before pack_cache after pack_cache + +-------------+ +-------------+ + | query 1 | | query 1 | + +-------------+ +-------------+ + | table 1 | | table 1 | + +-------------+ +-------------+ + | results 1.1 | | results 1.1 | + +-------------+ +-------------+ + | free | | query 2 | + +-------------+ +-------------+ + | query 2 | | table 2 | + +-------------+ ---> +-------------+ + | table 2 | | results 1.2 | + +-------------+ +-------------+ + | results 1.2 | | results 2 | + +-------------+ +-------------+ + | free | | free | + +-------------+ | | + | results 2 | | | + +-------------+ | | + | free | | | + +-------------+ +-------------+ + +pack_cache scan blocks in physical address order and move every non-free +block "higher". + +pack_cach remove every free block it finds. The length of the deleted block +is accumulated to the "gap". All non free blocks should be shifted with the +"gap" step. + +join_results scans all complete queries. If the results of query are not +stored in the same block, join_results tries to move results so, that they +are stored in one block. + + before join_results after join_results + +-------------+ +-------------+ + | query 1 | | query 1 | + +-------------+ +-------------+ + | table 1 | | table 1 | + +-------------+ +-------------+ + | results 1.1 | | free | + +-------------+ +-------------+ + | query 2 | | query 2 | + +-------------+ +-------------+ + | table 2 | | table 2 | + +-------------+ ---> +-------------+ + | results 1.2 | | free | + +-------------+ +-------------+ + | results 2 | | results 2 | + +-------------+ +-------------+ + | free | | results 1 | + | | | | + | | +-------------+ + | | | free | + | | | | + +-------------+ +-------------+ + +If join_results allocated new block(s) then we need call pack_cache again. + +TODO list: + + - Delayed till after-parsing qache answer (for column rights processing) + - Optimize cache resizing + - if new_size < old_size then pack & shrink + - if new_size > old_size copy cached query to new cache + - Move MRG_MYISAM table type processing to handlers, something like: + tables_used->table->file->register_used_filenames(callback, + first_argument); +*/ + #include "mysql_priv.h" +#ifdef HAVE_QUERY_CACHE #include <m_ctype.h> #include <my_dir.h> #include <hash.h> +#include "sql_acl.h" +#include "ha_myisammrg.h" +#ifndef MASTER +#include "../srclib/myisammrg/myrg_def.h" +#else +#include "../myisammrg/myrg_def.h" +#endif +#include <assert.h> + +#if defined(EXTRA_DEBUG) && !defined(DBUG_OFF) +#define MUTEX_LOCK(M) { DBUG_PRINT("lock", ("mutex lock 0x%lx", (ulong)(M))); \ + pthread_mutex_lock(M);} +#define MUTEX_UNLOCK(M) {DBUG_PRINT("lock", ("mutex unlock 0x%lx",\ + (ulong)(M))); pthread_mutex_unlock(M);} +#define RW_WLOCK(M) {DBUG_PRINT("lock", ("rwlock wlock 0x%lx",(ulong)(M))); \ + if (!rw_wrlock(M)) DBUG_PRINT("lock", ("rwlock wlock ok")) \ + else DBUG_PRINT("lock", ("rwlock wlock FAILED %d", errno)); } +#define RW_RLOCK(M) {DBUG_PRINT("lock", ("rwlock rlock 0x%lx", (ulong)(M))); \ + if (!rw_rdlock(M)) DBUG_PRINT("lock", ("rwlock rlock ok")) \ + else DBUG_PRINT("lock", ("rwlock wlock FAILED %d", errno)); } +#define RW_UNLOCK(M) {DBUG_PRINT("lock", ("rwlock unlock 0x%lx",(ulong)(M))); \ + if (!rw_unlock(M)) DBUG_PRINT("lock", ("rwlock unlock ok")) \ + else DBUG_PRINT("lock", ("rwlock unlock FAILED %d", errno)); } +#define STRUCT_LOCK(M) {DBUG_PRINT("lock", ("%d struct lock...",__LINE__)); \ + pthread_mutex_lock(M);DBUG_PRINT("lock", ("struct lock OK"));} +#define STRUCT_UNLOCK(M) { \ + DBUG_PRINT("lock", ("%d struct unlock...",__LINE__)); \ + pthread_mutex_unlock(M);DBUG_PRINT("lock", ("struct unlock OK"));} +#define BLOCK_LOCK_WR(B) {DBUG_PRINT("lock", ("%d LOCK_WR 0x%lx",\ + __LINE__,(ulong)(B))); \ + B->query()->lock_writing();} +#define BLOCK_LOCK_RD(B) {DBUG_PRINT("lock", ("%d LOCK_RD 0x%lx",\ + __LINE__,(ulong)(B))); \ + B->query()->lock_reading();} +#define BLOCK_UNLOCK_WR(B) { \ + DBUG_PRINT("lock", ("%d UNLOCK_WR 0x%lx",\ + __LINE__,(ulong)(B)));B->query()->unlock_writing();} +#define BLOCK_UNLOCK_RD(B) { \ + DBUG_PRINT("lock", ("%d UNLOCK_RD 0x%lx",\ + __LINE__,(ulong)(B)));B->query()->unlock_reading();} +#define DUMP(C) DBUG_EXECUTE("qcache", {\ + (C)->cache_dump(); (C)->queries_dump();(C)->tables_dump();}) +#else +#define MUTEX_LOCK(M) pthread_mutex_lock(M) +#define MUTEX_UNLOCK(M) pthread_mutex_unlock(M) +#define RW_WLOCK(M) rw_wrlock(M) +#define RW_RLOCK(M) rw_rdlock(M) +#define RW_UNLOCK(M) rw_unlock(M) +#define STRUCT_LOCK(M) pthread_mutex_lock(M) +#define STRUCT_UNLOCK(M) pthread_mutex_unlock(M) +#define BLOCK_LOCK_WR(B) B->query()->lock_writing() +#define BLOCK_LOCK_RD(B) B->query()->lock_reading() +#define BLOCK_UNLOCK_WR(B) B->query()->unlock_writing() +#define BLOCK_UNLOCK_RD(B) B->query()->unlock_reading() +#define DUMP(C) +#endif + +#ifdef FN_NO_CASE_SENCE +#define DB_NAME_PREPROCESS(C) tolower(C) +#else +#define DB_NAME_PREPROCESS(C) (C) +#endif + +const char *query_cache_type_names[]= { "OFF", "ON", "DEMAND",NullS }; +TYPELIB query_cache_type_typelib= +{ + array_elements(query_cache_type_names)-1,"", query_cache_type_names +}; + +/***************************************************************************** + Query_cache_block_table method(s) +*****************************************************************************/ + +inline Query_cache_block * Query_cache_block_table::block() +{ + return (Query_cache_block *)(((byte*)this) - + ALIGN_SIZE(sizeof(Query_cache_block_table)*n) - + ALIGN_SIZE(sizeof(Query_cache_block))); +}; + +/***************************************************************************** + Query_cache_block method(s) +*****************************************************************************/ + +void Query_cache_block::init(ulong block_length) +{ + DBUG_ENTER("Query_cache_block::init"); + DBUG_PRINT("qcache", ("init block 0x%lx length: %lu", (ulong) this, + block_length)); + length = block_length; + used = 0; + type = Query_cache_block::FREE; + n_tables = 0; + DBUG_VOID_RETURN; +} + +void Query_cache_block::destroy() +{ + DBUG_ENTER("Query_cache_block::destroy"); + DBUG_PRINT("qcache", ("destroy block 0x%lx, type %d", + (ulong) this, type)); + type = INCOMPLETE; + DBUG_VOID_RETURN; +} + +inline uint Query_cache_block::headers_len() +{ + return (ALIGN_SIZE(sizeof(Query_cache_block_table)*n_tables) + + ALIGN_SIZE(sizeof(Query_cache_block))); +} + +inline gptr Query_cache_block::data(void) +{ + return (gptr)( ((byte*)this) + headers_len() ); +} + +inline Query_cache_query * Query_cache_block::query() +{ +#ifndef DBUG_OFF + if (type != QUERY) + query_cache.wreck(__LINE__, "incorrect block type"); +#endif + return (Query_cache_query *) data(); +} + +inline Query_cache_table * Query_cache_block::table() +{ +#ifndef DBUG_OFF + if (type != TABLE) + query_cache.wreck(__LINE__, "incorrect block type"); +#endif + return (Query_cache_table *) data(); +} + +inline Query_cache_result * Query_cache_block::result() +{ +#ifndef DBUG_OFF + if (type != RESULT && type != RES_CONT && type != RES_BEG && + type != RES_INCOMPLETE) + query_cache.wreck(__LINE__, "incorrect block type"); +#endif + return (Query_cache_result *) data(); +} + +inline Query_cache_block_table * Query_cache_block::table(TABLE_COUNTER_TYPE n) +{ + return ((Query_cache_block_table *) + (((byte*)this)+ALIGN_SIZE(sizeof(Query_cache_block)) + + n*sizeof(Query_cache_block_table))); +} + + +/***************************************************************************** + * Query_cache_table method(s) + *****************************************************************************/ + +extern "C" +{ +byte *query_cache_table_get_key(const byte *record, uint *length, + my_bool not_used __attribute__((unused))) +{ + Query_cache_block* table_block = (Query_cache_block*) record; + *length = (table_block->used - table_block->headers_len() - + ALIGN_SIZE(sizeof(Query_cache_table))); + return (((byte *) table_block->data()) + + ALIGN_SIZE(sizeof(Query_cache_table))); +} +} + +/***************************************************************************** + Query_cache_query methods +*****************************************************************************/ + +/* + Following methods work for block read/write locking only in this + particular case and in interaction with structure_guard_mutex. + + Lock for write prevents any other locking. (exclusive use) + Lock for read prevents only locking for write. +*/ + +inline void Query_cache_query::lock_writing() +{ + RW_WLOCK(&lock); +} -#define SQL_CACHE_LENGTH 30 // 300 crashes apple gcc. -HASH sql_cache; -static LEX lex_array_static[SQL_CACHE_LENGTH]; -LEX * lex_array = lex_array_static; -int last_lex_array_item = SQL_CACHE_LENGTH - 1; +/* + Needed for finding queries, that we may delete from cache. + We don't want to wait while block become unlocked. In addition, + block locking means that query is now used and we don't need to + remove it. +*/ -/* Function to return a text string from a LEX struct */ -static byte *cache_key(const byte *record, uint *length, my_bool not_used) +my_bool Query_cache_query::try_lock_writing() { -#ifdef QQ - LEX *lex=(LEX*) record; - *length = lex->sql_query_length; - // *length = strlen(lex->ptr); - return (byte*) lex->sql_query_text; - // return (byte*) lex->ptr; + DBUG_ENTER("Query_cache_block::try_lock_writing"); + if (rw_trywrlock(&lock)!=0) + { + DBUG_PRINT("info", ("can't lock rwlock")); + DBUG_RETURN(0); + } + DBUG_PRINT("info", ("rwlock 0x%lx locked", (ulong) &lock)); + DBUG_RETURN(1); +} + + +inline void Query_cache_query::lock_reading() +{ + RW_RLOCK(&lock); +} + + +inline void Query_cache_query::unlock_writing() +{ + RW_UNLOCK(&lock); +} + + +inline void Query_cache_query::unlock_reading() +{ + RW_UNLOCK(&lock); +} + + +void Query_cache_query::init_n_lock() +{ + DBUG_ENTER("Query_cache_query::init_n_lock"); + res=0; wri = 0; len = 0; + my_rwlock_init(&lock, NULL); + lock_writing(); + DBUG_PRINT("qcache", ("inited & locked query for block 0x%lx", + ((byte*) this)-ALIGN_SIZE(sizeof(Query_cache_block)))); + DBUG_VOID_RETURN; +} + + +void Query_cache_query::unlock_n_destroy() +{ + DBUG_ENTER("Query_cache_query::unlock_n_destroy"); + DBUG_PRINT("qcache", ("destroyed & unlocked query for block 0x%lx", + ((byte*)this)-ALIGN_SIZE(sizeof(Query_cache_block)))); + /* + The following call is not needed on system where one can destroy an + active semaphore + */ + this->unlock_writing(); + rwlock_destroy(&lock); + DBUG_VOID_RETURN; +} + + +extern "C" +{ +byte *query_cache_query_get_key(const byte *record, uint *length, + my_bool not_used) +{ + Query_cache_block *query_block = (Query_cache_block*) record; + *length = (query_block->used - query_block->headers_len() - + ALIGN_SIZE(sizeof(Query_cache_query))); + return (((byte *) query_block->data()) + + ALIGN_SIZE(sizeof(Query_cache_query))); +} +} + +/***************************************************************************** + Functions to store things into the query cache +*****************************************************************************/ + +/* + Insert the packet into the query cache. + This should only be called if net->query_cache_query != 0 +*/ + +void query_cache_insert(NET *net, const char *packet, ulong length) +{ + DBUG_ENTER("query_cache_insert"); + +#ifndef DBUG_OFF + // Check if we have called query_cache.wreck() (which disables the cache) + if (query_cache.query_cache_size == 0) + DBUG_VOID_RETURN; +#endif + + STRUCT_LOCK(&query_cache.structure_guard_mutex); + Query_cache_block *query_block = ((Query_cache_block*) + net->query_cache_query); + if (query_block) + { + Query_cache_query *header = query_block->query(); + Query_cache_block *result = header->result(); + + DUMP(&query_cache); + BLOCK_LOCK_WR(query_block); + DBUG_PRINT("qcache", ("insert packet %lu bytes long",length)); + + /* + On success STRUCT_UNLOCK(&query_cache.structure_guard_mutex) will be + done by query_cache.append_result_data if success (if not we need + query_cache.structure_guard_mutex locked to free query) + */ + if (!query_cache.append_result_data(&result, length, (gptr) packet, + query_block)) + { + query_cache.refused++; + DBUG_PRINT("warning", ("Can't append data")); + header->result(result); + DBUG_PRINT("qcache", ("free query 0x%lx", (ulong) query_block)); + // The following call will remove the lock on query_block + query_cache.free_query(query_block); + // append_result_data no success => we need unlock + STRUCT_UNLOCK(&query_cache.structure_guard_mutex); + DBUG_VOID_RETURN; + } + header->result(result); + BLOCK_UNLOCK_WR(query_block); + } + else + STRUCT_UNLOCK(&query_cache.structure_guard_mutex); + DBUG_EXECUTE("check_querycache",query_cache.check_integrity(0);); + DBUG_VOID_RETURN; +} + + +void query_cache_abort(NET *net) +{ + DBUG_ENTER("query_cache_abort"); + +#ifndef DBUG_OFF + // Check if we have called query_cache.wreck() (which disables the cache) + if (query_cache.query_cache_size == 0) + DBUG_VOID_RETURN; +#endif + if (net->query_cache_query != 0) // Quick check on unlocked structure + { + STRUCT_LOCK(&query_cache.structure_guard_mutex); + Query_cache_block *query_block = ((Query_cache_block*) + net->query_cache_query); + if (query_block) // Test if changed by other thread + { + DUMP(&query_cache); + BLOCK_LOCK_WR(query_block); + // The following call will remove the lock on query_block + query_cache.free_query(query_block); + } + net->query_cache_query=0; + DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1);); + STRUCT_UNLOCK(&query_cache.structure_guard_mutex); + } + DBUG_VOID_RETURN; +} + + +void query_cache_end_of_result(NET *net) +{ + DBUG_ENTER("query_cache_end_of_result"); + +#ifndef DBUG_OFF + // Check if we have called query_cache.wreck() (which disables the cache) + if (query_cache.query_cache_size == 0) DBUG_VOID_RETURN; +#endif + + if (net->query_cache_query != 0) // Quick check on unlocked structure + { + STRUCT_LOCK(&query_cache.structure_guard_mutex); + Query_cache_block *query_block = ((Query_cache_block*) + net->query_cache_query); + if (query_block) + { + DUMP(&query_cache); + BLOCK_LOCK_WR(query_block); + Query_cache_query *header = query_block->query(); + Query_cache_block *last_result_block = header->result()->prev; + ulong allign_size = ALIGN_SIZE(last_result_block->used); + ulong len = max(query_cache.min_allocation_unit, allign_size); + if (last_result_block->length >= query_cache.min_allocation_unit + len) + query_cache.split_block(last_result_block,len); + STRUCT_UNLOCK(&query_cache.structure_guard_mutex); + +#ifndef DBUG_OFF + if (header->result() == 0) + { + DBUG_PRINT("error", ("end of data whith no result. query '%s'", + header->query())); + query_cache.wreck(__LINE__, ""); + DBUG_VOID_RETURN; + } #endif - return 0; + header->found_rows(current_thd->limit_found_rows); + header->result()->type = Query_cache_block::RESULT; + header->writer(0); + BLOCK_UNLOCK_WR(query_block); + } + else + { + // Cache was flushed or resized and query was deleted => do nothing + STRUCT_UNLOCK(&query_cache.structure_guard_mutex); + } + net->query_cache_query=0; + DBUG_EXECUTE("check_querycache",query_cache.check_integrity(0);); + } + DBUG_VOID_RETURN; +} + +void query_cache_invalidate_by_MyISAM_filename(const char *filename) +{ + query_cache.invalidate_by_MyISAM_filename(filename); + DBUG_EXECUTE("check_querycache",query_cache.check_integrity(0);); +} + + +/***************************************************************************** + Query_cache methods +*****************************************************************************/ + +Query_cache::Query_cache(ulong query_cache_limit_arg, + ulong min_allocation_unit_arg, + ulong min_result_data_size_arg, + uint def_query_hash_size_arg, + uint def_table_hash_size_arg) + :query_cache_size(0), + query_cache_limit(query_cache_limit_arg), + queries_in_cache(0), hits(0), inserts(0), refused(0), + total_blocks(0), lowmem_prunes(0), + min_allocation_unit(ALIGN_SIZE(min_allocation_unit_arg)), + min_result_data_size(ALIGN_SIZE(min_result_data_size_arg)), + def_query_hash_size(ALIGN_SIZE(def_query_hash_size_arg)), + def_table_hash_size(ALIGN_SIZE(def_table_hash_size_arg)), + initialized(0) +{ + ulong min_needed= (ALIGN_SIZE(sizeof(Query_cache_block)) + + ALIGN_SIZE(sizeof(Query_cache_block_table)) + + ALIGN_SIZE(sizeof(Query_cache_query)) + 3); + set_if_bigger(min_allocation_unit,min_needed); + this->min_allocation_unit= ALIGN_SIZE(min_allocation_unit); + set_if_bigger(this->min_result_data_size,min_allocation_unit); +} + + +ulong Query_cache::resize(ulong query_cache_size_arg) +{ + DBUG_ENTER("Query_cache::resize"); + DBUG_PRINT("qcache", ("from %lu to %lu",query_cache_size, + query_cache_size_arg)); + free_cache(0); + query_cache_size= query_cache_size_arg; + DBUG_RETURN(init_cache()); +} + + +void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used) +{ + TABLE_COUNTER_TYPE local_tables; + ulong tot_length; + DBUG_ENTER("Query_cache::store_query"); + if (query_cache_size == 0) + DBUG_VOID_RETURN; + + if ((local_tables = is_cacheable(thd, thd->query_length, + thd->query, &thd->lex, tables_used))) + { + NET *net = &thd->net; + byte flags = (thd->client_capabilities & CLIENT_LONG_FLAG ? 0x80 : 0); + STRUCT_LOCK(&structure_guard_mutex); + + if (query_cache_size == 0) + DBUG_VOID_RETURN; + DUMP(this); + + /* Key is query + database + flag */ + if (thd->db_length) + { + memcpy(thd->query+thd->query_length+1, thd->db, thd->db_length); + DBUG_PRINT("qcache", ("database : %s length %u", + thd->db, thd->db_length)); + } + else + { + DBUG_PRINT("qcache", ("No active database")); + } + /* + Prepare flags: + most significant bit - CLIENT_LONG_FLAG, + other - charset number (0 no charset convertion) + */ + if (thd->variables.convert_set != 0) + { + flags|= (byte) thd->variables.convert_set->number(); + DBUG_ASSERT(thd->variables.convert_set->number() < 128); + } + tot_length=thd->query_length+thd->db_length+2; + thd->query[tot_length-1] = (char) flags; + + /* Check if another thread is processing the same query? */ + Query_cache_block *competitor = (Query_cache_block *) + hash_search(&queries, (byte*) thd->query, tot_length); + DBUG_PRINT("qcache", ("competitor 0x%lx, flags %x", (ulong) competitor, + flags)); + if (competitor == 0) + { + /* Query is not in cache and no one is working with it; Store it */ + Query_cache_block *query_block; + query_block= write_block_data(tot_length, (gptr) thd->query, + ALIGN_SIZE(sizeof(Query_cache_query)), + Query_cache_block::QUERY, local_tables, 1); + if (query_block != 0) + { + DBUG_PRINT("qcache", ("query block 0x%lx allocated, %lu", + (ulong) query_block, query_block->used)); + + Query_cache_query *header = query_block->query(); + header->init_n_lock(); + if (hash_insert(&queries, (byte*) query_block)) + { + refused++; + DBUG_PRINT("qcache", ("insertion in query hash")); + header->unlock_n_destroy(); + free_memory_block(query_block); + STRUCT_UNLOCK(&structure_guard_mutex); + goto end; + } + if (!register_all_tables(query_block, tables_used, local_tables)) + { + refused++; + DBUG_PRINT("warning", ("tables list including failed")); + hash_delete(&queries, (byte *) query_block); + header->unlock_n_destroy(); + free_memory_block(query_block); + STRUCT_UNLOCK(&structure_guard_mutex); + goto end; + } + double_linked_list_simple_include(query_block, &queries_blocks); + inserts++; + queries_in_cache++; + STRUCT_UNLOCK(&structure_guard_mutex); + + net->query_cache_query= (gptr) query_block; + header->writer(net); + // init_n_lock make query block locked + BLOCK_UNLOCK_WR(query_block); + } + else + { + // We have not enough memory to store query => do nothing + refused++; + STRUCT_UNLOCK(&structure_guard_mutex); + DBUG_PRINT("warning", ("Can't allocate query")); + } + } + else + { + // Another thread is processing the same query => do nothing + refused++; + STRUCT_UNLOCK(&structure_guard_mutex); + DBUG_PRINT("qcache", ("Another thread process same query")); + } + } + else + statistic_increment(refused, &structure_guard_mutex); + +end: + DBUG_VOID_RETURN; +} + +/* + Check if the query is in the cache. If it was cached, send it + to the user. + + RESULTS + 1 Query was not cached. + 0 The query was cached and user was sent the result. + -1 The query was cached but we didn't have rights to use it. + No error is sent to the client yet. +*/ + + + +int +Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length) +{ + Query_cache_query *query; + Query_cache_block *first_result_block, *result_block; + Query_cache_block_table *block_table, *block_table_end; + ulong tot_length; + byte flags; + DBUG_ENTER("Query_cache::send_result_to_client"); + + if (query_cache_size == 0 || + /* + it is not possible to check has_transactions() function of handler + because tables not opened yet + */ + (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) || + thd->variables.query_cache_type == 0) + + goto err; + + /* Check that we haven't forgot to reset the query cache variables */ + DBUG_ASSERT(thd->net.query_cache_query == 0); + + if (!thd->safe_to_cache_query) + { + DBUG_PRINT("qcache", ("SELECT is non-cacheable")); + goto err; + } + + /* + Test if the query is a SELECT + (pre-space is removed in dispatch_command) + */ + if (toupper(sql[0]) != 'S' || toupper(sql[1]) != 'E' || + toupper(sql[2]) !='L') + { + DBUG_PRINT("qcache", ("The statement is not a SELECT; Not cached")); + goto err; + } + + STRUCT_LOCK(&structure_guard_mutex); + if (query_cache_size == 0) + { + DBUG_PRINT("qcache", ("query cache disabled")); + goto err_unlock; + } + Query_cache_block *query_block; + + tot_length=query_length+thd->db_length+2; + if (thd->db_length) + { + memcpy(sql+query_length+1, thd->db, thd->db_length); + DBUG_PRINT("qcache", ("database: '%s' length %u", + thd->db, thd->db_length)); + } + else + { + DBUG_PRINT("qcache", ("No active database")); + } + /* + prepare flags: + Most significant bit - CLIENT_LONG_FLAG, + Other - charset number (0 no charset convertion) + */ + flags = (thd->client_capabilities & CLIENT_LONG_FLAG ? 0x80 : 0); + if (thd->variables.convert_set != 0) + { + flags |= (byte) thd->variables.convert_set->number(); + DBUG_ASSERT(thd->variables.convert_set->number() < 128); + } + sql[tot_length-1] = (char) flags; + query_block = (Query_cache_block *) hash_search(&queries, (byte*) sql, + tot_length); + /* Quick abort on unlocked data */ + if (query_block == 0 || + query_block->query()->result() == 0 || + query_block->query()->result()->type != Query_cache_block::RESULT) + { + DBUG_PRINT("qcache", ("No query in query hash or no results")); + goto err_unlock; + } + DBUG_PRINT("qcache", ("Query in query hash 0x%lx", (ulong)query_block)); + + /* Now lock and test that nothing changed while blocks was unlocked */ + BLOCK_LOCK_RD(query_block); + + query = query_block->query(); + result_block= first_result_block= query->result(); + + if (result_block == 0 || result_block->type != Query_cache_block::RESULT) + { + /* The query is probably yet processed */ + DBUG_PRINT("qcache", ("query found, but no data or data incomplete")); + BLOCK_UNLOCK_RD(query_block); + goto err_unlock; + } + DBUG_PRINT("qcache", ("Query have result 0x%lx", (ulong) query)); + + // Check access; + block_table= query_block->table(0); + block_table_end= block_table+query_block->n_tables; + for (; block_table != block_table_end; block_table++) + { + TABLE_LIST table_list; + bzero((char*) &table_list,sizeof(table_list)); + + Query_cache_table *table = block_table->parent; + table_list.db = table->db(); + table_list.alias= table_list.real_name= table->table(); + if (check_table_access(thd,SELECT_ACL,&table_list,1)) + { + DBUG_PRINT("qcache", + ("probably no SELECT access to %s.%s => return to normal processing", + table_list.db, table_list.alias)); + refused++; // This is actually a hit + STRUCT_UNLOCK(&structure_guard_mutex); + thd->safe_to_cache_query=0; // Don't try to cache this + BLOCK_UNLOCK_RD(query_block); + DBUG_RETURN(-1); // Privilege error + } + if (table_list.grant.want_privilege) + { + DBUG_PRINT("qcache", ("Need to check column privileges for %s.%s", + table_list.db, table_list.alias)); + BLOCK_UNLOCK_RD(query_block); + thd->safe_to_cache_query=0; // Don't try to cache this + goto err_unlock; // Parse query + } + } + move_to_query_list_end(query_block); + hits++; + STRUCT_UNLOCK(&structure_guard_mutex); + + /* + Send cached result to client + */ + do + { + DBUG_PRINT("qcache", ("Results (len %lu, used %lu, headers %lu)", + result_block->length, result_block->used, + result_block->headers_len()+ + ALIGN_SIZE(sizeof(Query_cache_result)))); + + Query_cache_result *result = result_block->result(); + if (net_real_write(&thd->net, result->data(), + result_block->used - + result_block->headers_len() - + ALIGN_SIZE(sizeof(Query_cache_result)))) + break; // Client aborted + result_block = result_block->next; + } while (result_block != first_result_block); + + thd->limit_found_rows = query->found_rows(); + + BLOCK_UNLOCK_RD(query_block); + DBUG_RETURN(1); // Result sent to client + +err_unlock: + STRUCT_UNLOCK(&structure_guard_mutex); +err: + DBUG_RETURN(0); // Query was not cached +} + + +/* + Remove all cached queries that uses any of the tables in the list +*/ + +void Query_cache::invalidate(THD *thd, TABLE_LIST *tables_used, + my_bool using_transactions) +{ + DBUG_ENTER("Query_cache::invalidate (table list)"); + if (query_cache_size > 0) + { + STRUCT_LOCK(&structure_guard_mutex); + if (query_cache_size > 0) + { + DUMP(this); + + using_transactions = using_transactions && + (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)); + for (; tables_used; tables_used=tables_used->next) + { + DBUG_ASSERT(!using_transactions || tables_used->table!=0); + if (using_transactions && + tables_used->table->file->has_transactions()) + /* + Tables_used->table can't be 0 in transaction. + Only 'drop' invalidate not opened table, but 'drop' + force transaction finish. + */ + thd->add_changed_table(tables_used->table); + else + invalidate_table(tables_used); + } + } + STRUCT_UNLOCK(&structure_guard_mutex); + } + DBUG_VOID_RETURN; +} + +void Query_cache::invalidate(CHANGED_TABLE_LIST *tables_used) +{ + DBUG_ENTER("Query_cache::invalidate (changed table list)"); + if (query_cache_size > 0 && tables_used) + { + STRUCT_LOCK(&structure_guard_mutex); + if (query_cache_size > 0) + { + DUMP(this); + for (; tables_used; tables_used=tables_used->next) + { + invalidate_table((byte*) tables_used->key, tables_used->key_length); + DBUG_PRINT("qcache", (" db %s, table %s", tables_used->key, + tables_used->key+ + strlen(tables_used->key)+1)); + } + } + STRUCT_UNLOCK(&structure_guard_mutex); + } + DBUG_VOID_RETURN; +} + +/* + Remove all cached queries that uses the given table +*/ + +void Query_cache::invalidate(THD *thd, TABLE *table, + my_bool using_transactions) +{ + DBUG_ENTER("Query_cache::invalidate (table)"); + + if (query_cache_size > 0) + { + STRUCT_LOCK(&structure_guard_mutex); + if (query_cache_size > 0) + { + using_transactions = using_transactions && + (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)); + if (using_transactions && table->file->has_transactions()) + thd->add_changed_table(table); + else + invalidate_table(table); + } + STRUCT_UNLOCK(&structure_guard_mutex); + } + DBUG_VOID_RETURN; +} + +void Query_cache::invalidate(THD *thd, const char *key, uint32 key_length, + my_bool using_transactions) +{ + DBUG_ENTER("Query_cache::invalidate (key)"); + + if (query_cache_size > 0) + { + using_transactions = using_transactions && + (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)); + if (using_transactions) // used for innodb => has_transactions() is TRUE + thd->add_changed_table(key, key_length); + else + { + STRUCT_LOCK(&structure_guard_mutex); + if (query_cache_size > 0) + invalidate_table((byte*)key, key_length); + STRUCT_UNLOCK(&structure_guard_mutex); + } + } + DBUG_VOID_RETURN; +} + +/* + Remove all cached queries that uses the given database +*/ + +void Query_cache::invalidate(char *db) +{ + DBUG_ENTER("Query_cache::invalidate (db)"); + if (query_cache_size > 0) + { + STRUCT_LOCK(&structure_guard_mutex); + if (query_cache_size > 0) + { + DUMP(this); + /* invalidate_table reduce list while only root of list remain */ + while (tables_blocks !=0 ) + invalidate_table(tables_blocks); + } + STRUCT_UNLOCK(&structure_guard_mutex); + } + DBUG_VOID_RETURN; +} + + +void Query_cache::invalidate_by_MyISAM_filename(const char *filename) +{ + DBUG_ENTER("Query_cache::invalidate_by_MyISAM_filename"); + if (query_cache_size > 0) + { + /* Calculate the key outside the lock to make the lock shorter */ + char key[MAX_DBKEY_LENGTH]; + uint32 db_length; + uint key_length= filename_2_table_key(key, filename, &db_length); + STRUCT_LOCK(&structure_guard_mutex); + if (query_cache_size > 0) // Safety if cache removed + { + Query_cache_block *table_block; + if ((table_block = (Query_cache_block*) hash_search(&tables, + (byte*) key, + key_length))) + invalidate_table(table_block); + } + STRUCT_UNLOCK(&structure_guard_mutex); + } + DBUG_VOID_RETURN; +} + + /* Remove all queries from cache */ + +void Query_cache::flush() +{ + DBUG_ENTER("Query_cache::flush"); + STRUCT_LOCK(&structure_guard_mutex); + if (query_cache_size > 0) + { + DUMP(this); + flush_cache(); + DUMP(this); + } + + DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1);); + STRUCT_UNLOCK(&structure_guard_mutex); + DBUG_VOID_RETURN; } -/* At the moment we do not really want to do anything upon delete */ -static void free_cache_entry(void *entry) + /* Join result in cache in 1 block (if result length > join_limit) */ + +void Query_cache::pack(ulong join_limit, uint iteration_limit) { + DBUG_ENTER("Query_cache::pack"); + uint i = 0; + do + { + pack_cache(); + } while ((++i < iteration_limit) && join_results(join_limit)); + DBUG_VOID_RETURN; } -/* Initialization of the SQL cache hash -- should be called during - the bootstrap stage */ -bool sql_cache_init(void) + +void Query_cache::destroy() { - if (query_buff_size) + DBUG_ENTER("Query_cache::destroy"); + if (!initialized) + { + DBUG_PRINT("qcache", ("Query Cache not initialized")); + } + else { - VOID(hash_init(&sql_cache, 4096, 0, 0, - cache_key, - (void (*)(void*)) free_cache_entry, - 0)); + free_cache(1); + pthread_mutex_destroy(&structure_guard_mutex); + initialized = 0; } - return 0; + DBUG_VOID_RETURN; } -/* Clearing the SQL cache hash -- during shutdown */ -void sql_cache_free(void) + +/***************************************************************************** + init/destroy +*****************************************************************************/ + +void Query_cache::init() { - hash_free(&sql_cache); + DBUG_ENTER("Query_cache::init"); + pthread_mutex_init(&structure_guard_mutex,MY_MUTEX_INIT_FAST); + initialized = 1; + DBUG_VOID_RETURN; } -/* Finds whether the SQL command is already in the cache, at any case - establishes correct LEX structure in the THD (either from - cache or a new one) */ -int sql_cache_hit(THD *thd, char *sql, uint length) +ulong Query_cache::init_cache() { -#ifdef QQ - LEX *ptr; - ptr = (LEX *)hash_search(&sql_cache, sql, length); - if (ptr) { - fprintf(stderr, "Query `%s' -- hit in the cache (%p)\n", ptr->sql_query_text, ptr); - thd->lex_ptr = ptr; - ptr->thd = thd; - } else { - thd->lex_ptr = ptr = lex_array + last_lex_array_item--; + uint mem_bin_count, num, step; + ulong mem_bin_size, prev_size, inc; + ulong additional_data_size, max_mem_bin_size, approx_additional_data_size; + int align; - lex_start(thd, (uchar *)sql, length); + DBUG_ENTER("Query_cache::init_cache"); + if (!initialized) + init(); + approx_additional_data_size = (sizeof(Query_cache) + + sizeof(gptr)*(def_query_hash_size+ + def_query_hash_size)); + if (query_cache_size < approx_additional_data_size) + goto err; - if (hash_insert(&sql_cache, (const byte *)ptr)) { - fprintf(stderr, "Out of memory during hash_insert?\n"); + query_cache_size-= approx_additional_data_size; + align= query_cache_size % ALIGN_SIZE(1); + if (align) + { + query_cache_size-= align; + approx_additional_data_size+= align; + } + + /* + Count memory bins number. + Check section 6. in start comment for the used algorithm. + */ + + max_mem_bin_size = query_cache_size >> QUERY_CACHE_MEM_BIN_FIRST_STEP_PWR2; + mem_bin_count = (uint) ((1 + QUERY_CACHE_MEM_BIN_PARTS_INC) * + QUERY_CACHE_MEM_BIN_PARTS_MUL); + mem_bin_num = 1; + mem_bin_steps = 1; + mem_bin_size = max_mem_bin_size >> QUERY_CACHE_MEM_BIN_STEP_PWR2; + prev_size = 0; + while (mem_bin_size > min_allocation_unit) + { + mem_bin_num += mem_bin_count; + prev_size = mem_bin_size; + mem_bin_size >>= QUERY_CACHE_MEM_BIN_STEP_PWR2; + mem_bin_steps++; + mem_bin_count += QUERY_CACHE_MEM_BIN_PARTS_INC; + mem_bin_count = (uint) (mem_bin_count * QUERY_CACHE_MEM_BIN_PARTS_MUL); + + // Prevent too small bins spacing + if (mem_bin_count > (mem_bin_size >> QUERY_CACHE_MEM_BIN_SPC_LIM_PWR2)) + mem_bin_count= (mem_bin_size >> QUERY_CACHE_MEM_BIN_SPC_LIM_PWR2); + } + inc = (prev_size - mem_bin_size) / mem_bin_count; + mem_bin_num += (mem_bin_count - (min_allocation_unit - mem_bin_size)/inc); + mem_bin_steps++; + additional_data_size = ((mem_bin_num+1) * + ALIGN_SIZE(sizeof(Query_cache_memory_bin))+ + (mem_bin_steps * + ALIGN_SIZE(sizeof(Query_cache_memory_bin_step)))); + + if (query_cache_size < additional_data_size) + goto err; + query_cache_size -= additional_data_size; + + STRUCT_LOCK(&structure_guard_mutex); + if (max_mem_bin_size <= min_allocation_unit) + { + DBUG_PRINT("qcache", + (" max bin size (%lu) <= min_allocation_unit => cache disabled", + max_mem_bin_size)); + STRUCT_UNLOCK(&structure_guard_mutex); + goto err; + } + + if (!(cache = (byte *) + my_malloc_lock(query_cache_size+additional_data_size, MYF(0)))) + { + STRUCT_UNLOCK(&structure_guard_mutex); + goto err; + } + + DBUG_PRINT("qcache", ("cache length %lu, min unit %lu, %u bins", + query_cache_size, min_allocation_unit, mem_bin_num)); + + steps = (Query_cache_memory_bin_step *) cache; + bins = ((Query_cache_memory_bin *) + (cache + mem_bin_steps * + ALIGN_SIZE(sizeof(Query_cache_memory_bin_step)))); + + first_block = (Query_cache_block *) (cache + additional_data_size); + first_block->init(query_cache_size); + total_blocks++; + first_block->pnext=first_block->pprev=first_block; + first_block->next=first_block->prev=first_block; + + /* Prepare bins */ + + bins[0].init(max_mem_bin_size); + steps[0].init(max_mem_bin_size,0,0); + mem_bin_count = (uint) ((1 + QUERY_CACHE_MEM_BIN_PARTS_INC) * + QUERY_CACHE_MEM_BIN_PARTS_MUL); + num= step= 1; + mem_bin_size = max_mem_bin_size >> QUERY_CACHE_MEM_BIN_STEP_PWR2; + while (mem_bin_size > min_allocation_unit) + { + ulong incr = (steps[step-1].size - mem_bin_size) / mem_bin_count; + unsigned long size = mem_bin_size; + for (uint i= mem_bin_count; i > 0; i--) + { + bins[num+i-1].init(size); + size += incr; } - fprintf(stderr, "Query `%s' not found in the cache -- insert %p from slot %d\n", thd->lex_ptr->ptr, ptr, last_lex_array_item+1); - if (!hash_search(&sql_cache, sql, length)) { - fprintf(stderr, "I just enterred a hash key but it's not where -- what's that?\n"); - } else { - fprintf(stderr, "Inserted to cache\n"); + num += mem_bin_count; + steps[step].init(mem_bin_size, num-1, incr); + mem_bin_size >>= QUERY_CACHE_MEM_BIN_STEP_PWR2; + step++; + mem_bin_count += QUERY_CACHE_MEM_BIN_PARTS_INC; + mem_bin_count = (uint) (mem_bin_count * QUERY_CACHE_MEM_BIN_PARTS_MUL); + if (mem_bin_count > (mem_bin_size >> QUERY_CACHE_MEM_BIN_SPC_LIM_PWR2)) + mem_bin_count=(mem_bin_size >> QUERY_CACHE_MEM_BIN_SPC_LIM_PWR2); + } + inc = (steps[step-1].size - mem_bin_size) / mem_bin_count; + + /* + num + mem_bin_count > mem_bin_num, but index never be > mem_bin_num + because block with size < min_allocated_unit never will be requested + */ + + steps[step].init(mem_bin_size, num + mem_bin_count - 1, inc); + { + uint skiped = (min_allocation_unit - mem_bin_size)/inc; + ulong size = mem_bin_size + inc*skiped; + uint i = mem_bin_count - skiped; + while (i-- > 0) + { + bins[num+i].init(size); + size += inc; } - return 0; } + bins[mem_bin_num].number = 1; // For easy end test in get_free_block + free_memory = free_memory_blocks = 0; + insert_into_free_memory_list(first_block); + + DUMP(this); + + VOID(hash_init(&queries,def_query_hash_size, 0, 0, + query_cache_query_get_key, 0, 0)); +#ifndef FN_NO_CASE_SENCE + VOID(hash_init(&tables,def_table_hash_size, 0, 0, + query_cache_table_get_key, 0, 0)); +#else + // windows, OS/2 or other case insensitive file names work around + VOID(hash_init(&tables,def_table_hash_size, 0, 0, + query_cache_table_get_key, 0, + (lower_case_table_names?0:HASH_CASE_INSENSITIVE))); +#endif + + queries_in_cache = 0; + queries_blocks = 0; + STRUCT_UNLOCK(&structure_guard_mutex); + DBUG_RETURN(query_cache_size + + additional_data_size + approx_additional_data_size); + +err: + make_disabled(); + DBUG_RETURN(0); +} + + +/* Disable the use of the query cache */ + +void Query_cache::make_disabled() +{ + DBUG_ENTER("Query_cache::make_disabled"); + query_cache_size= 0; + free_memory= 0; + bins= 0; + steps= 0; + cache= 0; + mem_bin_num= mem_bin_steps= 0; + queries_in_cache= 0; + first_block= 0; + DBUG_VOID_RETURN; +} + + +void Query_cache::free_cache(my_bool destruction) +{ + DBUG_ENTER("Query_cache::free_cache"); + if (query_cache_size > 0) + { + if (!destruction) + STRUCT_LOCK(&structure_guard_mutex); + + flush_cache(); +#ifndef DBUG_OFF + if (bins[0].free_blocks == 0) + { + wreck(__LINE__,"no free memory found in (bins[0].free_blocks"); + DBUG_VOID_RETURN; + } +#endif + + /* Becasue we did a flush, all cache memory must be in one this block */ + bins[0].free_blocks->destroy(); + total_blocks--; +#ifndef DBUG_OFF + if (free_memory != query_cache_size) + DBUG_PRINT("qcache", ("free memory %lu (should be %lu)", + free_memory , query_cache_size)); #endif - return 1; + my_free((gptr) cache, MYF(MY_ALLOW_ZERO_PTR)); + make_disabled(); + hash_free(&queries); + hash_free(&tables); + if (!destruction) + STRUCT_UNLOCK(&structure_guard_mutex); + } + DBUG_VOID_RETURN; +} + +/***************************************************************************** + Free block data +*****************************************************************************/ + +/* + The following assumes we have a lock on the cache +*/ + +void Query_cache::flush_cache() +{ + while (queries_blocks != 0) + { + BLOCK_LOCK_WR(queries_blocks); + free_query(queries_blocks); + } +} + +/* + Free oldest query that is not in use by another thread. + Returns 1 if we couldn't remove anything +*/ + +my_bool Query_cache::free_old_query() +{ + DBUG_ENTER("Query_cache::free_old_query"); + if (queries_blocks) + { + /* + try_lock_writing used to prevent client because here lock + sequence is breached. + Also we don't need remove locked queries at this point. + */ + Query_cache_block *query_block = 0; + if (queries_blocks != 0) + { + Query_cache_block *block = queries_blocks; + /* Search until we find first query that we can remove */ + do + { + Query_cache_query *header = block->query(); + if (header->result() != 0 && + header->result()->type == Query_cache_block::RESULT && + block->query()->try_lock_writing()) + { + query_block = block; + break; + } + } while ((block=block->next) != queries_blocks ); + } + + if (query_block != 0) + { + free_query(query_block); + lowmem_prunes++; + DBUG_RETURN(0); + } + } + DBUG_RETURN(1); // Nothing to remove +} + +/* + Free query from query cache. + query_block must be locked for writing. + This function will remove (and destroy) the lock for the query. +*/ + +void Query_cache::free_query(Query_cache_block *query_block) +{ + DBUG_ENTER("Query_cache::free_query"); + DBUG_PRINT("qcache", ("free query 0x%lx %lu bytes result", + (ulong) query_block, + query_block->query()->length() )); + + queries_in_cache--; + hash_delete(&queries,(byte *) query_block); + + Query_cache_query *query = query_block->query(); + + if (query->writer() != 0) + { + /* Tell MySQL that this query should not be cached anymore */ + query->writer()->query_cache_query = 0; + query->writer(0); + } + double_linked_list_exclude(query_block, &queries_blocks); + Query_cache_block_table *table=query_block->table(0); + + for (TABLE_COUNTER_TYPE i=0; i < query_block->n_tables; i++) + unlink_table(table++); + Query_cache_block *result_block = query->result(); + + /* + The following is true when query destruction was called and no results + in query . (query just registered and then abort/pack/flush called) + */ + if (result_block != 0) + { + Query_cache_block *block = result_block; + do + { + Query_cache_block *current = block; + block = block->next; + free_memory_block(current); + } while (block != result_block); + } + + query->unlock_n_destroy(); + free_memory_block(query_block); + + DBUG_VOID_RETURN; +} + +/***************************************************************************** + Query data creation +*****************************************************************************/ + +Query_cache_block * +Query_cache::write_block_data(ulong data_len, gptr data, + ulong header_len, + Query_cache_block::block_type type, + TABLE_COUNTER_TYPE ntab, + my_bool under_guard) +{ + ulong all_headers_len = (ALIGN_SIZE(sizeof(Query_cache_block)) + + ALIGN_SIZE(ntab*sizeof(Query_cache_block_table)) + + header_len); + ulong len = data_len + all_headers_len; + ulong align_len= ALIGN_SIZE(len); + DBUG_ENTER("Query_cache::write_block_data"); + DBUG_PRINT("qcache", ("data: %ld, header: %ld, all header: %ld", + data_len, header_len, all_headers_len)); + Query_cache_block *block = allocate_block(max(align_len, + min_allocation_unit), + 1, 0, under_guard); + if (block != 0) + { + block->type = type; + block->n_tables = ntab; + block->used = len; + + memcpy((void*) (((byte *) block)+ all_headers_len), + (void*) data, data_len); + } + DBUG_RETURN(block); } + + +/* + On success STRUCT_UNLOCK(&query_cache.structure_guard_mutex) will be done. +*/ + +my_bool +Query_cache::append_result_data(Query_cache_block **current_block, + ulong data_len, gptr data, + Query_cache_block *query_block) +{ + DBUG_ENTER("Query_cache::append_result_data"); + DBUG_PRINT("qcache", ("append %lu bytes to 0x%lx query", + data_len, query_block)); + + if (query_block->query()->add(data_len) > query_cache_limit) + { + DBUG_PRINT("qcache", ("size limit reached %lu > %lu", + query_block->query()->length(), + query_cache_limit)); + DBUG_RETURN(0); + } + if (*current_block == 0) + { + DBUG_PRINT("qcache", ("allocated first result data block %lu", data_len)); + /* + STRUCT_UNLOCK(&structure_guard_mutex) Will be done by + write_result_data if success; + */ + DBUG_RETURN(write_result_data(current_block, data_len, data, query_block, + Query_cache_block::RES_BEG)); + } + Query_cache_block *last_block = (*current_block)->prev; + + DBUG_PRINT("qcache", ("lastblock 0x%lx len %lu used %lu", + (ulong) last_block, last_block->length, + last_block->used)); + my_bool success = 1; + ulong last_block_free_space= last_block->length - last_block->used; + + /* + We will first allocate and write the 'tail' of data, that doesn't fit + in the 'last_block'. Only if this succeeds, we will fill the last_block. + This saves us a memcpy if the query doesn't fit in the query cache. + */ + + // Try join blocks if physically next block is free... + ulong tail = data_len - last_block_free_space; + ulong append_min = get_min_append_result_data_size(); + if (last_block_free_space < data_len && + append_next_free_block(last_block, + max(tail, append_min))) + last_block_free_space = last_block->length - last_block->used; + // If no space in last block (even after join) allocate new block + if (last_block_free_space < data_len) + { + DBUG_PRINT("qcache", ("allocate new block for %lu bytes", + data_len-last_block_free_space)); + Query_cache_block *new_block = 0; + /* + On success STRUCT_UNLOCK(&structure_guard_mutex) will be done + by the next call + */ + success = write_result_data(&new_block, data_len-last_block_free_space, + (gptr)(((byte*)data)+last_block_free_space), + query_block, + Query_cache_block::RES_CONT); + /* + new_block may be != 0 even !success (if write_result_data + allocate a small block but failed to allocate continue) + */ + if (new_block != 0) + double_linked_list_join(last_block, new_block); + } + else + { + // It is success (nobody can prevent us write data) + STRUCT_UNLOCK(&structure_guard_mutex); + } + + // Now finally write data to the last block + if (success && last_block_free_space > 0) + { + ulong to_copy = min(data_len,last_block_free_space); + DBUG_PRINT("qcache", ("use free space %lub at block 0x%lx to copy %lub", + last_block_free_space, (ulong)last_block, to_copy)); + memcpy((void*) (((byte*) last_block) + last_block->used), (void*) data, + to_copy); + last_block->used+=to_copy; + } + DBUG_RETURN(success); +} + + +my_bool Query_cache::write_result_data(Query_cache_block **result_block, + ulong data_len, gptr data, + Query_cache_block *query_block, + Query_cache_block::block_type type) +{ + DBUG_ENTER("Query_cache::write_result_data"); + DBUG_PRINT("qcache", ("data_len %lu",data_len)); + + /* + Reserve block(s) for filling + During data allocation we must have structure_guard_mutex locked. + As data copy is not a fast operation, it's better if we don't have + structure_guard_mutex locked during data coping. + Thus we first allocate space and lock query, then unlock + structure_guard_mutex and copy data. + */ + + my_bool success = allocate_data_chain(result_block, data_len, query_block, + type == Query_cache_block::RES_BEG); + if (success) + { + // It is success (nobody can prevent us write data) + STRUCT_UNLOCK(&structure_guard_mutex); + byte *rest = (byte*) data; + Query_cache_block *block = *result_block; + uint headers_len = (ALIGN_SIZE(sizeof(Query_cache_block)) + + ALIGN_SIZE(sizeof(Query_cache_result))); + // Now fill list of blocks that created by allocate_data_chain + do + { + block->type = type; + ulong length = block->used - headers_len; + DBUG_PRINT("qcache", ("write %lu byte in block 0x%lx",length, + (ulong)block)); + memcpy((void*)(((byte*) block)+headers_len), (void*) rest, length); + rest += length; + block = block->next; + type = Query_cache_block::RES_CONT; + } while (block != *result_block); + } + else + { + if (*result_block != 0) + { + // Destroy list of blocks that was created & locked by lock_result_data + Query_cache_block *block = *result_block; + do + { + Query_cache_block *current = block; + block = block->next; + free_memory_block(current); + } while (block != *result_block); + *result_block = 0; + /* + It is not success => not unlock structure_guard_mutex (we need it to + free query) + */ + } + } + DBUG_PRINT("qcache", ("success %d", (int) success)); + DBUG_RETURN(success); +} + +inline ulong Query_cache::get_min_first_result_data_size() +{ + if (queries_in_cache < QUERY_CACHE_MIN_ESTIMATED_QUERIES_NUMBER) + return min_result_data_size; + ulong avg_result = (query_cache_size - free_memory) / queries_in_cache; + avg_result = min(avg_result, query_cache_limit); + return max(min_result_data_size, avg_result); +} + +inline ulong Query_cache::get_min_append_result_data_size() +{ + return min_result_data_size; +} + +/* + Allocate one or more blocks to hold data +*/ + +my_bool Query_cache::allocate_data_chain(Query_cache_block **result_block, + ulong data_len, + Query_cache_block *query_block, + my_bool first_block_arg) +{ + ulong all_headers_len = (ALIGN_SIZE(sizeof(Query_cache_block)) + + ALIGN_SIZE(sizeof(Query_cache_result))); + ulong len= data_len + all_headers_len; + ulong align_len= ALIGN_SIZE(len); + DBUG_ENTER("Query_cache::allocate_data_chain"); + DBUG_PRINT("qcache", ("data_len %lu, all_headers_len %lu", + data_len, all_headers_len)); + + ulong min_size = (first_block_arg ? + get_min_first_result_data_size(): + get_min_append_result_data_size()); + *result_block = allocate_block(max(min_size, align_len), + min_result_data_size == 0, + all_headers_len + min_result_data_size, + 1); + my_bool success = (*result_block != 0); + if (success) + { + Query_cache_block *new_block= *result_block; + new_block->n_tables = 0; + new_block->used = 0; + new_block->type = Query_cache_block::RES_INCOMPLETE; + new_block->next = new_block->prev = new_block; + Query_cache_result *header = new_block->result(); + header->parent(query_block); + + if (new_block->length < len) + { + /* + We got less memory then we need (no big memory blocks) => + Continue to allocated more blocks until we got everything we need. + */ + Query_cache_block *next_block; + if ((success = allocate_data_chain(&next_block, + len - new_block->length, + query_block, first_block_arg))) + double_linked_list_join(new_block, next_block); + } + if (success) + { + new_block->used = min(len, new_block->length); + + DBUG_PRINT("qcache", ("Block len %lu used %lu", + new_block->length, new_block->used)); + } + else + DBUG_PRINT("warning", ("Can't allocate block for continue")); + } + else + DBUG_PRINT("warning", ("Can't allocate block for results")); + DBUG_RETURN(success); +} + +/***************************************************************************** + Tables management +*****************************************************************************/ + +/* + Invalidate the first table in the table_list +*/ + +void Query_cache::invalidate_table(TABLE_LIST *table_list) +{ + if (table_list->table != 0) + invalidate_table(table_list->table); // Table is open + else + { + char key[MAX_DBKEY_LENGTH]; + uint key_length; + Query_cache_block *table_block; + key_length=(uint) (strmov(strmov(key,table_list->db)+1, + table_list->real_name) -key)+ 1; + + // We don't store temporary tables => no key_length+=4 ... + if ((table_block = (Query_cache_block*) + hash_search(&tables,(byte*) key,key_length))) + invalidate_table(table_block); + } +} + +void Query_cache::invalidate_table(TABLE *table) +{ + invalidate_table((byte*) table->table_cache_key, table->key_length); +} + +void Query_cache::invalidate_table(byte * key, uint32 key_length) +{ + Query_cache_block *table_block; + if ((table_block = ((Query_cache_block*) + hash_search(&tables, key, key_length)))) + invalidate_table(table_block); +} + +void Query_cache::invalidate_table(Query_cache_block *table_block) +{ + Query_cache_block_table *list_root = table_block->table(0); + while (list_root->next != list_root) + { + Query_cache_block *query_block = list_root->next->block(); + BLOCK_LOCK_WR(query_block); + free_query(query_block); + } +} + +/* + Store all used tables + + SYNOPSIS + register_all_tables() + block Store tables in this block + tables_used List if used tables + tables_arg Not used ? +*/ + +my_bool Query_cache::register_all_tables(Query_cache_block *block, + TABLE_LIST *tables_used, + TABLE_COUNTER_TYPE tables_arg) +{ + TABLE_COUNTER_TYPE n; + DBUG_PRINT("qcache", ("register tables block 0x%lx, n %d, header %x", + (ulong) block, (int) tables_arg, + (int) ALIGN_SIZE(sizeof(Query_cache_block)))); + + Query_cache_block_table *block_table = block->table(0); + + for (n=0; tables_used; tables_used=tables_used->next, n++, block_table++) + { + DBUG_PRINT("qcache", + ("table %s, db %s, openinfo at 0x%lx, keylen %u, key at 0x%lx", + tables_used->real_name, tables_used->db, + (ulong) tables_used->table, + tables_used->table->key_length, + (ulong) tables_used->table->table_cache_key)); + block_table->n=n; + if (!insert_table(tables_used->table->key_length, + tables_used->table->table_cache_key, block_table, + tables_used->db_length)) + break; + + if (tables_used->table->db_type == DB_TYPE_MRG_MYISAM) + { + ha_myisammrg *handler = (ha_myisammrg *) tables_used->table->file; + MYRG_INFO *file = handler->myrg_info(); + for (MYRG_TABLE *table = file->open_tables; + table != file->end_table ; + table++) + { + char key[MAX_DBKEY_LENGTH]; + uint32 db_length; + uint key_length =filename_2_table_key(key, table->table->filename, + &db_length); + (++block_table)->n= ++n; + if (!insert_table(key_length, key, block_table, + db_length)) + goto err; + } + } + } + +err: + if (tables_used) + { + DBUG_PRINT("qcache", ("failed at table %d", (int) n)); + /* Unlink the tables we allocated above */ + for (Query_cache_block_table *tmp = block->table(0) ; + tmp != block_table; + tmp++) + unlink_table(tmp); + } + return (tables_used == 0); +} + +/* + Insert used tablename in cache + Returns 0 on error +*/ + +my_bool +Query_cache::insert_table(uint key_len, char *key, + Query_cache_block_table *node, + uint32 db_length) +{ + DBUG_ENTER("Query_cache::insert_table"); + DBUG_PRINT("qcache", ("insert table node 0x%lx, len %d", + (ulong)node, key_len)); + + Query_cache_block *table_block = ((Query_cache_block *) + hash_search(&tables, (byte*) key, + key_len)); + + if (table_block == 0) + { + DBUG_PRINT("qcache", ("new table block from 0x%lx (%u)", + (ulong) key, (int) key_len)); + table_block = write_block_data(key_len, (gptr) key, + ALIGN_SIZE(sizeof(Query_cache_table)), + Query_cache_block::TABLE, + 1, 1); + if (table_block == 0) + { + DBUG_PRINT("qcache", ("Can't write table name to cache")); + DBUG_RETURN(0); + } + Query_cache_table *header = table_block->table(); + double_linked_list_simple_include(table_block, + &tables_blocks); + Query_cache_block_table *list_root = table_block->table(0); + list_root->n = 0; + list_root->next = list_root->prev = list_root; + if (hash_insert(&tables, (const byte *) table_block)) + { + DBUG_PRINT("qcache", ("Can't insert table to hash")); + // write_block_data return locked block + free_memory_block(table_block); + DBUG_RETURN(0); + } + char *db = header->db(); + header->table(db + db_length + 1); + } + + Query_cache_block_table *list_root = table_block->table(0); + node->next = list_root->next; + list_root->next = node; + node->next->prev = node; + node->prev = list_root; + node->parent = table_block->table(); + DBUG_RETURN(1); +} + + +void Query_cache::unlink_table(Query_cache_block_table *node) +{ + DBUG_ENTER("Query_cache::unlink_table"); + node->prev->next = node->next; + node->next->prev = node->prev; + Query_cache_block_table *neighbour = node->next; + if (neighbour->next == neighbour) + { + // list is empty (neighbor is root of list) + Query_cache_block *table_block = neighbour->block(); + double_linked_list_exclude(table_block, + &tables_blocks); + hash_delete(&tables,(byte *) table_block); + free_memory_block(table_block); + } + DBUG_VOID_RETURN; +} + +/***************************************************************************** + Free memory management +*****************************************************************************/ + +Query_cache_block * +Query_cache::allocate_block(ulong len, my_bool not_less, ulong min, + my_bool under_guard) +{ + DBUG_ENTER("Query_cache::allocate_block"); + DBUG_PRINT("qcache", ("len %lu, not less %d, min %lu, uder_guard %d", + len, not_less,min,under_guard)); + + if (len >= min(query_cache_size, query_cache_limit)) + { + DBUG_PRINT("qcache", ("Query cache hase only %lu memory and limit %lu", + query_cache_size, query_cache_limit)); + DBUG_RETURN(0); // in any case we don't have such piece of memory + } + + if (!under_guard) + STRUCT_LOCK(&structure_guard_mutex); + + /* Free old queries until we have enough memory to store this block */ + Query_cache_block *block; + do + { + block= get_free_block(len, not_less, min); + } + while (block == 0 && !free_old_query()); + + if (block != 0) // If we found a suitable block + { + if (block->length >= ALIGN_SIZE(len) + min_allocation_unit) + split_block(block,ALIGN_SIZE(len)); + } + + if (!under_guard) + STRUCT_UNLOCK(&structure_guard_mutex); + DBUG_RETURN(block); +} + + +Query_cache_block * +Query_cache::get_free_block(ulong len, my_bool not_less, ulong min) +{ + Query_cache_block *block = 0, *first = 0; + DBUG_ENTER("Query_cache::get_free_block"); + DBUG_PRINT("qcache",("length %lu, not_less %d, min %lu", len, + (int)not_less, min)); + + /* Find block with minimal size > len */ + uint start = find_bin(len); + // try matching bin + if (bins[start].number != 0) + { + Query_cache_block *list = bins[start].free_blocks; + if (list->prev->length >= len) // check block with max size + { + first = list; + uint n = 0; + while ( n < QUERY_CACHE_MEM_BIN_TRY && + first->length < len) //we don't need irst->next != list + { + first=first->next; + n++; + } + if (first->length >= len) + block=first; + else // we don't need if (first->next != list) + { + n = 0; + block = list->prev; + while (n < QUERY_CACHE_MEM_BIN_TRY && + block->length > len) + { + block=block->prev; + n++; + } + if (block->length < len) + block=block->next; + } + } + else + first = list->prev; + } + if (block == 0 && start > 0) + { + DBUG_PRINT("qcache",("Try bins with bigger block size")); + // Try more big bins + int i = start - 1; + while (i > 0 && bins[i].number == 0) + i--; + if (bins[i].number > 0) + block = bins[i].free_blocks; + } + + // If no big blocks => try less size (if it is possible) + if (block == 0 && ! not_less) + { + DBUG_PRINT("qcache",("Try to allocate a smaller block")); + if (first != 0 && first->length > min) + block = first; + else + { + uint i = start + 1; + /* bins[mem_bin_num].number contains 1 for easy end test */ + for (i= start+1 ; bins[i].number == 0 ; i++) ; + if (i < mem_bin_num && bins[i].free_blocks->prev->length >= min) + block = bins[i].free_blocks->prev; + } + } + if (block != 0) + exclude_from_free_memory_list(block); + + DBUG_PRINT("qcache",("getting block 0x%lx", (ulong) block)); + DBUG_RETURN(block); +} + + +void Query_cache::free_memory_block(Query_cache_block *block) +{ + DBUG_ENTER("Query_cache::free_memory_block"); + block->used=0; + DBUG_PRINT("qcache",("first_block 0x%lx, block 0x%lx, pnext 0x%lx pprev 0x%lx", + (ulong) first_block, (ulong) block,block->pnext, + (ulong) block->pprev)); + + if (block->pnext != first_block && block->pnext->is_free()) + block = join_free_blocks(block, block->pnext); + if (block != first_block && block->pprev->is_free()) + block = join_free_blocks(block->pprev, block->pprev); + insert_into_free_memory_list(block); + DBUG_VOID_RETURN; +} + + +void Query_cache::split_block(Query_cache_block *block, ulong len) +{ + DBUG_ENTER("Query_cache::split_block"); + Query_cache_block *new_block = (Query_cache_block*)(((byte*) block)+len); + + new_block->init(block->length - len); + total_blocks++; + block->length=len; + new_block->pnext = block->pnext; + block->pnext = new_block; + new_block->pprev = block; + new_block->pnext->pprev = new_block; + + if (block->type == Query_cache_block::FREE) + { + // if block was free then it already joined with all free neighbours + insert_into_free_memory_list(new_block); + } + else + free_memory_block(new_block); + + DBUG_PRINT("qcache", ("split 0x%lx (%lu) new 0x%lx", + (ulong) block, len, (ulong) new_block)); + DBUG_VOID_RETURN; +} + + +Query_cache_block * +Query_cache::join_free_blocks(Query_cache_block *first_block_arg, + Query_cache_block *block_in_list) +{ + Query_cache_block *second_block; + DBUG_ENTER("Query_cache::join_free_blocks"); + DBUG_PRINT("qcache", + ("join first 0x%lx, pnext 0x%lx, in list 0x%lx", + (ulong) first_block_arg, (ulong) first_block_arg->pnext, + (ulong) block_in_list)); + + exclude_from_free_memory_list(block_in_list); + second_block = first_block_arg->pnext; + // May be was not free block + second_block->used=0; + second_block->destroy(); + total_blocks--; + + first_block_arg->length += second_block->length; + first_block_arg->pnext = second_block->pnext; + second_block->pnext->pprev = first_block_arg; + + DBUG_RETURN(first_block_arg); +} + + +my_bool Query_cache::append_next_free_block(Query_cache_block *block, + ulong add_size) +{ + Query_cache_block *next_block = block->pnext; + DBUG_ENTER("Query_cache::append_next_free_block"); + DBUG_PRINT("enter", ("block 0x%lx, add_size %lu", (ulong) block, + add_size)); + + if (next_block != first_block && next_block->is_free()) + { + ulong old_len = block->length; + exclude_from_free_memory_list(next_block); + next_block->destroy(); + total_blocks--; + + block->length += next_block->length; + block->pnext = next_block->pnext; + next_block->pnext->pprev = block; + + if (block->length > ALIGN_SIZE(old_len + add_size) + min_allocation_unit) + split_block(block,ALIGN_SIZE(old_len + add_size)); + DBUG_PRINT("exit", ("block was appended")); + DBUG_RETURN(1); + } + DBUG_RETURN(0); +} + + +void Query_cache::exclude_from_free_memory_list(Query_cache_block *free_block) +{ + DBUG_ENTER("Query_cache::exclude_from_free_memory_list"); + Query_cache_memory_bin *bin = *((Query_cache_memory_bin **) + free_block->data()); + double_linked_list_exclude(free_block, &bin->free_blocks); + bin->number--; + free_memory-=free_block->length; + free_memory_blocks--; + DBUG_PRINT("qcache",("exclude block 0x%lx, bin 0x%lx", (ulong) free_block, + (ulong) bin)); + DBUG_VOID_RETURN; +} + +void Query_cache::insert_into_free_memory_list(Query_cache_block *free_block) +{ + DBUG_ENTER("Query_cache::insert_into_free_memory_list"); + uint idx = find_bin(free_block->length); + insert_into_free_memory_sorted_list(free_block, &bins[idx].free_blocks); + /* + We have enough memory in block for storing bin reference due to + min_allocation_unit choice + */ + Query_cache_memory_bin **bin_ptr = ((Query_cache_memory_bin**) + free_block->data()); + *bin_ptr = bins+idx; + (*bin_ptr)->number++; + DBUG_PRINT("qcache",("insert block 0x%lx, bin[%d] 0x%lx", + (ulong) free_block, idx, (ulong) *bin_ptr)); + DBUG_VOID_RETURN; +} + +uint Query_cache::find_bin(ulong size) +{ + DBUG_ENTER("Query_cache::find_bin"); + // Binary search + int left = 0, right = mem_bin_steps; + do + { + int middle = (left + right) / 2; + if (steps[middle].size > size) + left = middle+1; + else + right = middle; + } while (left < right); + if (left == 0) + { + // first bin not subordinate of common rules + DBUG_PRINT("qcache", ("first bin (# 0), size %lu",size)); + DBUG_RETURN(0); + } + uint bin = steps[left].idx - + (uint)((size - steps[left].size)/steps[left].increment); +#ifndef DBUG_OFF + bins_dump(); +#endif + DBUG_PRINT("qcache", ("bin %u step %u, size %lu step size %lu", + bin, left, size, steps[left].size)); + DBUG_RETURN(bin); +} + + +/***************************************************************************** + Lists management +*****************************************************************************/ + +void Query_cache::move_to_query_list_end(Query_cache_block *query_block) +{ + DBUG_ENTER("Query_cache::move_to_query_list_end"); + double_linked_list_exclude(query_block, &queries_blocks); + double_linked_list_simple_include(query_block, &queries_blocks); + DBUG_VOID_RETURN; +} + + +void Query_cache::insert_into_free_memory_sorted_list(Query_cache_block * + new_block, + Query_cache_block ** + list) +{ + DBUG_ENTER("Query_cache::insert_into_free_memory_sorted_list"); + /* + list sorted by size in ascendant order, because we need small blocks + more frequently than bigger ones + */ + + new_block->used = 0; + new_block->n_tables = 0; + new_block->type = Query_cache_block::FREE; + + if (*list == 0) + { + *list = new_block->next=new_block->prev=new_block; + DBUG_PRINT("qcache", ("inserted into empty list")); + } + else + { + Query_cache_block *point = *list; + if (point->length >= new_block->length) + { + point = point->prev; + *list = new_block; + } + else + { + /* Find right position in sorted list to put block */ + while (point->next != *list && + point->next->length < new_block->length) + point=point->next; + } + new_block->prev = point; + new_block->next = point->next; + new_block->next->prev = new_block; + point->next = new_block; + } + free_memory+=new_block->length; + free_memory_blocks++; + DBUG_VOID_RETURN; +} + + +void +Query_cache::double_linked_list_simple_include(Query_cache_block *point, + Query_cache_block ** + list_pointer) +{ + DBUG_ENTER("Query_cache::double_linked_list_simple_include"); + DBUG_PRINT("qcache", ("including block 0x%lx", (ulong) point)); + if (*list_pointer == 0) + *list_pointer=point->next=point->prev=point; + else + { + // insert to the end of list + point->next = (*list_pointer); + point->prev = (*list_pointer)->prev; + point->prev->next = point; + (*list_pointer)->prev = point; + } + DBUG_VOID_RETURN; +} + +void +Query_cache::double_linked_list_exclude(Query_cache_block *point, + Query_cache_block **list_pointer) +{ + DBUG_ENTER("Query_cache::double_linked_list_exclude"); + DBUG_PRINT("qcache", ("excluding block 0x%lx, list 0x%lx", + (ulong) point, (ulong) list_pointer)); + if (point->next == point) + *list_pointer = 0; // empty list + else + { + point->next->prev = point->prev; + point->prev->next = point->next; + if (point == *list_pointer) + *list_pointer = point->next; + } + DBUG_VOID_RETURN; +} + + +void Query_cache::double_linked_list_join(Query_cache_block *head_tail, + Query_cache_block *tail_head) +{ + Query_cache_block *head_head = head_tail->next, + *tail_tail = tail_head->prev; + head_head->prev = tail_tail; + head_tail->next = tail_head; + tail_head->prev = head_tail; + tail_tail->next = head_head; +} + +/***************************************************************************** + Query +*****************************************************************************/ + +/* + If query is cacheable return number tables in query + (query without tables are not cached) +*/ + +TABLE_COUNTER_TYPE Query_cache::is_cacheable(THD *thd, uint32 query_len, + char *query, + LEX *lex, TABLE_LIST *tables_used) +{ + TABLE_COUNTER_TYPE table_count = 0; + DBUG_ENTER("Query_cache::is_cacheable"); + + if (lex->sql_command == SQLCOM_SELECT && + (thd->variables.query_cache_type == 1 || + (thd->variables.query_cache_type == 2 && (lex->select->options & + OPTION_TO_QUERY_CACHE))) && + thd->safe_to_cache_query) + { + my_bool has_transactions = 0; + DBUG_PRINT("qcache", ("options %lx %lx, type %u", + OPTION_TO_QUERY_CACHE, + lex->select->options, + (int) thd->variables.query_cache_type)); + + for (; tables_used; tables_used= tables_used->next) + { + table_count++; + DBUG_PRINT("qcache", ("table %s, db %s, type %u", + tables_used->real_name, + tables_used->db, tables_used->table->db_type)); + has_transactions = (has_transactions || + tables_used->table->file->has_transactions()); + + if (tables_used->table->db_type == DB_TYPE_MRG_ISAM || + tables_used->table->tmp_table != NO_TMP_TABLE || + (tables_used->db_length == 5 && + DB_NAME_PREPROCESS(tables_used->db[0])=='m' && + DB_NAME_PREPROCESS(tables_used->db[1])=='y' && + DB_NAME_PREPROCESS(tables_used->db[2])=='s' && + DB_NAME_PREPROCESS(tables_used->db[3])=='q' && + DB_NAME_PREPROCESS(tables_used->db[4])=='l')) + { + DBUG_PRINT("qcache", + ("select not cacheable: used MRG_ISAM, temporary or system table(s)")); + DBUG_RETURN(0); + } + if (tables_used->table->db_type == DB_TYPE_MRG_MYISAM) + { + ha_myisammrg *handler = (ha_myisammrg *)tables_used->table->file; + MYRG_INFO *file = handler->myrg_info(); + table_count+= (file->end_table - file->open_tables); + } + } + + if ((thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) && + has_transactions) + { + DBUG_PRINT("qcache", ("not in autocommin mode")); + DBUG_RETURN(0); + } + DBUG_PRINT("qcache", ("select is using %d tables", table_count)); + DBUG_RETURN(table_count); + } + + DBUG_PRINT("qcache", + ("not interesting query: %d or not cacheable, options %lx %lx, type %u", + (int) lex->sql_command, + OPTION_TO_QUERY_CACHE, + lex->select->options, + (int) thd->variables.query_cache_type)); + DBUG_RETURN(0); +} + + +/***************************************************************************** + Packing +*****************************************************************************/ + +void Query_cache::pack_cache() +{ + DBUG_ENTER("Query_cache::pack_cache"); + STRUCT_LOCK(&structure_guard_mutex); + DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1);); + + byte *border = 0; + Query_cache_block *before = 0; + ulong gap = 0; + my_bool ok = 1; + Query_cache_block *block = first_block; + DUMP(this); + + if (first_block) + { + do + { + Query_cache_block *next=block->pnext; + ok = move_by_type(&border, &before, &gap, block); + block = next; + } while (ok && block != first_block); + + if (border != 0) + { + Query_cache_block *new_block = (Query_cache_block *) border; + new_block->init(gap); + total_blocks++; + new_block->pnext = before->pnext; + before->pnext = new_block; + new_block->pprev = before; + new_block->pnext->pprev = new_block; + insert_into_free_memory_list(new_block); + } + DUMP(this); + } + + DBUG_EXECUTE("check_querycache",query_cache.check_integrity(1);); + STRUCT_UNLOCK(&structure_guard_mutex); + DBUG_VOID_RETURN; +} + + +my_bool Query_cache::move_by_type(byte **border, + Query_cache_block **before, ulong *gap, + Query_cache_block *block) +{ + DBUG_ENTER("Query_cache::move_by_type"); + + my_bool ok = 1; + switch (block->type) { + case Query_cache_block::FREE: + { + DBUG_PRINT("qcache", ("block 0x%lx FREE", (ulong) block)); + if (*border == 0) + { + *border = (byte *) block; + *before = block->pprev; + DBUG_PRINT("qcache", ("gap beginning here")); + } + exclude_from_free_memory_list(block); + *gap +=block->length; + block->pprev->pnext=block->pnext; + block->pnext->pprev=block->pprev; + block->destroy(); + total_blocks--; + DBUG_PRINT("qcache", ("added to gap (%lu)", *gap)); + break; + } + case Query_cache_block::TABLE: + { + DBUG_PRINT("qcache", ("block 0x%lx TABLE", (ulong) block)); + if (*border == 0) + break; + ulong len = block->length, used = block->used; + Query_cache_block_table *list_root = block->table(0); + Query_cache_block_table *tprev = list_root->prev, + *tnext = list_root->next; + Query_cache_block *prev = block->prev, + *next = block->next, + *pprev = block->pprev, + *pnext = block->pnext, + *new_block =(Query_cache_block *) *border; + uint tablename_offset = block->table()->table() - block->table()->db(); + char *data = (char*) block->data(); + byte *key; + uint key_length; + key=query_cache_table_get_key((byte*) block, &key_length, 0); + hash_search(&tables, (byte*) key, key_length); + + block->destroy(); + new_block->init(len); + new_block->type=Query_cache_block::TABLE; + new_block->used=used; + new_block->n_tables=1; + memmove((char*) new_block->data(), data, len-new_block->headers_len()); + relink(block, new_block, next, prev, pnext, pprev); + if (tables_blocks == block) + tables_blocks = new_block; + + Query_cache_block_table *nlist_root = new_block->table(0); + nlist_root->n = 0; + nlist_root->next = tnext; + tnext->prev = nlist_root; + nlist_root->prev = tprev; + tprev->next = nlist_root; + DBUG_PRINT("qcache", + ("list_root: 0x%lx tnext 0x%lx tprev 0x%lx tprev->next 0x%lx tnext->prev 0x%lx", + (ulong) list_root, (ulong) tnext, (ulong) tprev, + (ulong)tprev->next, (ulong)tnext->prev)); + /* + Go through all queries that uses this table and change them to + point to the new table object + */ + Query_cache_table *new_block_table=new_block->table(); + for (;tnext != nlist_root; tnext=tnext->next) + tnext->parent= new_block_table; + *border += len; + *before = new_block; + /* Fix pointer to table name */ + new_block->table()->table(new_block->table()->db() + tablename_offset); + /* Fix hash to point at moved block */ + hash_replace(&tables, tables.current_record, (byte*) new_block); + + DBUG_PRINT("qcache", ("moved %lu bytes to 0x%lx, new gap at 0x%lx", + len, (ulong) new_block, (ulong) *border)); + break; + } + case Query_cache_block::QUERY: + { + DBUG_PRINT("qcache", ("block 0x%lx QUERY", (ulong) block)); + if (*border == 0) + break; + BLOCK_LOCK_WR(block); + ulong len = block->length, used = block->used; + TABLE_COUNTER_TYPE n_tables = block->n_tables; + Query_cache_block *prev = block->prev, + *next = block->next, + *pprev = block->pprev, + *pnext = block->pnext, + *new_block =(Query_cache_block*) *border; + char *data = (char*) block->data(); + Query_cache_block *first_result_block = ((Query_cache_query *) + block->data())->result(); + byte *key; + uint key_length; + key=query_cache_query_get_key((byte*) block, &key_length, 0); + hash_search(&queries, (byte*) key, key_length); + // Move table of used tables + memmove((char*) new_block->table(0), (char*) block->table(0), + ALIGN_SIZE(n_tables*sizeof(Query_cache_block_table))); + block->query()->unlock_n_destroy(); + block->destroy(); + new_block->init(len); + new_block->type=Query_cache_block::QUERY; + new_block->used=used; + new_block->n_tables=n_tables; + memmove((char*) new_block->data(), data, len - new_block->headers_len()); + relink(block, new_block, next, prev, pnext, pprev); + if (queries_blocks == block) + queries_blocks = new_block; + for (TABLE_COUNTER_TYPE j=0; j < n_tables; j++) + { + Query_cache_block_table *block_table = new_block->table(j); + block_table->next->prev = block_table; + block_table->prev->next = block_table; + } + DBUG_PRINT("qcache", ("after circle tt")); + *border += len; + *before = new_block; + new_block->query()->result(first_result_block); + if (first_result_block != 0) + { + Query_cache_block *result_block = first_result_block; + do + { + result_block->result()->parent(new_block); + result_block = result_block->next; + } while ( result_block != first_result_block ); + } + Query_cache_query *new_query= ((Query_cache_query *) new_block->data()); + my_rwlock_init(&new_query->lock, NULL); + + /* + If someone is writing to this block, inform the writer that the block + has been moved. + */ + NET *net = new_block->query()->writer(); + if (net != 0) + { + net->query_cache_query= (gptr) new_block; + } + /* Fix hash to point at moved block */ + hash_replace(&queries, queries.current_record, (byte*) new_block); + DBUG_PRINT("qcache", ("moved %lu bytes to 0x%lx, new gap at 0x%lx", + len, (ulong) new_block, (ulong) *border)); + break; + } + case Query_cache_block::RES_INCOMPLETE: + case Query_cache_block::RES_BEG: + case Query_cache_block::RES_CONT: + case Query_cache_block::RESULT: + { + DBUG_PRINT("qcache", ("block 0x%lx RES* (%d)", (ulong) block, + (int) block->type)); + if (*border == 0) + break; + Query_cache_block *query_block = block->result()->parent(), + *next = block->next, + *prev = block->prev; + Query_cache_block::block_type type = block->type; + BLOCK_LOCK_WR(query_block); + ulong len = block->length, used = block->used; + Query_cache_block *pprev = block->pprev, + *pnext = block->pnext, + *new_block =(Query_cache_block*) *border; + char *data = (char*) block->data(); + block->destroy(); + new_block->init(len); + new_block->type=type; + new_block->used=used; + memmove((char*) new_block->data(), data, len - new_block->headers_len()); + relink(block, new_block, next, prev, pnext, pprev); + new_block->result()->parent(query_block); + Query_cache_query *query = query_block->query(); + if (query->result() == block) + query->result(new_block); + *border += len; + *before = new_block; + /* If result writing complete && we have free space in block */ + ulong free_space= new_block->length - new_block->used; + free_space-= free_space % ALIGN_SIZE(1); + if (query->result()->type == Query_cache_block::RESULT && + new_block->length > new_block->used && + *gap + free_space > min_allocation_unit && + new_block->length - free_space > min_allocation_unit) + { + *border-= free_space; + *gap+= free_space; + DBUG_PRINT("qcache", + ("rest of result free space added to gap (%lu)", *gap)); + new_block->length -= free_space; + } + BLOCK_UNLOCK_WR(query_block); + DBUG_PRINT("qcache", ("moved %lu bytes to 0x%lx, new gap at 0x%lx", + len, (ulong) new_block, (ulong) *border)); + break; + } + default: + DBUG_PRINT("error", ("unexpected block type %d, block 0x%lx", + (int)block->type, (ulong) block)); + ok = 0; + } + DBUG_RETURN(ok); +} + + +void Query_cache::relink(Query_cache_block *oblock, + Query_cache_block *nblock, + Query_cache_block *next, Query_cache_block *prev, + Query_cache_block *pnext, Query_cache_block *pprev) +{ + if (prev == oblock) //check pointer to himself + { + nblock->prev = nblock; + nblock->next = nblock; + } + else + { + nblock->prev = prev; + prev->next=nblock; + } + if (next != oblock) + { + nblock->next = next; + next->prev=nblock; + } + nblock->pprev = pprev; // Physical pointer to himself have only 1 free block + nblock->pnext = pnext; + pprev->pnext=nblock; + pnext->pprev=nblock; +} + + +my_bool Query_cache::join_results(ulong join_limit) +{ + my_bool has_moving = 0; + DBUG_ENTER("Query_cache::join_results"); + + STRUCT_LOCK(&structure_guard_mutex); + if (queries_blocks != 0) + { + Query_cache_block *block = queries_blocks; + do + { + Query_cache_query *header = block->query(); + if (header->result() != 0 && + header->result()->type == Query_cache_block::RESULT && + header->length() > join_limit) + { + Query_cache_block *new_result_block = + get_free_block(ALIGN_SIZE(header->length()) + + ALIGN_SIZE(sizeof(Query_cache_block)) + + ALIGN_SIZE(sizeof(Query_cache_result)), 1, 0); + if (new_result_block != 0) + { + has_moving = 1; + Query_cache_block *first_result = header->result(); + ulong new_len = (header->length() + + ALIGN_SIZE(sizeof(Query_cache_block)) + + ALIGN_SIZE(sizeof(Query_cache_result))); + if (new_result_block->length > + ALIGN_SIZE(new_len) + min_allocation_unit) + split_block(new_result_block, ALIGN_SIZE(new_len)); + BLOCK_LOCK_WR(block); + header->result(new_result_block); + new_result_block->type = Query_cache_block::RESULT; + new_result_block->n_tables = 0; + new_result_block->used = new_len; + + new_result_block->next = new_result_block->prev = new_result_block; + DBUG_PRINT("qcache", ("new block %lu/%lu (%lu)", + new_result_block->length, + new_result_block->used, + header->length())); + + Query_cache_result *new_result = new_result_block->result(); + new_result->parent(block); + byte *write_to = (byte*) new_result->data(); + Query_cache_block *result_block = first_result; + do + { + ulong len = (result_block->used - result_block->headers_len() - + ALIGN_SIZE(sizeof(Query_cache_result))); + DBUG_PRINT("loop", ("add block %lu/%lu (%lu)", + result_block->length, + result_block->used, + len)); + memcpy((char *) write_to, + (char*) result_block->result()->data(), + len); + write_to += len; + Query_cache_block *old_result_block = result_block; + result_block = result_block->next; + free_memory_block(old_result_block); + } while (result_block != first_result); + BLOCK_UNLOCK_WR(block); + } + } + block = block->next; + } while ( block != queries_blocks ); + } + STRUCT_UNLOCK(&structure_guard_mutex); + DBUG_RETURN(has_moving); +} + + +uint Query_cache::filename_2_table_key (char *key, const char *path, + uint32 *db_length) +{ + char tablename[FN_REFLEN+2], *filename, *dbname; + DBUG_ENTER("Query_cache::filename_2_table_key"); + + /* Safety if filename didn't have a directory name */ + tablename[0]= FN_LIBCHAR; + tablename[1]= FN_LIBCHAR; + /* Convert filename to this OS's format in tablename */ + fn_format(tablename + 2, path, "", "", MY_REPLACE_EXT); + filename= tablename + dirname_length(tablename + 2) + 2; + /* Find start of databasename */ + for (dbname= filename - 2 ; dbname[-1] != FN_LIBCHAR ; dbname--) ; + *db_length= (filename - dbname) - 1; + DBUG_PRINT("qcache", ("table '%-.*s.%s'", *db_length, dbname, filename)); + + DBUG_RETURN((uint) (strmov(strmake(key, dbname, *db_length) + 1, + filename) -key) + 1); +} + +/**************************************************************************** + Functions to be used when debugging +****************************************************************************/ + +#if defined(DBUG_OFF) && !defined(USE_QUERY_CACHE_INTEGRITY_CHECK) + +void wreck(uint line, const char *message) {} +void bins_dump() {} +void cache_dump() {} +void queries_dump() {} +void tables_dump() {} +my_bool check_integrity(bool not_locked) { return 0; } +my_bool in_list(Query_cache_block * root, Query_cache_block * point, + const char *name) { return 0;} +my_bool in_blocks(Query_cache_block * point) { return 0; } + +#else + +void Query_cache::wreck(uint line, const char *message) +{ + THD *thd=current_thd; + DBUG_ENTER("Query_cache::wreck"); + query_cache_size = 0; + if (*message) + DBUG_PRINT("error", (" %s", message)); + DBUG_PRINT("warning", ("==================================")); + DBUG_PRINT("warning", ("%5d QUERY CACHE WRECK => DISABLED",line)); + DBUG_PRINT("warning", ("==================================")); + if (thd) + thd->killed = 1; + cache_dump(); + /* check_integrity(0); */ /* Can't call it here because of locks */ + bins_dump(); + DBUG_VOID_RETURN; +} + + +void Query_cache::bins_dump() +{ + uint i; + + if (!initialized) + { + DBUG_PRINT("qcache", ("Query Cache not initialized")); + return; + } + + DBUG_PRINT("qcache", ("mem_bin_num=%u, mem_bin_steps=%u", + mem_bin_num, mem_bin_steps)); + DBUG_PRINT("qcache", ("-------------------------")); + DBUG_PRINT("qcache", (" size idx step")); + DBUG_PRINT("qcache", ("-------------------------")); + for (i=0; i < mem_bin_steps; i++) + { + DBUG_PRINT("qcache", ("%10lu %3d %10lu", steps[i].size, steps[i].idx, + steps[i].increment)); + } + DBUG_PRINT("qcache", ("-------------------------")); + DBUG_PRINT("qcache", (" size num")); + DBUG_PRINT("qcache", ("-------------------------")); + for (i=0; i < mem_bin_num; i++) + { + DBUG_PRINT("qcache", ("%10lu %3d 0x%lx", bins[i].size, bins[i].number, + (ulong)&(bins[i]))); + if (bins[i].free_blocks) + { + Query_cache_block *block = bins[i].free_blocks; + do{ + DBUG_PRINT("qcache", ("\\-- %lu 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx", + block->length, (ulong)block, + (ulong)block->next, (ulong)block->prev, + (ulong)block->pnext, (ulong)block->pprev)); + block = block->next; + } while ( block != bins[i].free_blocks ); + } + } + DBUG_PRINT("qcache", ("-------------------------")); +} + + +void Query_cache::cache_dump() +{ + if (!initialized) + { + DBUG_PRINT("qcache", ("Query Cache not initialized")); + return; + } + + DBUG_PRINT("qcache", ("-------------------------------------")); + DBUG_PRINT("qcache", (" length used t nt")); + DBUG_PRINT("qcache", ("-------------------------------------")); + Query_cache_block *i = first_block; + do + { + DBUG_PRINT("qcache", + ("%10lu %10lu %1d %2d 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx", + i->length, i->used, (int)i->type, + i->n_tables, (ulong)i, + (ulong)i->next, (ulong)i->prev, (ulong)i->pnext, + (ulong)i->pprev)); + i = i->pnext; + } while ( i != first_block ); + DBUG_PRINT("qcache", ("-------------------------------------")); +} + + +void Query_cache::queries_dump() +{ + + if (!initialized) + { + DBUG_PRINT("qcache", ("Query Cache not initialized")); + return; + } + + DBUG_PRINT("qcache", ("------------------")); + DBUG_PRINT("qcache", (" QUERIES")); + DBUG_PRINT("qcache", ("------------------")); + if (queries_blocks != 0) + { + Query_cache_block *block = queries_blocks; + do + { + uint len; + char *str = (char*) query_cache_query_get_key((byte*) block, &len, 0); + len--; // Point at flags + uint flags = (uint) (uchar) str[len]; + str[len]=0; + DBUG_PRINT("qcache", ("%u (%u,%u) '%s' '%s'", + ((flags & QUERY_CACHE_CLIENT_LONG_FLAG_MASK)? 1:0), + (flags & QUERY_CACHE_CHARSET_CONVERT_MASK), len, + str,strend(str)+1)); + DBUG_PRINT("qcache", ("-b- 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx", (ulong) block, + (ulong) block->next, (ulong) block->prev, + (ulong)block->pnext, (ulong)block->pprev)); + str[len]=(char) flags; + for (TABLE_COUNTER_TYPE t = 0; t < block->n_tables; t++) + { + Query_cache_table *table = block->table(t)->parent; + DBUG_PRINT("qcache", ("-t- '%s' '%s'", table->db(), table->table())); + } + Query_cache_query *header = block->query(); + if (header->result()) + { + Query_cache_block *result_block = header->result(); + Query_cache_block *result_beg = result_block; + do + { + DBUG_PRINT("qcache", ("-r- %u %lu/%lu 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx", + (uint) result_block->type, + result_block->length, result_block->used, + (ulong) result_block, + (ulong) result_block->next, + (ulong) result_block->prev, + (ulong) result_block->pnext, + (ulong) result_block->pprev)); + result_block = result_block->next; + } while ( result_block != result_beg ); + } + } while ((block=block->next) != queries_blocks); + } + else + { + DBUG_PRINT("qcache", ("no queries in list")); + } + DBUG_PRINT("qcache", ("------------------")); +} + + +void Query_cache::tables_dump() +{ + if (!initialized) + { + DBUG_PRINT("qcache", ("Query Cache not initialized")); + return; + } + + DBUG_PRINT("qcache", ("--------------------")); + DBUG_PRINT("qcache", ("TABLES")); + DBUG_PRINT("qcache", ("--------------------")); + if (tables_blocks != 0) + { + Query_cache_block *table_block = tables_blocks; + do + { + Query_cache_table *table = table_block->table(); + DBUG_PRINT("qcache", ("'%s' '%s'", table->db(), table->table())); + table_block = table_block->next; + } while ( table_block != tables_blocks); + } + else + DBUG_PRINT("qcache", ("no tables in list")); + DBUG_PRINT("qcache", ("--------------------")); +} + + +my_bool Query_cache::check_integrity(bool not_locked) +{ + my_bool result = 0; + uint i; + DBUG_ENTER("check_integrity"); + + if (query_cache_size == 0) + { + DBUG_PRINT("qcache", ("Query Cache not initialized")); + DBUG_RETURN(0); + } + if (!not_locked) + STRUCT_LOCK(&structure_guard_mutex); + + if (hash_check(&queries)) + { + DBUG_PRINT("error", ("queries hash is damaged")); + result = 1; + } + + if (hash_check(&tables)) + { + DBUG_PRINT("error", ("tables hash is damaged")); + result = 1; + } + + DBUG_PRINT("qcache", ("physical address check ...")); + ulong free=0, used=0; + Query_cache_block * block = first_block; + do + { + DBUG_PRINT("qcache", ("block 0x%lx, type %u...", + (ulong) block, (uint) block->type)); + // Check allignment + if ((((long)block) % (long) ALIGN_SIZE(1)) != + (((long)first_block) % (long)ALIGN_SIZE(1))) + { + DBUG_PRINT("error", + ("block 0x%lx do not aligned by %d", (ulong) block, + ALIGN_SIZE(1))); + result = 1; + } + // Check memory allocation + if (block->pnext == first_block) // Is it last block? + { + if (((byte*)block) + block->length != + ((byte*)first_block) + query_cache_size) + { + DBUG_PRINT("error", + ("block 0x%lx, type %u, ended at 0x%lx, but cache ended at 0x%lx", + (ulong) block, (uint) block->type, + (ulong) (((byte*)block) + block->length), + (ulong) (((byte*)first_block) + query_cache_size))); + result = 1; + } + } + else + if (((byte*)block) + block->length != ((byte*)block->pnext)) + { + DBUG_PRINT("error", + ("block 0x%lx, type %u, ended at 0x%lx, but next block begining at 0x%lx", + (ulong) block, (uint) block->type, + (ulong) (((byte*)block) + block->length), + (ulong) ((byte*)block->pnext))); + } + if (block->type == Query_cache_block::FREE) + free+= block->length; + else + used+= block->length; + switch(block->type) { + case Query_cache_block::FREE: + { + Query_cache_memory_bin *bin = *((Query_cache_memory_bin **) + block->data()); + //is it correct pointer? + if (((byte*)bin) < ((byte*)bins) || + ((byte*)bin) >= ((byte*)first_block)) + { + DBUG_PRINT("error", + ("free block 0x%lx have bin pointer 0x%lx beyaond of bins array bounds [0x%lx,0x%lx]", + (ulong) block, + (ulong) bin, + (ulong) bins, + (ulong) first_block)); + result = 1; + } + else + { + int idx = (((byte*)bin) - ((byte*)bins)) / + sizeof(Query_cache_memory_bin); + if (in_list(bins[idx].free_blocks, block, "free memory")) + result = 1; + } + break; + } + case Query_cache_block::TABLE: + if (in_list(tables_blocks, block, "tables")) + result = 1; + if (in_table_list(block->table(0), block->table(0), "table list root")) + result = 1; + break; + case Query_cache_block::QUERY: + { + if (in_list(queries_blocks, block, "query")) + result = 1; + for (TABLE_COUNTER_TYPE j=0; j < block->n_tables; j++) + { + Query_cache_block_table *block_table = block->table(j); + Query_cache_block_table *block_table_root = + (Query_cache_block_table *) + (((byte*)block_table->parent) - + ALIGN_SIZE(sizeof(Query_cache_block_table))); + + if (in_table_list(block_table, block_table_root, "table list")) + result = 1; + } + break; + } + case Query_cache_block::RES_INCOMPLETE: + // This type of block can be not lincked yet (in multithread environment) + break; + case Query_cache_block::RES_BEG: + case Query_cache_block::RES_CONT: + case Query_cache_block::RESULT: + { + Query_cache_block * query_block = block->result()->parent(); + if (((byte*)query_block) < ((byte*)first_block) || + ((byte*)query_block) >= (((byte*)first_block) + query_cache_size)) + { + DBUG_PRINT("error", + ("result block 0x%lx have query block pointer 0x%lx beyaond of block pool bounds [0x%lx,0x%lx]", + (ulong) block, + (ulong) query_block, + (ulong) first_block, + (ulong) (((byte*)first_block) + query_cache_size))); + result = 1; + } + else + { + BLOCK_LOCK_RD(query_block); + if (in_list(queries_blocks, query_block, "query from results")) + result = 1; + if (in_list(query_block->query()->result(), block, + "results")) + result = 1; + BLOCK_UNLOCK_RD(query_block); + } + break; + } + default: + DBUG_PRINT("error", + ("block 0x%lx have incorrect type %u", + block, block->type)); + result = 1; + } + + block = block->pnext; + } while (block != first_block); + + if (used + free != query_cache_size) + { + DBUG_PRINT("error", + ("used memory (%lu) + free memory (%lu) != query_cache_size (%lu)", + used, free, query_cache_size)); + result = 1; + } + + if (free != free_memory) + { + DBUG_PRINT("error", + ("free memory (%lu) != free_memory (%lu)", + free, free_memory)); + result = 1; + } + + DBUG_PRINT("qcache", ("check queries ...")); + if ((block = queries_blocks)) + { + do + { + DBUG_PRINT("qcache", ("block 0x%lx, type %u...", + (ulong) block, (uint) block->type)); + uint length; + byte *key = query_cache_query_get_key((byte*) block, &length, 0); + gptr val = hash_search(&queries, key, length); + if (((gptr)block) != val) + { + DBUG_PRINT("error", ("block 0x%lx found in queries hash like 0x%lx", + (ulong) block, (ulong) val)); + } + if (in_blocks(block)) + result = 1; + Query_cache_block * results = block->query()->result(); + if (results) + { + Query_cache_block * result_block = results; + do + { + DBUG_PRINT("qcache", ("block 0x%lx, type %u...", + (ulong) block, (uint) block->type)); + if (in_blocks(result_block)) + result = 1; + + result_block = result_block->next; + } while (result_block != results); + } + block = block->next; + } while (block != queries_blocks); + } + + DBUG_PRINT("qcache", ("check tables ...")); + if ((block = tables_blocks)) + { + do + { + DBUG_PRINT("qcache", ("block 0x%lx, type %u...", + (ulong) block, (uint) block->type)); + uint length; + byte *key = query_cache_table_get_key((byte*) block, &length, 0); + gptr val = hash_search(&tables, key, length); + if (((gptr)block) != val) + { + DBUG_PRINT("error", ("block 0x%lx found in tables hash like 0x%lx", + (ulong) block, (ulong) val)); + } + + if (in_blocks(block)) + result = 1; + block=block->next; + } while (block != tables_blocks); + } + + DBUG_PRINT("qcache", ("check free blocks")); + for (i = 0; i < mem_bin_num; i++) + { + if ((block = bins[i].free_blocks)) + { + uint count = 0; + do + { + DBUG_PRINT("qcache", ("block 0x%lx, type %u...", + (ulong) block, (uint) block->type)); + if (in_blocks(block)) + result = 1; + + count++; + block=block->next; + } while (block != bins[i].free_blocks); + if (count != bins[i].number) + { + DBUG_PRINT("error", ("bin[%d].number is %d, but bin have %d blocks", + bins[i].number, count)); + result = 1; + } + } + } + DBUG_ASSERT(result == 0); + if (!not_locked) + STRUCT_UNLOCK(&structure_guard_mutex); + DBUG_RETURN(result); +} + + +my_bool Query_cache::in_blocks(Query_cache_block * point) +{ + my_bool result = 0; + Query_cache_block *block = point; + //back + do + { + if (block->pprev->pnext != block) + { + DBUG_PRINT("error", + ("block 0x%lx in physical list is incorrect linked, prev block 0x%lx refered as next to 0x%lx (check from 0x%lx)", + (ulong) block, (ulong) block->pprev, + (ulong) block->pprev->pnext, + (ulong) point)); + //back trace + for (; block != point; block = block->pnext) + DBUG_PRINT("error", ("back trace 0x%lx", (ulong) block)); + result = 1; + goto err1; + } + block = block->pprev; + } while (block != first_block && block != point); + if (block != first_block) + { + DBUG_PRINT("error", + ("block 0x%lx (0x%lx<-->0x%lx) not owned by pysical list", + (ulong) block, (ulong) block->pprev, (ulong )block->pnext)); + return 1; + } + +err1: + //forward + block = point; + do + { + if (block->pnext->pprev != block) + { + DBUG_PRINT("error", + ("block 0x%lx in physicel list is incorrect linked, next block 0x%lx refered as prev to 0x%lx (check from 0x%lx)", + (ulong) block, (ulong) block->pnext, + (ulong) block->pnext->pprev, + (ulong) point)); + //back trace + for (; block != point; block = block->pprev) + DBUG_PRINT("error", ("back trace 0x%lx", (ulong) block)); + result = 1; + goto err2; + } + block = block->pnext; + } while (block != first_block); +err2: + return result; +} + + +my_bool Query_cache::in_list(Query_cache_block * root, + Query_cache_block * point, + const char *name) +{ + my_bool result = 0; + Query_cache_block *block = point; + //back + do + { + if (block->prev->next != block) + { + DBUG_PRINT("error", + ("block 0x%lx in list '%s' 0x%lx is incorrect linked, prev block 0x%lx refered as next to 0x%lx (check from 0x%lx)", + (ulong) block, name, (ulong) root, (ulong) block->prev, + (ulong) block->prev->next, + (ulong) point)); + //back trace + for (; block != point; block = block->next) + DBUG_PRINT("error", ("back trace 0x%lx", (ulong) block)); + result = 1; + goto err1; + } + block = block->prev; + } while (block != root && block != point); + if (block != root) + { + DBUG_PRINT("error", + ("block 0x%lx (0x%lx<-->0x%lx) not owned by list '%s' 0x%lx", + (ulong) block, + (ulong) block->prev, (ulong) block->next, + name, (ulong) root)); + return 1; + } +err1: + // forward + block = point; + do + { + if (block->next->prev != block) + { + DBUG_PRINT("error", + ("block 0x%lx in list '%s' 0x%lx is incorrect linked, next block 0x%lx refered as prev to 0x%lx (check from 0x%lx)", + (ulong) block, name, (ulong) root, (ulong) block->next, + (ulong) block->next->prev, + (ulong) point)); + //back trace + for (; block != point; block = block->prev) + DBUG_PRINT("error", ("back trace 0x%lx", (ulong) block)); + result = 1; + goto err2; + } + block = block->next; + } while (block != root); +err2: + return result; +} + +void dump_node(Query_cache_block_table * node, + const char * call, const char * descr) +{ + DBUG_PRINT("qcache", ("%s: %s: node: 0x%lx", call, descr, (ulong) node)); + DBUG_PRINT("qcache", ("%s: %s: node block: 0x%lx", + call, descr, (ulong) node->block())); + DBUG_PRINT("qcache", ("%s: %s: next: 0x%lx", call, descr, + (ulong) node->next)); + DBUG_PRINT("qcache", ("%s: %s: prev: 0x%lx", call, descr, + (ulong) node->prev)); +} + +my_bool Query_cache::in_table_list(Query_cache_block_table * root, + Query_cache_block_table * point, + const char *name) +{ + my_bool result = 0; + Query_cache_block_table *table = point; + dump_node(root, name, "parameter root"); + //back + do + { + dump_node(table, name, "list element << "); + if (table->prev->next != table) + { + DBUG_PRINT("error", + ("table 0x%lx(0x%lx) in list '%s' 0x%lx(0x%lx) is incorrect linked, prev table 0x%lx(0x%lx) refered as next to 0x%lx(0x%lx) (check from 0x%lx(0x%lx))", + (ulong) table, (ulong) table->block(), name, + (ulong) root, (ulong) root->block(), + (ulong) table->prev, (ulong) table->prev->block(), + (ulong) table->prev->next, + (ulong) table->prev->next->block(), + (ulong) point, (ulong) point->block())); + //back trace + for (; table != point; table = table->next) + DBUG_PRINT("error", ("back trace 0x%lx(0x%lx)", + (ulong) table, (ulong) table->block())); + result = 1; + goto err1; + } + table = table->prev; + } while (table != root && table != point); + if (table != root) + { + DBUG_PRINT("error", + ("table 0x%lx(0x%lx) (0x%lx(0x%lx)<-->0x%lx(0x%lx)) not owned by list '%s' 0x%lx(0x%lx)", + (ulong) table, (ulong) table->block(), + (ulong) table->prev, (ulong) table->prev->block(), + (ulong) table->next, (ulong) table->next->block(), + name, (ulong) root, (ulong) root->block())); + return 1; + } +err1: + // forward + table = point; + do + { + dump_node(table, name, "list element >> "); + if (table->next->prev != table) + { + DBUG_PRINT("error", + ("table 0x%lx(0x%lx) in list '%s' 0x%lx(0x%lx) is incorrect linked, next table 0x%lx(0x%lx) refered as prev to 0x%lx(0x%lx) (check from 0x%lx(0x%lx))", + (ulong) table, (ulong) table->block(), + name, (ulong) root, (ulong) root->block(), + (ulong) table->next, (ulong) table->next->block(), + (ulong) table->next->prev, + (ulong) table->next->prev->block(), + (ulong) point, (ulong) point->block())); + //back trace + for (; table != point; table = table->prev) + DBUG_PRINT("error", ("back trace 0x%lx(0x%lx)", + (ulong) table, (ulong) table->block())); + result = 1; + goto err2; + } + table = table->next; + } while (table != root); +err2: + return result; +} + +#endif /* DBUG_OFF */ + +#endif /*HAVE_QUERY_CACHE*/ diff --git a/sql/sql_cache.h b/sql/sql_cache.h new file mode 100644 index 00000000000..b15df28f54b --- /dev/null +++ b/sql/sql_cache.h @@ -0,0 +1,407 @@ +/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef _SQL_CACHE_H +#define _SQL_CACHE_H + +/* Query cache */ + +/* + Can't create new free memory block if unused memory in block less + then QUERY_CACHE_MIN_ALLOCATION_UNIT. + if QUERY_CACHE_MIN_ALLOCATION_UNIT == 0 then + QUERY_CACHE_MIN_ALLOCATION_UNIT choosed automaticaly +*/ +#define QUERY_CACHE_MIN_ALLOCATION_UNIT 512 + +/* inittial size of hashes */ +#define QUERY_CACHE_DEF_QUERY_HASH_SIZE 1024 +#define QUERY_CACHE_DEF_TABLE_HASH_SIZE 1024 + +/* minimal result data size when data allocated */ +#define QUERY_CACHE_MIN_RESULT_DATA_SIZE 1024*4 + +/* + start estimation of first result block size only when number of queries + bigger then: +*/ +#define QUERY_CACHE_MIN_ESTIMATED_QUERIES_NUMBER 3 + + + +/* memory bins size spacing (see at Query_cache::init_cache (sql_cache.cc)) */ +#define QUERY_CACHE_MEM_BIN_FIRST_STEP_PWR2 4 +#define QUERY_CACHE_MEM_BIN_STEP_PWR2 2 +#define QUERY_CACHE_MEM_BIN_PARTS_INC 1 +#define QUERY_CACHE_MEM_BIN_PARTS_MUL 1.2 +#define QUERY_CACHE_MEM_BIN_SPC_LIM_PWR2 3 + +/* how many free blocks check when finding most suitable before other 'end' + of list of free blocks */ +#define QUERY_CACHE_MEM_BIN_TRY 5 + +/* query flags masks */ +#define QUERY_CACHE_CLIENT_LONG_FLAG_MASK 0x80 +#define QUERY_CACHE_CHARSET_CONVERT_MASK 0x7F + +/* packing parameters */ +#define QUERY_CACHE_PACK_ITERATION 2 +#define QUERY_CACHE_PACK_LIMIT (512*1024L) + +#define TABLE_COUNTER_TYPE uint8 + +struct Query_cache_block; +struct Query_cache_block_table; +struct Query_cache_table; +struct Query_cache_query; +struct Query_cache_result; +class Query_cache; + + +struct Query_cache_block_table +{ + TABLE_COUNTER_TYPE n; // numbr in table (from 0) + Query_cache_block_table *next, *prev; + Query_cache_table *parent; + inline Query_cache_block *block(); +}; + + +struct Query_cache_block +{ + enum block_type {FREE, QUERY, RESULT, RES_CONT, RES_BEG, + RES_INCOMPLETE, TABLE, INCOMPLETE}; + + ulong length; // length of all block + ulong used; // length of data + /* + Not used **pprev, **prev because really needed access to pervious block: + *pprev to join free blocks + *prev to access to opposite side of list in cyclic sorted list + */ + Query_cache_block *pnext,*pprev, // physical next/previous block + *next,*prev; // logical next/previous block + block_type type; + TABLE_COUNTER_TYPE n_tables; // number of tables in query + + inline my_bool is_free(void) { return type == FREE; } + void init(ulong length); + void destroy(); + inline uint headers_len(); + inline gptr data(void); + inline Query_cache_query *query(); + inline Query_cache_table *table(); + inline Query_cache_result *result(); + inline Query_cache_block_table *table(TABLE_COUNTER_TYPE n); +}; + +struct Query_cache_query +{ + ulonglong limit_found_rows; + rw_lock_t lock; + Query_cache_block *res; + NET *wri; + ulong len; + + inline void init_n_lock(); + void unlock_n_destroy(); + inline ulonglong found_rows() { return limit_found_rows; } + inline void found_rows(ulonglong rows) { limit_found_rows = rows; } + inline Query_cache_block *result() { return res; } + inline void result(Query_cache_block *p) { res=p; } + inline NET *writer() { return wri; } + inline void writer(NET *p) { wri=p; } + inline ulong length() { return len; } + inline ulong add(ulong packet_len) { return(len += packet_len); } + inline void length(ulong length) { len = length; } + inline gptr query() + { + return (gptr)(((byte*)this)+ + ALIGN_SIZE(sizeof(Query_cache_query))); + } + void lock_writing(); + void lock_reading(); + my_bool try_lock_writing(); + void unlock_writing(); + void unlock_reading(); + static byte *cache_key(const byte *record, uint *length, my_bool not_used); +}; + + +struct Query_cache_table +{ + char *tbl; + + inline char *db() { return (char *) data(); } + inline char *table() { return tbl; } + inline void table(char *table) { tbl = table; } + inline gptr data() + { + return (gptr)(((byte*)this)+ + ALIGN_SIZE(sizeof(Query_cache_table))); + } +}; + +struct Query_cache_result +{ + Query_cache_block *query; + + inline gptr data() + { + return (gptr)(((byte*) this)+ + ALIGN_SIZE(sizeof(Query_cache_result))); + } + /* data_continue (if not whole packet contained by this block) */ + inline Query_cache_block *parent() { return query; } + inline void parent (Query_cache_block *p) { query=p; } +}; + + +extern "C" +{ + byte *query_cache_query_get_key(const byte *record, uint *length, + my_bool not_used); + byte *query_cache_table_get_key(const byte *record, uint *length, + my_bool not_used); +} +void query_cache_insert(NET *thd, const char *packet, ulong length); +extern "C" void query_cache_invalidate_by_MyISAM_filename(const char* filename); + + +struct Query_cache_memory_bin +{ +#ifndef DBUG_OFF + ulong size; +#endif + uint number; + Query_cache_block *free_blocks; + + inline void init(ulong size_arg) + { +#ifndef DBUG_OFF + size = size_arg; +#endif + number = 0; + free_blocks = 0; + } +}; + +struct Query_cache_memory_bin_step +{ + ulong size; + ulong increment; + uint idx; + inline void init(ulong size_arg, uint idx_arg, ulong increment_arg) + { + size = size_arg; + idx = idx_arg; + increment = increment_arg; + } +}; + +class Query_cache +{ +public: + /* Info */ + ulong query_cache_size, query_cache_limit; + /* statistics */ + ulong free_memory, queries_in_cache, hits, inserts, refused, + free_memory_blocks, total_blocks, lowmem_prunes; + +protected: + /* + The following mutex is locked when searching or changing global + query, tables lists or hashes. When we are operating inside the + query structure we locked an internal query block mutex. + LOCK SEQUENCE (to prevent deadlocks): + 1. structure_guard_mutex + 2. query block (for operation inside query (query block/results)) + */ + pthread_mutex_t structure_guard_mutex; + byte *cache; // cache memory + Query_cache_block *first_block; // physical location block list + Query_cache_block *queries_blocks; // query list (LIFO) + Query_cache_block *tables_blocks; + + Query_cache_memory_bin *bins; // free block lists + Query_cache_memory_bin_step *steps; // bins spacing info + HASH queries, tables; + /* options */ + ulong min_allocation_unit, min_result_data_size; + uint def_query_hash_size, def_table_hash_size; + uint mem_bin_num, mem_bin_steps; // See at init_cache & find_bin + + my_bool initialized; + + /* Exclude/include from cyclic double linked list */ + static void double_linked_list_exclude(Query_cache_block *point, + Query_cache_block **list_pointer); + static void double_linked_list_simple_include(Query_cache_block *point, + Query_cache_block ** + list_pointer); + static void double_linked_list_join(Query_cache_block *head_tail, + Query_cache_block *tail_head); + + /* Table key generation */ + static uint filename_2_table_key (char *key, const char *filename, + uint32 *db_langth); + + /* The following functions require that structure_guard_mutex is locked */ + void flush_cache(); + my_bool free_old_query(); + void free_query(Query_cache_block *point); + my_bool allocate_data_chain(Query_cache_block **result_block, + ulong data_len, + Query_cache_block *query_block, + my_bool first_block); + void invalidate_table(TABLE_LIST *table); + void invalidate_table(TABLE *table); + void invalidate_table(byte *key, uint32 key_length); + void invalidate_table(Query_cache_block *table_block); + my_bool register_all_tables(Query_cache_block *block, + TABLE_LIST *tables_used, + TABLE_COUNTER_TYPE tables); + my_bool insert_table(uint key_len, char *key, + Query_cache_block_table *node, + uint32 db_length); + void unlink_table(Query_cache_block_table *node); + Query_cache_block *get_free_block (ulong len, my_bool not_less, + ulong min); + void free_memory_block(Query_cache_block *point); + void split_block(Query_cache_block *block, ulong len); + Query_cache_block *join_free_blocks(Query_cache_block *first_block, + Query_cache_block *block_in_list); + my_bool append_next_free_block(Query_cache_block *block, + ulong add_size); + void exclude_from_free_memory_list(Query_cache_block *free_block); + void insert_into_free_memory_list(Query_cache_block *new_block); + my_bool move_by_type(byte **border, Query_cache_block **before, + ulong *gap, Query_cache_block *i); + uint find_bin(ulong size); + void move_to_query_list_end(Query_cache_block *block); + void insert_into_free_memory_sorted_list(Query_cache_block *new_block, + Query_cache_block **list); + void pack_cache(); + void relink(Query_cache_block *oblock, + Query_cache_block *nblock, + Query_cache_block *next, + Query_cache_block *prev, + Query_cache_block *pnext, + Query_cache_block *pprev); + my_bool join_results(ulong join_limit); + + /* + Following function control structure_guard_mutex + by themself or don't need structure_guard_mutex + */ + void init(); + ulong init_cache(); + void make_disabled(); + void free_cache(my_bool destruction); + Query_cache_block *write_block_data(ulong data_len, gptr data, + ulong header_len, + Query_cache_block::block_type type, + TABLE_COUNTER_TYPE ntab = 0, + my_bool under_guard=0); + my_bool append_result_data(Query_cache_block **result, + ulong data_len, gptr data, + Query_cache_block *parent); + my_bool write_result_data(Query_cache_block **result, + ulong data_len, gptr data, + Query_cache_block *parent, + Query_cache_block::block_type + type=Query_cache_block::RESULT); + inline ulong get_min_first_result_data_size(); + inline ulong get_min_append_result_data_size(); + Query_cache_block *allocate_block(ulong len, my_bool not_less, + ulong min, + my_bool under_guard=0); + /* + If query is cacheable return number tables in query + (query without tables not cached) + */ + TABLE_COUNTER_TYPE is_cacheable(THD *thd, uint32 query_len, char *query, + LEX *lex, TABLE_LIST *tables_used); + public: + + Query_cache(ulong query_cache_limit = ULONG_MAX, + ulong min_allocation_unit = QUERY_CACHE_MIN_ALLOCATION_UNIT, + ulong min_result_data_size = QUERY_CACHE_MIN_RESULT_DATA_SIZE, + uint def_query_hash_size = QUERY_CACHE_DEF_QUERY_HASH_SIZE, + uint def_table_hash_size = QUERY_CACHE_DEF_TABLE_HASH_SIZE); + + /* resize query cache (return real query size, 0 if disabled) */ + ulong resize(ulong query_cache_size); + inline void result_size_limit(ulong limit){query_cache_limit=limit;} + + /* register query in cache */ + void store_query(THD *thd, TABLE_LIST *used_tables); + + /* + Check if the query is in the cache and if this is true send the + data to client. + */ + int send_result_to_client(THD *thd, char *query, uint query_length); + + /* Remove all queries that uses any of the listed following tables */ + void invalidate(THD* thd, TABLE_LIST *tables_used, + my_bool using_transactions); + void invalidate(CHANGED_TABLE_LIST *tables_used); + void invalidate(THD* thd, TABLE *table, my_bool using_transactions); + void invalidate(THD *thd, const char *key, uint32 key_length, + my_bool using_transactions); + + /* Remove all queries that uses any of the tables in following database */ + void invalidate(char *db); + + /* Remove all queries that uses any of the listed following table */ + void invalidate_by_MyISAM_filename(const char *filename); + + void flush(); + void pack(ulong join_limit = QUERY_CACHE_PACK_LIMIT, + uint iteration_limit = QUERY_CACHE_PACK_ITERATION); + + void destroy(); + + friend void query_cache_insert(NET *net, const char *packet, ulong length); + friend void query_cache_end_of_result(NET *net); + friend void query_cache_abort(NET *net); + + /* + The following functions are only used when debugging + We don't protect these with ifndef DEBUG_OFF to not have to recompile + everything if we want to add checks of the cache at some places. + */ + void wreck(uint line, const char *message); + void bins_dump(); + void cache_dump(); + void queries_dump(); + void tables_dump(); + my_bool check_integrity(bool not_locked); + my_bool in_list(Query_cache_block * root, Query_cache_block * point, + const char *name); + my_bool in_table_list(Query_cache_block_table * root, + Query_cache_block_table * point, + const char *name); + my_bool in_blocks(Query_cache_block * point); +}; + +extern Query_cache query_cache; +extern TYPELIB query_cache_type_typelib; +void query_cache_end_of_result(NET *net); +void query_cache_abort(NET *net); + +#endif diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 316c544ba78..a99d17b0ec4 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -34,8 +34,9 @@ #ifdef __WIN__ #include <io.h> #endif +#include <mysys_err.h> +#include <assert.h> -extern struct rand_struct sql_rand; /***************************************************************************** ** Instansiate templates @@ -57,14 +58,14 @@ template class List_iterator<Alter_column>; ** User variables ****************************************************************************/ -static byte* get_var_key(user_var_entry *entry, uint *length, - my_bool not_used __attribute__((unused))) +extern "C" byte *get_var_key(user_var_entry *entry, uint *length, + my_bool not_used __attribute__((unused))) { *length=(uint) entry->name.length; return (byte*) entry->name.str; } -static void free_var(user_var_entry *entry) +extern "C" void free_user_var(user_var_entry *entry) { char *pos= (char*) entry+ALIGN_SIZE(sizeof(*entry)); if (entry->value && entry->value != pos) @@ -78,66 +79,65 @@ static void free_var(user_var_entry *entry) ****************************************************************************/ THD::THD():user_time(0),fatal_error(0),last_insert_id_used(0), - insert_id_used(0),in_lock_tables(0), + insert_id_used(0),rand_used(0),in_lock_tables(0), global_read_lock(0),bootstrap(0) { host=user=priv_user=db=query=ip=0; + host_or_ip= "connecting host"; locked=killed=count_cuted_fields=some_tables_deleted=no_errors=password= - query_start_used=0; - query_length=col_access=0; + query_start_used=safe_to_cache_query=0; + db_length=query_length=col_access=0; query_error=0; next_insert_id=last_insert_id=0; - open_tables=temporary_tables=0; + open_tables=temporary_tables=handler_tables=0; + current_tablenr=0; + handler_items=0; tmp_table=0; lock=locked_tables=0; used_tables=0; - gemini_spin_retries=0; cuted_fields=sent_row_count=0L; start_time=(time_t) 0; current_linfo = 0; slave_thread = 0; slave_proxy_id = 0; - last_nx_table = last_nx_db = 0; + file_id = 0; cond_count=0; - convert_set=0; mysys_var=0; +#ifndef DBUG_OFF + dbug_sentry=THD_SENTRY_MAGIC; +#endif net.vio=0; + net.last_error[0]=0; // If error on boot ull=0; system_thread=cleanup_done=0; + transaction.changed_tables = 0; #ifdef __WIN__ real_id = 0; #endif -#ifdef HAVE_GEMINI_DB - bzero((char *)&gemini, sizeof(gemini)); -#endif #ifdef SIGNAL_WITH_VIO_CLOSE active_vio = 0; - pthread_mutex_init(&active_vio_lock, MY_MUTEX_INIT_FAST); #endif + pthread_mutex_init(&LOCK_delete, MY_MUTEX_INIT_FAST); /* Variables with default values */ proc_info="login"; where="field list"; server_id = ::server_id; - server_status=SERVER_STATUS_AUTOCOMMIT; - update_lock_default= low_priority_updates ? TL_WRITE_LOW_PRIORITY : TL_WRITE; - options=thd_startup_options; - sql_mode=(uint) opt_sql_mode; - inactive_timeout=net_wait_timeout; - open_options=ha_open_options; - tx_isolation=session_tx_isolation=default_tx_isolation; + slave_net = 0; + log_pos = 0; command=COM_CONNECT; set_query_id=1; - default_select_limit= HA_POS_ERROR; - max_join_size= ((::max_join_size != ~ (ulong) 0L) ? ::max_join_size : - HA_POS_ERROR); db_access=NO_ACCESS; + version=refresh_version; // For boot + init(); /* Initialize sub structures */ bzero((char*) &mem_root,sizeof(mem_root)); + bzero((char*) &transaction.mem_root,sizeof(transaction.mem_root)); + user_connect=(USER_CONN *)0; hash_init(&user_vars, USER_VARS_HASH_SIZE, 0, 0, (hash_get_key) get_var_key, - (void (*)(void*)) free_var,0); + (hash_free_key) free_user_var,0); #ifdef USING_TRANSACTIONS bzero((char*) &transaction,sizeof(transaction)); if (opt_using_transactions) @@ -150,19 +150,60 @@ THD::THD():user_time(0),fatal_error(0),last_insert_id_used(0), } #endif - /* - We need good random number initialization for new thread - Just coping global one will not work + /* + We need good random number initialization for new thread + Just coping global one will not work */ { pthread_mutex_lock(&LOCK_thread_count); - ulong tmp=(ulong) (rnd(&sql_rand) * ((ulong)0xffffffff)); - randominit(&rand, tmp + (ulong) start_time, - tmp + (ulong) thread_id); + ulong tmp=(ulong) (rnd(&sql_rand) * 0xffffffff); /* make all bits random */ pthread_mutex_unlock(&LOCK_thread_count); + randominit(&rand, tmp + (ulong) &rand, tmp + (ulong) ::query_id); } } + +/* + Init common variables that has to be reset on start and on change_user +*/ + +void THD::init(void) +{ + pthread_mutex_lock(&LOCK_global_system_variables); + variables= global_system_variables; + pthread_mutex_unlock(&LOCK_global_system_variables); + server_status= SERVER_STATUS_AUTOCOMMIT; + options= thd_startup_options; + sql_mode=(uint) opt_sql_mode; + open_options=ha_open_options; + update_lock_default= (variables.low_priority_updates ? + TL_WRITE_LOW_PRIORITY : + TL_WRITE); + session_tx_isolation= (enum_tx_isolation) variables.tx_isolation; +} + +/* + Do what's needed when one invokes change user + + SYNOPSIS + change_user() + + IMPLEMENTATION + Reset all resources that are connection specific +*/ + + +void THD::change_user(void) +{ + cleanup(); + cleanup_done=0; + init(); + hash_init(&user_vars, USER_VARS_HASH_SIZE, 0, 0, + (hash_get_key) get_var_key, + (hash_free_key) free_user_var,0); +} + + /* Do operations that may take a long time */ void THD::cleanup(void) @@ -174,21 +215,35 @@ void THD::cleanup(void) lock=locked_tables; locked_tables=0; close_thread_tables(this); } + if (handler_tables) + { + open_tables=handler_tables; handler_tables=0; + close_thread_tables(this); + } close_temporary_tables(this); -#ifdef USING_TRANSACTIONS - if (opt_using_transactions) + hash_free(&user_vars); + if (global_read_lock) + unlock_global_read_lock(this); + if (ull) { - close_cached_file(&transaction.trans_log); - ha_close_connection(this); + pthread_mutex_lock(&LOCK_user_locks); + item_user_lock_release(ull); + pthread_mutex_unlock(&LOCK_user_locks); + ull= 0; } -#endif cleanup_done=1; DBUG_VOID_RETURN; } + THD::~THD() { + THD_CHECK_SENTRY(this); DBUG_ENTER("~THD()"); + /* Ensure that no one is using THD */ + pthread_mutex_lock(&LOCK_delete); + pthread_mutex_unlock(&LOCK_delete); + /* Close connection */ if (net.vio) { @@ -197,23 +252,15 @@ THD::~THD() } if (!cleanup_done) cleanup(); - if (global_read_lock) - { - pthread_mutex_lock(&LOCK_open); - ::global_read_lock--; - pthread_cond_broadcast(&COND_refresh); - pthread_mutex_unlock(&LOCK_open); - } - if (ull) +#ifdef USING_TRANSACTIONS + if (opt_using_transactions) { - pthread_mutex_lock(&LOCK_user_locks); - item_user_lock_release(ull); - pthread_mutex_unlock(&LOCK_user_locks); + close_cached_file(&transaction.trans_log); + ha_close_connection(this); } - hash_free(&user_vars); +#endif DBUG_PRINT("info", ("freeing host")); - if (host != localhost) // If not pointer to constant safeFree(host); if (user != delayed_user) @@ -221,44 +268,165 @@ THD::~THD() safeFree(db); safeFree(ip); free_root(&mem_root,MYF(0)); + free_root(&transaction.mem_root,MYF(0)); mysys_var=0; // Safety (shouldn't be needed) -#ifdef SIGNAL_WITH_VIO_CLOSE - pthread_mutex_destroy(&active_vio_lock); + pthread_mutex_destroy(&LOCK_delete); +#ifndef DBUG_OFF + dbug_sentry = THD_SENTRY_GONE; #endif DBUG_VOID_RETURN; } -void THD::prepare_to_die() + +void THD::awake(bool prepare_to_die) { + THD_CHECK_SENTRY(this); + safe_mutex_assert_owner(&LOCK_delete); + + if (prepare_to_die) + killed = 1; thr_alarm_kill(real_id); - killed = 1; #ifdef SIGNAL_WITH_VIO_CLOSE close_active_vio(); #endif if (mysys_var) + { + pthread_mutex_lock(&mysys_var->mutex); + if (!system_thread) // Don't abort locks + mysys_var->abort=1; + /* + This broadcast could be up in the air if the victim thread + exits the cond in the time between read and broadcast, but that is + ok since all we want to do is to make the victim thread get out + of waiting on current_cond. + */ + if (mysys_var->current_cond) { - pthread_mutex_lock(&mysys_var->mutex); - if (!system_thread) // Don't abort locks - mysys_var->abort=1; - if (mysys_var->current_cond) + pthread_mutex_lock(mysys_var->current_mutex); + pthread_cond_broadcast(mysys_var->current_cond); + pthread_mutex_unlock(mysys_var->current_mutex); + } + pthread_mutex_unlock(&mysys_var->mutex); + } +} + +/* + Remember the location of thread info, the structure needed for + sql_alloc() and the structure for the net buffer +*/ + +bool THD::store_globals() +{ + if (my_pthread_setspecific_ptr(THR_THD, this) || + my_pthread_setspecific_ptr(THR_MALLOC, &mem_root) || + my_pthread_setspecific_ptr(THR_NET, &net)) + return 1; + mysys_var=my_thread_var; + dbug_thread_id=my_thread_id(); + return 0; +} + + +/* routings to adding tables to list of changed in transaction tables */ + +inline static void list_include(CHANGED_TABLE_LIST** prev, + CHANGED_TABLE_LIST* curr, + CHANGED_TABLE_LIST* new_table) +{ + if (new_table) + { + *prev = new_table; + (*prev)->next = curr; + } +} + +/* add table to list of changed in transaction tables */ + +void THD::add_changed_table(TABLE *table) +{ + DBUG_ENTER("THD::add_changed_table(table)"); + + DBUG_ASSERT((options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) && + table->file->has_transactions()); + add_changed_table(table->table_cache_key, table->key_length); + DBUG_VOID_RETURN; +} + + +void THD::add_changed_table(const char *key, long key_length) +{ + DBUG_ENTER("THD::add_changed_table(key)"); + CHANGED_TABLE_LIST **prev_changed = &transaction.changed_tables; + CHANGED_TABLE_LIST *curr = transaction.changed_tables; + + for (; curr; prev_changed = &(curr->next), curr = curr->next) + { + int cmp = (long)curr->key_length - (long)key_length; + if (cmp < 0) + { + list_include(prev_changed, curr, changed_table_dup(key, key_length)); + DBUG_PRINT("info", + ("key_length %u %u", key_length, (*prev_changed)->key_length)); + DBUG_VOID_RETURN; + } + else if (cmp == 0) + { + cmp = memcmp(curr->key, key, curr->key_length); + if (cmp < 0) { - pthread_mutex_lock(mysys_var->current_mutex); - pthread_cond_broadcast(mysys_var->current_cond); - pthread_mutex_unlock(mysys_var->current_mutex); + list_include(prev_changed, curr, changed_table_dup(key, key_length)); + DBUG_PRINT("info", + ("key_length %u %u", key_length, + (*prev_changed)->key_length)); + DBUG_VOID_RETURN; + } + else if (cmp == 0) + { + DBUG_PRINT("info", ("already in list")); + DBUG_VOID_RETURN; } - pthread_mutex_unlock(&mysys_var->mutex); } + } + *prev_changed = changed_table_dup(key, key_length); + DBUG_PRINT("info", ("key_length %u %u", key_length, + (*prev_changed)->key_length)); + DBUG_VOID_RETURN; } -// remember the location of thread info, the structure needed for -// sql_alloc() and the structure for the net buffer -bool THD::store_globals() +CHANGED_TABLE_LIST* THD::changed_table_dup(const char *key, long key_length) +{ + CHANGED_TABLE_LIST* new_table = + (CHANGED_TABLE_LIST*) trans_alloc(ALIGN_SIZE(sizeof(CHANGED_TABLE_LIST))+ + key_length + 1); + if (!new_table) + { + my_error(EE_OUTOFMEMORY, MYF(ME_BELL), + ALIGN_SIZE(sizeof(TABLE_LIST)) + key_length + 1); + killed= 1; + return 0; + } + + new_table->key = (char *) (((byte*)new_table)+ + ALIGN_SIZE(sizeof(CHANGED_TABLE_LIST))); + new_table->next = 0; + new_table->key_length = key_length; + ::memcpy(new_table->key, key, key_length); + return new_table; +} + + +#ifdef SIGNAL_WITH_VIO_CLOSE +void THD::close_active_vio() { - return (my_pthread_setspecific_ptr(THR_THD, this) || - my_pthread_setspecific_ptr(THR_MALLOC, &mem_root) || - my_pthread_setspecific_ptr(THR_NET, &net)); + safe_mutex_assert_owner(&LOCK_delete); + if (active_vio) + { + vio_close(active_vio); + active_vio = 0; + } } +#endif /***************************************************************************** ** Functions to provide a interface to select results @@ -291,10 +459,18 @@ bool select_send::send_fields(List<Item> &list,uint flag) bool select_send::send_data(List<Item> &items) { - List_iterator<Item> li(items); + List_iterator_fast<Item> li(items); String *packet= &thd->packet; DBUG_ENTER("send_data"); +#ifdef HAVE_INNOBASE_DB + /* We may be passing the control from mysqld to the client: release the + InnoDB adaptive hash S-latch to avoid thread deadlocks if it was reserved + by thd */ + if (thd->transaction.all.innobase_tid) + ha_release_temporary_latches(thd); +#endif + if (thd->offset_limit) { // using limit offset,count thd->offset_limit--; @@ -304,7 +480,7 @@ bool select_send::send_data(List<Item> &items) Item *item; while ((item=li++)) { - if (item->send(packet)) + if (item->send(thd, packet)) { packet->free(); // Free used my_error(ER_OUT_OF_RESOURCES,MYF(0)); @@ -316,14 +492,16 @@ bool select_send::send_data(List<Item> &items) DBUG_RETURN(error); } - -void select_send::send_error(uint errcode,const char *err) -{ - ::send_error(&thd->net,errcode,err); -} - bool select_send::send_eof() { +#ifdef HAVE_INNOBASE_DB + /* We may be passing the control from mysqld to the client: release the + InnoDB adaptive hash S-latch to avoid thread deadlocks if it was reserved + by thd */ + if (thd->transaction.all.innobase_tid) + ha_release_temporary_latches(thd); +#endif + /* Unlock tables before sending packet to gain some speed */ if (thd->lock) { @@ -384,7 +562,7 @@ select_export::prepare(List<Item> &list) } /* Check if there is any blobs in data */ { - List_iterator<Item> li(list); + List_iterator_fast<Item> li(list); Item *item; while ((item=li++)) { @@ -431,7 +609,7 @@ bool select_export::send_data(List<Item> &items) Item *item; char *buff_ptr=buff; uint used_length=0,items_left=items.elements; - List_iterator<Item> li(items); + List_iterator_fast<Item> li(items); if (my_b_write(&cache,(byte*) exchange->line_start->ptr(), exchange->line_start->length())) @@ -519,7 +697,7 @@ bool select_export::send_data(List<Item> &items) bfill(space,sizeof(space),' '); } uint length=item->max_length-used_length; - for ( ; length > sizeof(space) ; length-=sizeof(space)) + for (; length > sizeof(space) ; length-=sizeof(space)) { if (my_b_write(&cache,(byte*) space,sizeof(space))) goto err; @@ -624,7 +802,7 @@ select_dump::prepare(List<Item> &list __attribute__((unused))) bool select_dump::send_data(List<Item> &items) { - List_iterator<Item> li(items); + List_iterator_fast<Item> li(items); char buff[MAX_FIELD_WIDTH]; String tmp(buff,sizeof(buff)),*res; tmp.length(0); diff --git a/sql/sql_class.h b/sql/sql_class.h index d9497907926..a8eaf1b200d 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -21,12 +21,18 @@ #pragma interface /* gcc class implementation */ #endif +// TODO: create log.h and move all the log header stuff there + class Query_log_event; class Load_log_event; +class Slave_log_event; - +enum enum_enable_or_disable { LEAVE_AS_IS, ENABLE, DISABLE }; +enum enum_ha_read_modes { RFIRST, RNEXT, RPREV, RLAST, RKEY }; enum enum_duplicates { DUP_ERROR, DUP_REPLACE, DUP_IGNORE }; enum enum_log_type { LOG_CLOSED, LOG_NORMAL, LOG_NEW, LOG_BIN }; +enum enum_delay_key_write { DELAY_KEY_WRITE_NONE, DELAY_KEY_WRITE_ON, + DELAY_KEY_WRITE_ALL }; // log info errors #define LOG_INFO_EOF -1 @@ -38,10 +44,12 @@ enum enum_log_type { LOG_CLOSED, LOG_NORMAL, LOG_NEW, LOG_BIN }; #define LOG_INFO_FATAL -7 #define LOG_INFO_IN_USE -8 +struct st_relay_log_info; + typedef struct st_log_info { char log_file_name[FN_REFLEN]; - my_off_t index_file_offset; + my_off_t index_file_offset, index_file_start_offset; my_off_t pos; bool fatal; // if the purge happens to give us a negative offset pthread_mutex_t lock; @@ -49,58 +57,106 @@ typedef struct st_log_info ~st_log_info() { pthread_mutex_destroy(&lock);} } LOG_INFO; +class Log_event; class MYSQL_LOG { private: pthread_mutex_t LOCK_log, LOCK_index; + pthread_cond_t update_cond; + ulonglong bytes_written; time_t last_time,query_start; IO_CACHE log_file; - File index_file; + IO_CACHE index_file; char *name; - volatile enum_log_type log_type; char time_buff[20],db[NAME_LEN+1]; char log_file_name[FN_REFLEN],index_file_name[FN_REFLEN]; + // current file sequence number for load data infile binary logging + uint file_id; + uint open_count; // For replication + /* + For binlog - if log name can never change we should not try to rotate it + or write any rotation events. The user should use FLUSH MASTER instead + of FLUSH LOGS for purging. + */ + volatile enum_log_type log_type; + enum cache_type io_cache_type; bool write_error,inited; - bool no_rotate; // for binlog - if log name can never change - // we should not try to rotate it or write any rotation events - // the user should use FLUSH MASTER instead of FLUSH LOGS for - // purging + bool no_rotate; + bool need_start_event; + bool no_auto_events; // for relay binlog + friend class Log_event; public: MYSQL_LOG(); ~MYSQL_LOG(); - pthread_mutex_t* get_log_lock() { return &LOCK_log; } - void set_index_file_name(const char* index_file_name = 0); - void init(enum_log_type log_type_arg); - void open(const char *log_name,enum_log_type log_type, - const char *new_name=0); - void new_file(bool inside_mutex = 0); - bool open_index(int options); - void close_index(); - bool write(THD *thd, enum enum_server_command command,const char *format,...); + void reset_bytes_written() + { + bytes_written = 0; + } + void harvest_bytes_written(ulonglong* counter) + { +#ifndef DBUG_OFF + char buf1[22],buf2[22]; +#endif + DBUG_ENTER("harvest_bytes_written"); + (*counter)+=bytes_written; + DBUG_PRINT("info",("counter: %s bytes_written: %s", llstr(*counter,buf1), + llstr(bytes_written,buf2))); + bytes_written=0; + DBUG_VOID_RETURN; + } + void signal_update() { pthread_cond_broadcast(&update_cond);} + void wait_for_update(THD* thd); + void set_need_start_event() { need_start_event = 1; } + void init(enum_log_type log_type_arg, + enum cache_type io_cache_type_arg = WRITE_CACHE, + bool no_auto_events_arg = 0); + void cleanup(); + bool open(const char *log_name,enum_log_type log_type, + const char *new_name, const char *index_file_name_arg, + enum cache_type io_cache_type_arg, + bool no_auto_events_arg); + void new_file(bool need_lock= 1); + bool write(THD *thd, enum enum_server_command command, + const char *format,...); bool write(THD *thd, const char *query, uint query_length, time_t query_start=0); - bool write(Query_log_event* event_info); // binary log write - bool write(Load_log_event* event_info); + bool write(Log_event* event_info); // binary log write bool write(THD *thd, IO_CACHE *cache); + + /* + v stands for vector + invoked as appendv(buf1,len1,buf2,len2,...,bufn,lenn,0) + */ + bool appendv(const char* buf,uint len,...); + bool append(Log_event* ev); + int generate_new_name(char *new_name,const char *old_name); void make_log_name(char* buf, const char* log_ident); bool is_active(const char* log_file_name); int purge_logs(THD* thd, const char* to_log); - void close(bool exiting = 0); // if we are exiting, we also want to close the - // index file + int purge_first_log(struct st_relay_log_info* rli); + bool reset_logs(THD* thd); + // if we are exiting, we also want to close the index file + void close(bool exiting = 0); // iterating through the log index file - int find_first_log(LOG_INFO* linfo, const char* log_name); - int find_next_log(LOG_INFO* linfo); + int find_log_pos(LOG_INFO* linfo, const char* log_name, + bool need_mutex); + int find_next_log(LOG_INFO* linfo, bool need_mutex); int get_current_log(LOG_INFO* linfo); + uint next_file_id(); inline bool is_open() { return log_type != LOG_CLOSED; } - char* get_index_fname() { return index_file_name;} - char* get_log_fname() { return log_file_name; } - void lock_index() { pthread_mutex_lock(&LOCK_index);} - void unlock_index() { pthread_mutex_unlock(&LOCK_index);} - File get_index_file() { return index_file;} + inline char* get_index_fname() { return index_file_name;} + inline char* get_log_fname() { return log_file_name; } + inline pthread_mutex_t* get_log_lock() { return &LOCK_log; } + inline IO_CACHE* get_log_file() { return &log_file; } + + inline void lock_index() { pthread_mutex_lock(&LOCK_index);} + inline void unlock_index() { pthread_mutex_unlock(&LOCK_index);} + inline IO_CACHE *get_index_file() { return &index_file;} + inline uint32 get_open_count() { return open_count; } }; /* character conversion tables */ @@ -114,23 +170,25 @@ class CONVERT void convert_array(const uchar *mapping,uchar *buff,uint length); public: const char *name; - CONVERT(const char *name_par,uchar *from_par,uchar *to_par) - :from_map(from_par),to_map(to_par),name(name_par) {} + uint numb; + CONVERT(const char *name_par,uchar *from_par,uchar *to_par, uint number) + :from_map(from_par),to_map(to_par),name(name_par),numb(number) {} friend CONVERT *get_convert_set(const char *name_ptr); inline void convert(char *a,uint length) { convert_array(from_map, (uchar*) a,length); } bool store(String *, const char *,uint); + inline uint number() { return numb; } }; typedef struct st_copy_info { ha_rows records; ha_rows deleted; ha_rows copied; - ha_rows error; + ha_rows error_count; enum enum_duplicates handle_duplicates; - int escape_char; + int escape_char, last_errno; } COPY_INFO; @@ -165,11 +223,13 @@ class Key :public Sql_alloc { public: enum Keytype { PRIMARY, UNIQUE, MULTIPLE, FULLTEXT }; enum Keytype type; + enum ha_key_alg algorithm; List<key_part_spec> columns; const char *Name; Key(enum Keytype type_par,const char *name_arg,List<key_part_spec> &cols) - :type(type_par), columns(cols),Name(name_arg) {} + :type(type_par), algorithm(HA_KEY_ALG_UNDEF), columns(cols), Name(name_arg) + {} ~Key() {} const char *name() { return Name; } }; @@ -214,114 +274,216 @@ public: }; -/**************************************************************************** -** every connection is handle by a thread with a THD -****************************************************************************/ - class delayed_insert; -class THD :public ilink { +#define THD_SENTRY_MAGIC 0xfeedd1ff +#define THD_SENTRY_GONE 0xdeadbeef + +#define THD_CHECK_SENTRY(thd) DBUG_ASSERT(thd->dbug_sentry == THD_SENTRY_MAGIC) + +struct system_variables +{ + ulonglong myisam_max_extra_sort_file_size; + ulonglong myisam_max_sort_file_size; + ha_rows select_limit; + ha_rows max_join_size; + ulong bulk_insert_buff_size; + ulong join_buff_size; + ulong long_query_time; + ulong max_allowed_packet; + ulong max_heap_table_size; + ulong max_sort_length; + ulong max_tmp_tables; + ulong myisam_sort_buff_size; + ulong net_buffer_length; + ulong net_interactive_timeout; + ulong net_read_timeout; + ulong net_wait_timeout; + ulong net_write_timeout; + ulong net_retry_count; + ulong query_cache_type; + ulong read_buff_size; + ulong read_rnd_buff_size; + ulong sortbuff_size; + ulong tmp_table_size; + ulong tx_isolation; + ulong table_type; + + my_bool log_warnings; + my_bool low_priority_updates; + + CONVERT *convert_set; +}; + + +/* + For each client connection we create a separate thread with THD serving as + a thread/connection descriptor +*/ + +class THD :public ilink +{ public: - NET net; - LEX lex; - MEM_ROOT mem_root; - HASH user_vars; - String packet; /* Room for 1 row */ - struct sockaddr_in remote; - struct rand_struct rand; - char *query,*thread_stack; + NET net; // client connection descriptor + LEX lex; // parse tree descriptor + MEM_ROOT mem_root; // 1 command-life memory pool + HASH user_vars; // hash for user variables + String packet; // dynamic buffer for network I/O + struct sockaddr_in remote; // client socket address + struct rand_struct rand; // used for authentication + struct system_variables variables; // Changeable local variables + pthread_mutex_t LOCK_delete; // Locked before thd is deleted + + char *query; // Points to the current query, + /* + A pointer to the stack frame of handle_one_connection(), + which is called first in the thread for handling a client + */ + char *thread_stack; + + /* + host - host of the client + user - user of the client, set to NULL until the user has been read from + the connection + priv_user - not sure why we have it, but it is set to "boot" when we run + with --bootstrap + db - currently selected database + ip - client IP + */ char *host,*user,*priv_user,*db,*ip; - const char *proc_info; - uint client_capabilities,sql_mode,max_packet_length; - uint master_access,db_access; - TABLE *open_tables,*temporary_tables; + /* remote (peer) port */ + uint16 peer_port; + /* Points to info-string that will show in SHOW PROCESSLIST */ + const char *proc_info; + /* points to host if host is available, otherwise points to ip */ + const char *host_or_ip; + + uint client_capabilities; /* What the client supports */ + /* Determines if which non-standard SQL behaviour should be enabled */ + uint sql_mode; + ulong max_client_packet_length; + ulong master_access; /* Global privileges from mysql.user */ + ulong db_access; /* Privileges for current db */ + + + /* + open_tables - list of regular tables in use by this thread + temporary_tables - list of temp tables in use by this thread + handler_tables - list of tables that were opened with HANDLER OPEN + and are still in use by this thread + */ + TABLE *open_tables,*temporary_tables, *handler_tables; + // TODO: document the variables below MYSQL_LOCK *lock,*locked_tables; ULL *ull; +#ifndef DBUG_OFF + uint dbug_sentry; // watch out for memory corruption +#endif struct st_my_thread_var *mysys_var; enum enum_server_command command; - uint32 server_id; + uint32 server_id; + uint32 file_id; // for LOAD DATA INFILE const char *where; - char* last_nx_table; // last non-existent table, we need this for replication - char* last_nx_db; // database of the last nx table - time_t start_time,time_after_lock,user_time; - time_t connect_time,thr_create_time; // track down slow pthread_create + time_t start_time,time_after_lock,user_time; + time_t connect_time,thr_create_time; // track down slow pthread_create thr_lock_type update_lock_default; delayed_insert *di; struct st_transactions { IO_CACHE trans_log; - THD_TRANS all; /* Trans since BEGIN WORK */ - THD_TRANS stmt; /* Trans for current statement */ + THD_TRANS all; // Trans since BEGIN WORK + THD_TRANS stmt; // Trans for current statement uint bdb_lock_count; + + /* + Tables changed in transaction (that must be invalidated in query cache). + List contain only transactional tables, that not invalidated in query + cache (instead of full list of changed in transaction tables). + */ + CHANGED_TABLE_LIST* changed_tables; + MEM_ROOT mem_root; // Transaction-life memory allocation pool + void cleanup() + { + changed_tables = 0; + free_root(&mem_root,MYF(MY_KEEP_PREALLOC)); + } } transaction; -#ifdef HAVE_GEMINI_DB - struct st_gemini gemini; -#endif - Item *free_list; - CONVERT *convert_set; + Item *free_list, *handler_items; Field *dupp_field; #ifndef __WIN__ sigset_t signals,block_signals; #endif #ifdef SIGNAL_WITH_VIO_CLOSE Vio* active_vio; - pthread_mutex_t active_vio_lock; #endif - ulonglong next_insert_id,last_insert_id,current_insert_id; - ha_rows select_limit,offset_limit,default_select_limit,cuted_fields, - max_join_size, sent_row_count, examined_row_count; - table_map used_tables; - ulong query_id,version, inactive_timeout,options,thread_id; - ulong gemini_spin_retries; - long dbug_thread_id; + ulonglong next_insert_id,last_insert_id,current_insert_id, + limit_found_rows; + ha_rows select_limit, offset_limit, cuted_fields, + sent_row_count, examined_row_count; + table_map used_tables; + USER_CONN *user_connect; + ulong query_id,version, options,thread_id, col_access; + ulong rand_saved_seed1, rand_saved_seed2; + long dbug_thread_id; pthread_t real_id; - uint current_tablenr,tmp_table,cond_count,col_access,query_length; - uint server_status,open_options; - enum_tx_isolation tx_isolation, session_tx_isolation; + uint current_tablenr,tmp_table,cond_count; + uint server_status,open_options; + uint32 query_length; + uint32 db_length; + /* variables.transaction_isolation is reset to this after each commit */ + enum_tx_isolation session_tx_isolation; char scramble[9]; + uint8 query_cache_type; // type of query cache processing bool slave_thread; bool set_query_id,locked,count_cuted_fields,some_tables_deleted; bool no_errors, allow_sum_func, password, fatal_error; - bool query_start_used,last_insert_id_used,insert_id_used; + bool query_start_used,last_insert_id_used,insert_id_used,rand_used; bool system_thread,in_lock_tables,global_read_lock; bool query_error, bootstrap, cleanup_done; + bool safe_to_cache_query; bool volatile killed; + /* + If we do a purge of binary logs, log index info of the threads + that are currently reading it needs to be adjusted. To do that + each thread that is using LOG_INFO needs to adjust the pointer to it + */ LOG_INFO* current_linfo; - // if we do a purge of binary logs, log index info of the threads - // that are currently reading it needs to be adjusted. To do that - // each thread that is using LOG_INFO needs to adjust the pointer to it - - ulong slave_proxy_id; // in slave thread we need to know in behalf of which - // thread the query is being run to replicate temp tables properly + /* + In slave thread we need to know in behalf of which + thread the query is being run to replicate temp tables properly + */ + ulong slave_proxy_id; + NET* slave_net; // network connection from slave -> m. + my_off_t log_pos; + + /* Used by the sys_var class to store temporary values */ + union + { + my_bool my_bool_value; + long long_value; + } sys_var_tmp; THD(); ~THD(); + void init(void); + void change_user(void); void cleanup(void); bool store_globals(); #ifdef SIGNAL_WITH_VIO_CLOSE inline void set_active_vio(Vio* vio) { - pthread_mutex_lock(&active_vio_lock); + pthread_mutex_lock(&LOCK_delete); active_vio = vio; - pthread_mutex_unlock(&active_vio_lock); + pthread_mutex_unlock(&LOCK_delete); } inline void clear_active_vio() { - pthread_mutex_lock(&active_vio_lock); + pthread_mutex_lock(&LOCK_delete); active_vio = 0; - pthread_mutex_unlock(&active_vio_lock); - } - inline void close_active_vio() - { - pthread_mutex_lock(&active_vio_lock); - if(active_vio) - { - vio_close(active_vio); - active_vio = 0; - } - pthread_mutex_unlock(&active_vio_lock); + pthread_mutex_unlock(&LOCK_delete); } + void close_active_vio(); #endif - void prepare_to_die(); + void awake(bool prepare_to_die); inline const char* enter_cond(pthread_cond_t *cond, pthread_mutex_t* mutex, const char* msg) { @@ -355,16 +517,18 @@ public: } return last_insert_id; } + inline ulonglong found_rows(void) + { + return limit_found_rows; + } inline bool active_transaction() { #ifdef USING_TRANSACTIONS return (transaction.all.bdb_tid != 0 || - transaction.all.innodb_active_trans != 0 || - transaction.all.gemini_tid != 0); + transaction.all.innodb_active_trans != 0); #else return 0; #endif - } inline gptr alloc(unsigned int size) { return alloc_root(&mem_root,size); } inline gptr calloc(unsigned int size) @@ -376,11 +540,30 @@ public: } inline char *strdup(const char *str) { return strdup_root(&mem_root,str); } - inline char *memdup(const char *str, unsigned int size) + inline char *strmake(const char *str, uint size) + { return strmake_root(&mem_root,str,size); } + inline char *memdup(const char *str, uint size) { return memdup_root(&mem_root,str,size); } + inline char *memdup_w_gap(const char *str, uint size, uint gap) + { + gptr ptr; + if ((ptr=alloc_root(&mem_root,size+gap))) + memcpy(ptr,str,size); + return ptr; + } + inline gptr trans_alloc(unsigned int size) + { + return alloc_root(&transaction.mem_root,size); + } + void add_changed_table(TABLE *table); + void add_changed_table(const char *key, long key_length); + CHANGED_TABLE_LIST * changed_table_dup(const char *key, long key_length); }; - +/* + Used to hold information about file and file structure in exchainge + via non-DB file (...INTO OUTFILE..., ...LOAD DATA...) +*/ class sql_exchange :public Sql_alloc { public: @@ -399,6 +582,10 @@ public: ** This is used to get result from a select */ +class JOIN; + +void send_error(NET *net,uint sql_errno=0, const char *err=0); + class select_result :public Sql_alloc { protected: THD *thd; @@ -408,7 +595,11 @@ public: virtual int prepare(List<Item> &list) { return 0; } virtual bool send_fields(List<Item> &list,uint flag)=0; virtual bool send_data(List<Item> &items)=0; - virtual void send_error(uint errcode,const char *err)=0; + virtual bool initialize_tables (JOIN *join=0) { return 0; } + virtual void send_error(uint errcode,const char *err) + { + ::send_error(&thd->net,errcode,err); + } virtual bool send_eof()=0; virtual void abort() {} }; @@ -419,7 +610,6 @@ public: select_send() {} bool send_fields(List<Item> &list,uint flag); bool send_data(List<Item> &items); - void send_error(uint errcode,const char *err); bool send_eof(); }; @@ -443,6 +633,7 @@ public: bool send_eof(); }; + class select_dump :public select_result { sql_exchange *exchange; File file; @@ -463,29 +654,28 @@ public: class select_insert :public select_result { - protected: + public: TABLE *table; List<Item> *fields; - uint save_time_stamp; ulonglong last_insert_id; COPY_INFO info; -public: select_insert(TABLE *table_par,List<Item> *fields_par,enum_duplicates duplic) - :table(table_par),fields(fields_par), save_time_stamp(0),last_insert_id(0) - { - bzero((char*) &info,sizeof(info)); - info.handle_duplicates=duplic; - } + :table(table_par),fields(fields_par), last_insert_id(0) + { + bzero((char*) &info,sizeof(info)); + info.handle_duplicates=duplic; + } ~select_insert(); int prepare(List<Item> &list); - bool send_fields(List<Item> &list, - uint flag) { return 0; } + bool send_fields(List<Item> &list, uint flag) + { return 0; } bool send_data(List<Item> &items); void send_error(uint errcode,const char *err); bool send_eof(); }; + class select_create: public select_insert { ORDER *group; const char *db; @@ -512,6 +702,23 @@ public: void abort(); }; +class select_union :public select_result { + public: + TABLE *table; + COPY_INFO info; + TMP_TABLE_PARAM *tmp_table_param; + bool not_describe; + + select_union(TABLE *table_par); + ~select_union(); + int prepare(List<Item> &list); + bool send_fields(List<Item> &list, uint flag) + { return 0; } + bool send_data(List<Item> &items); + bool send_eof(); + bool flush(); +}; + /* Structs used when sorting */ typedef struct st_sort_field { @@ -561,3 +768,80 @@ class user_var_entry Item_result type; }; +/* Class for unique (removing of duplicates) */ + +class Unique :public Sql_alloc +{ + DYNAMIC_ARRAY file_ptrs; + ulong max_elements, max_in_memory_size; + IO_CACHE file; + TREE tree; + byte *record_pointers; + bool flush(); + +public: + ulong elements; + Unique(qsort_cmp2 comp_func, void * comp_func_fixed_arg, + uint size, ulong max_in_memory_size_arg); + ~Unique(); + inline bool unique_add(gptr ptr) + { + if (tree.elements_in_tree > max_elements && flush()) + return 1; + return !tree_insert(&tree,ptr,0); + } + + bool get(TABLE *table); + + friend int unique_write_to_file(gptr key, element_count count, Unique *unique); + friend int unique_write_to_ptrs(gptr key, element_count count, Unique *unique); +}; + + class multi_delete : public select_result { + TABLE_LIST *delete_tables, *table_being_deleted; + Unique **tempfiles; + THD *thd; + ha_rows deleted; + uint num_of_tables; + int error; + bool do_delete, transactional_tables, log_delayed, normal_tables; + public: + multi_delete(THD *thd, TABLE_LIST *dt, uint num_of_tables); + ~multi_delete(); + int prepare(List<Item> &list); + bool send_fields(List<Item> &list, + uint flag) { return 0; } + bool send_data(List<Item> &items); + bool initialize_tables (JOIN *join); + void send_error(uint errcode,const char *err); + int do_deletes (bool from_send_error); + bool send_eof(); + }; + +class multi_update : public select_result +{ + TABLE_LIST *all_tables, *update_tables, *table_being_updated; + THD *thd; + TABLE **tmp_tables, *main_table; + TMP_TABLE_PARAM *tmp_table_param; + ha_rows updated, found; + List <Item> *fields, *values; + List <Item> **fields_for_table, **values_for_table; + uint table_count; + Copy_field *copy_field; + enum enum_duplicates handle_duplicates; + bool do_update, trans_safe, transactional_tables, log_delayed; + +public: + multi_update(THD *thd_arg, TABLE_LIST *ut, List<Item> *fields, + List<Item> *values, enum_duplicates handle_duplicates); + ~multi_update(); + int prepare(List<Item> &list); + bool send_fields(List<Item> &list, uint flag) { return 0; } + bool send_data(List<Item> &items); + bool initialize_tables (JOIN *join); + void send_error(uint errcode,const char *err); + int do_updates (bool from_send_error); + bool send_eof(); +}; + diff --git a/sql/sql_crypt.cc b/sql/sql_crypt.cc index 371d63f8c73..f2e4a8934be 100644 --- a/sql/sql_crypt.cc +++ b/sql/sql_crypt.cc @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ diff --git a/sql/sql_crypt.h b/sql/sql_crypt.h index b3a9d54133f..1b27f0a4d27 100644 --- a/sql/sql_crypt.h +++ b/sql/sql_crypt.h @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 60eeb3777c4..900c87d83a5 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -25,45 +25,28 @@ #include <direct.h> #endif -static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *path, +static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, + const char *db, const char *path, uint level); /* db-name is already validated when we come here */ -void mysql_create_db(THD *thd, char *db, uint create_options) +int mysql_create_db(THD *thd, char *db, uint create_options, bool silent) { char path[FN_REFLEN+16]; MY_DIR *dirp; long result=1; + int error = 0; DBUG_ENTER("mysql_create_db"); - + VOID(pthread_mutex_lock(&LOCK_mysql_create_db)); - VOID(pthread_mutex_lock(&LOCK_open)); // do not create database if another thread is holding read lock - if (global_read_lock) + if (wait_if_global_read_lock(thd,0)) { - if (thd->global_read_lock) - { - net_printf(&thd->net, ER_CREATE_DB_WITH_READ_LOCK); - VOID(pthread_mutex_unlock(&LOCK_open)); - goto exit; - } - while (global_read_lock && ! thd->killed) - { - (void) pthread_cond_wait(&COND_refresh,&LOCK_open); - } - - if (thd->killed) - { - net_printf(&thd->net, ER_SERVER_SHUTDOWN); - VOID(pthread_mutex_unlock(&LOCK_open)); - goto exit; - } - + error= -1; + goto exit2; } - - VOID(pthread_mutex_unlock(&LOCK_open)); /* Check directory */ (void)sprintf(path,"%s/%s", mysql_data_home, db); @@ -73,7 +56,8 @@ void mysql_create_db(THD *thd, char *db, uint create_options) my_dirend(dirp); if (!(create_options & HA_LEX_CREATE_IF_NOT_EXISTS)) { - net_printf(&thd->net,ER_DB_CREATE_EXISTS,db); + my_error(ER_DB_CREATE_EXISTS,MYF(0),db); + error = -1; goto exit; } result = 0; @@ -83,142 +67,154 @@ void mysql_create_db(THD *thd, char *db, uint create_options) strend(path)[-1]=0; // Remove last '/' from path if (my_mkdir(path,0777,MYF(0)) < 0) { - net_printf(&thd->net,ER_CANT_CREATE_DB,db,my_errno); + my_error(ER_CANT_CREATE_DB,MYF(0),db,my_errno); + error = -1; goto exit; } } - if (!thd->query) - { - thd->query_length = (uint) (strxmov(path,"create database ", db, NullS)- - path); - thd->query = path; - } + + if (!silent) { - mysql_update_log.write(thd,thd->query, thd->query_length); - if (mysql_bin_log.is_open()) + if (!thd->query) { - Query_log_event qinfo(thd, thd->query); - mysql_bin_log.write(&qinfo); + /* The client used the old obsolete mysql_create_db() call */ + thd->query_length= (uint) (strxmov(path,"create database `", db, "`", + NullS) - path); + thd->query= path; } + { + mysql_update_log.write(thd,thd->query, thd->query_length); + if (mysql_bin_log.is_open()) + { + Query_log_event qinfo(thd, thd->query, thd->query_length, 0); + mysql_bin_log.write(&qinfo); + } + } + if (thd->query == path) + { + VOID(pthread_mutex_lock(&LOCK_thread_count)); + thd->query= 0; + VOID(pthread_mutex_unlock(&LOCK_thread_count)); + } + send_ok(&thd->net, result); } - if (thd->query == path) - { - VOID(pthread_mutex_lock(&LOCK_thread_count)); - thd->query = 0; // just in case - VOID(pthread_mutex_unlock(&LOCK_thread_count)); - } - send_ok(&thd->net, result); exit: + start_waiting_global_read_lock(thd); +exit2: VOID(pthread_mutex_unlock(&LOCK_mysql_create_db)); - DBUG_VOID_RETURN; + DBUG_RETURN(error); } -const char *del_exts[]= -{".frm",".ISM",".ISD",".ISM",".HSH",".DAT",".MRG",".MYI",".MYD", ".db", ".BAK", NullS}; +const char *del_exts[]= {".frm", ".BAK", ".TMD", NullS}; static TYPELIB deletable_extentions= {array_elements(del_exts)-1,"del_exts", del_exts}; +const char *known_exts[]= +{".ISM",".ISD",".ISM",".MRG",".MYI",".MYD",".db",NullS}; +static TYPELIB known_extentions= +{array_elements(known_exts)-1,"known_exts", known_exts}; -/* db-name is already validated when we come here */ +/* + Drop all tables in a database. + + db-name is already validated when we come here + If thd == 0, do not write any messages; This is useful in replication + when we want to remove a stale database before replacing it with the new one +*/ -void mysql_rm_db(THD *thd,char *db,bool if_exists) + +int mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) { long deleted=0; + int error = 0; char path[FN_REFLEN+16]; MY_DIR *dirp; DBUG_ENTER("mysql_rm_db"); VOID(pthread_mutex_lock(&LOCK_mysql_create_db)); - VOID(pthread_mutex_lock(&LOCK_open)); // do not drop database if another thread is holding read lock - if (global_read_lock) + if (wait_if_global_read_lock(thd,0)) { - if (thd->global_read_lock) - { - net_printf(&thd->net, ER_DROP_DB_WITH_READ_LOCK); - goto exit; - } - while (global_read_lock && ! thd->killed) - { - (void) pthread_cond_wait(&COND_refresh,&LOCK_open); - } - - if (thd->killed) - { - net_printf(&thd->net, ER_SERVER_SHUTDOWN); - goto exit; - } + error= -1; + goto exit2; } (void) sprintf(path,"%s/%s",mysql_data_home,db); unpack_dirname(path,path); // Convert if not unix /* See if the directory exists */ - if (!(dirp = my_dir(path,MYF(MY_WME | MY_DONT_SORT)))) + if (!(dirp = my_dir(path,MYF(MY_DONT_SORT)))) { if (!if_exists) - net_printf(&thd->net,ER_DB_DROP_EXISTS,db); - else + { + error= -1; + my_error(ER_DB_DROP_EXISTS,MYF(0),db); + } + else if (!silent) send_ok(&thd->net,0); goto exit; } + pthread_mutex_lock(&LOCK_open); remove_db_from_cache(db); + pthread_mutex_unlock(&LOCK_open); - if ((deleted=mysql_rm_known_files(thd, dirp, path,0)) >= 0) + error = -1; + if ((deleted=mysql_rm_known_files(thd, dirp, db, path,0)) >= 0 && thd) { - /* - If there are running queries on the tables, MySQL needs to get - access to LOCK_open to end them. InnoDB on the other hand waits - for the queries to end before dropping the database. That is why we - must do the dropping with LOCK_open released. - */ - VOID(pthread_mutex_unlock(&LOCK_open)); ha_drop_database(path); - VOID(pthread_mutex_lock(&LOCK_open)); - - if (!thd->query) - { - thd->query_length = (uint) (strxmov(path,"drop database ", db, NullS)- - path); - thd->query = path; - } - mysql_update_log.write(thd, thd->query, thd->query_length); - if (mysql_bin_log.is_open()) + query_cache_invalidate1(db); + if (!silent) { - Query_log_event qinfo(thd, thd->query); - mysql_bin_log.write(&qinfo); - } - if (thd->query == path) - { - VOID(pthread_mutex_lock(&LOCK_thread_count)); - thd->query = 0; // just in case - VOID(pthread_mutex_unlock(&LOCK_thread_count)); + if (!thd->query) + { + thd->query_length= (uint) (strxmov(path,"drop database `", db, "`", + NullS)- + path); + thd->query= path; + } + mysql_update_log.write(thd, thd->query, thd->query_length); + if (mysql_bin_log.is_open()) + { + Query_log_event qinfo(thd, thd->query, thd->query_length, 0); + mysql_bin_log.write(&qinfo); + } + if (thd->query == path) + { + VOID(pthread_mutex_lock(&LOCK_thread_count)); + thd->query= 0; + VOID(pthread_mutex_unlock(&LOCK_thread_count)); + } + send_ok(&thd->net,(ulong) deleted); } - send_ok(&thd->net,(ulong) deleted); + error = 0; } exit: - VOID(pthread_mutex_unlock(&LOCK_open)); + start_waiting_global_read_lock(thd); +exit2: VOID(pthread_mutex_unlock(&LOCK_mysql_create_db)); - DBUG_VOID_RETURN; + DBUG_RETURN(error); } /* Removes files with known extensions plus all found subdirectories that are 2 digits (raid directories). + thd MUST be set when calling this function! */ -static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *org_path, - uint level) +static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db, + const char *org_path, uint level) { long deleted=0; ulong found_other_files=0; char filePath[FN_REFLEN]; + TABLE_LIST *tot_list=0, **tot_list_next; DBUG_ENTER("mysql_rm_known_files"); DBUG_PRINT("enter",("path: %s", org_path)); - /* remove all files with known extensions */ + + tot_list_next= &tot_list; for (uint idx=2 ; idx < (uint) dirp->number_off_files && !thd->killed ; @@ -238,7 +234,7 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *org_path, if ((new_dirp = my_dir(newpath,MYF(MY_DONT_SORT)))) { DBUG_PRINT("my",("New subdir found: %s", newpath)); - if ((mysql_rm_known_files(thd,new_dirp,newpath,1)) < 0) + if ((mysql_rm_known_files(thd, new_dirp, NullS, newpath,1)) < 0) { my_dirend(dirp); DBUG_RETURN(-1); @@ -248,27 +244,45 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *org_path, } if (find_type(fn_ext(file->name),&deletable_extentions,1+2) <= 0) { - found_other_files++; + if (find_type(fn_ext(file->name),&known_extentions,1+2) <= 0) + found_other_files++; continue; } strxmov(filePath,org_path,"/",file->name,NullS); - unpack_filename(filePath,filePath); - if (my_delete(filePath,MYF(MY_WME))) + if (db && !my_strcasecmp(fn_ext(file->name), reg_ext)) { - net_printf(&thd->net,ER_DB_DROP_DELETE,filePath,my_error); - my_dirend(dirp); - DBUG_RETURN(-1); + /* Drop the table nicely */ + *fn_ext(file->name)=0; // Remove extension + TABLE_LIST *table_list=(TABLE_LIST*) + thd->calloc(sizeof(*table_list)+ strlen(db)+strlen(file->name)+2); + if (!table_list) + { + my_dirend(dirp); + DBUG_RETURN(-1); + } + table_list->db= (char*) (table_list+1); + strmov(table_list->real_name=strmov(table_list->db,db)+1, + file->name); + /* Link into list */ + (*tot_list_next)= table_list; + tot_list_next= &table_list->next; } - deleted++; - } + else + { + if (my_delete_with_symlink(filePath,MYF(MY_WME))) + { + my_dirend(dirp); + DBUG_RETURN(-1); + } + deleted++; + } + } my_dirend(dirp); - if (thd->killed) - { - send_error(&thd->net,ER_SERVER_SHUTDOWN); + if (thd->killed || + (tot_list && mysql_rm_table_part2_with_lock(thd, tot_list, 1, 1))) DBUG_RETURN(-1); - } /* If the directory is a symbolic link, remove the link first, then @@ -280,7 +294,7 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *org_path, char *path=unpack_filename(tmp_path,org_path); #ifdef HAVE_READLINK int error; - + /* Remove end FN_LIBCHAR as this causes problem on Linux in readlink */ pos=strend(path); if (pos > path && pos[-1] == FN_LIBCHAR) @@ -295,21 +309,21 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *org_path, /* Don't give errors if we can't delete 'RAID' directory */ if (level) DBUG_RETURN(deleted); - send_error(&thd->net); DBUG_RETURN(-1); } /* Delete directory symbolic link pointed at */ path= filePath; } #endif - /* Remove last FN_LIBCHAR to not cause a probelm on OS/2 */ + /* Remove last FN_LIBCHAR to not cause a problem on OS/2 */ pos=strend(path); + if (pos > path && pos[-1] == FN_LIBCHAR) *--pos=0; /* Don't give errors if we can't delete 'RAID' directory */ if (rmdir(path) < 0 && !level) { - net_printf(&thd->net,ER_DB_DROP_RMDIR, path,errno); + my_error(ER_DB_DROP_RMDIR, MYF(0), path, errno); DBUG_RETURN(-1); } } @@ -319,25 +333,25 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *org_path, bool mysql_change_db(THD *thd,const char *name) { - int length; + int length, db_length; char *dbname=my_strdup((char*) name,MYF(MY_WME)); char path[FN_REFLEN]; - uint db_access; + ulong db_access; DBUG_ENTER("mysql_change_db"); - if (!dbname || !(length=strip_sp(dbname))) + if (!dbname || !(db_length=strip_sp(dbname))) { x_free(dbname); /* purecov: inspected */ send_error(&thd->net,ER_NO_DB_ERROR); /* purecov: inspected */ DBUG_RETURN(1); /* purecov: inspected */ } - if ((length > NAME_LEN) || check_db_name(dbname)) + if ((db_length > NAME_LEN) || check_db_name(dbname)) { net_printf(&thd->net,ER_WRONG_DB_NAME, dbname); x_free(dbname); DBUG_RETURN(1); } - DBUG_PRINT("general",("Use database: %s", dbname)); + DBUG_PRINT("info",("Use database: %s", dbname)); if (test_all_bits(thd->master_access,DB_ACLS)) db_access=DB_ACLS; else @@ -348,11 +362,11 @@ bool mysql_change_db(THD *thd,const char *name) { net_printf(&thd->net,ER_DBACCESS_DENIED_ERROR, thd->priv_user, - thd->host ? thd->host : thd->ip ? thd->ip : "unknown", + thd->host_or_ip, dbname); mysql_log.write(thd,COM_INIT_DB,ER(ER_DBACCESS_DENIED_ERROR), thd->priv_user, - thd->host ? thd->host : thd->ip ? thd->ip : "unknown", + thd->host_or_ip, dbname); my_free(dbname,MYF(0)); DBUG_RETURN(1); @@ -371,6 +385,7 @@ bool mysql_change_db(THD *thd,const char *name) send_ok(&thd->net); x_free(thd->db); thd->db=dbname; + thd->db_length=db_length; thd->db_access=db_access; DBUG_RETURN(0); } diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 278e98533c9..1507d49ebd6 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000 MySQL AB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -15,162 +15,69 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -/* Delete of records */ - -#include "mysql_priv.h" -#include "ha_innobase.h" - /* - Optimize delete of all rows by doing a full generate of the table - This will work even if the .ISM and .ISD tables are destroyed -*/ - -int generate_table(THD *thd, TABLE_LIST *table_list, TABLE *locked_table) -{ - char path[FN_REFLEN]; - int error; - TABLE **table_ptr; - DBUG_ENTER("generate_table"); - - thd->proc_info="generate_table"; + Delete of records and truncate of tables. - if (global_read_lock) - { - if(thd->global_read_lock) - { - my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE,MYF(0), - table_list->real_name); - DBUG_RETURN(-1); - } - pthread_mutex_lock(&LOCK_open); - while (global_read_lock && ! thd->killed || - thd->version != refresh_version) - { - (void) pthread_cond_wait(&COND_refresh,&LOCK_open); - } - pthread_mutex_unlock(&LOCK_open); - } + Multi-table deletes were introduced by Monty and Sinisa +*/ - /* If it is a temporary table, close and regenerate it */ - if ((table_ptr=find_temporary_table(thd,table_list->db, - table_list->real_name))) - { - TABLE *table= *table_ptr; - HA_CREATE_INFO create_info; - table->file->info(HA_STATUS_AUTO | HA_STATUS_NO_LOCK); - bzero((char*) &create_info,sizeof(create_info)); - create_info.auto_increment_value= table->file->auto_increment_value; - db_type table_type=table->db_type; - strmov(path,table->path); - *table_ptr= table->next; // Unlink table from list - close_temporary(table,0); - *fn_ext(path)=0; // Remove the .frm extension - ha_create_table(path, &create_info,1); - if ((error= (int) !(open_temporary_table(thd, path, table_list->db, - table_list->real_name, 1)))) - { - (void) rm_temporary_table(table_type, path); - } - } - else - { - (void) sprintf(path,"%s/%s/%s%s",mysql_data_home,table_list->db, - table_list->real_name,reg_ext); - fn_format(path,path,"","",4); - VOID(pthread_mutex_lock(&LOCK_open)); - if (locked_table) - mysql_lock_abort(thd,locked_table); // end threads waiting on lock - // close all copies in use - if (remove_table_from_cache(thd,table_list->db,table_list->real_name)) - { - if (!locked_table) - { - VOID(pthread_mutex_unlock(&LOCK_open)); - DBUG_RETURN(1); // We must get a lock on table - } - } - if (locked_table) - locked_table->file->extra(HA_EXTRA_FORCE_REOPEN); - if (thd->locked_tables) - close_data_tables(thd,table_list->db,table_list->real_name); - else - close_thread_tables(thd,1); - HA_CREATE_INFO create_info; - bzero((char*) &create_info,sizeof(create_info)); - *fn_ext(path)=0; // Remove the .frm extension - error= ha_create_table(path,&create_info,1) ? -1 : 0; - if (thd->locked_tables && reopen_tables(thd,1,0)) - error= -1; - VOID(pthread_mutex_unlock(&LOCK_open)); - } - if (!error) - { - mysql_update_log.write(thd,thd->query,thd->query_length); - if (mysql_bin_log.is_open()) - { - Query_log_event qinfo(thd, thd->query); - mysql_bin_log.write(&qinfo); - } - send_ok(&thd->net); // This should return record count - } - DBUG_RETURN(error ? -1 : 0); -} +#include "mysql_priv.h" +#include "ha_innodb.h" +#include "sql_select.h" -int mysql_delete(THD *thd,TABLE_LIST *table_list,COND *conds,ha_rows limit, - thr_lock_type lock_type, ulong options) +int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ORDER *order, + ha_rows limit, ulong options) { int error; TABLE *table; - SQL_SELECT *select; + SQL_SELECT *select=0; READ_RECORD info; - bool using_limit=limit != HA_POS_ERROR; - bool use_generate_table,using_transactions; + bool using_limit=limit != HA_POS_ERROR; + bool transactional_table, log_delayed, safe_update; + ha_rows deleted; DBUG_ENTER("mysql_delete"); - if (!table_list->db) - table_list->db=thd->db; - if ((thd->options & OPTION_SAFE_UPDATES) && !conds) + if (((safe_update=thd->options & OPTION_SAFE_UPDATES)) && !conds) { send_error(&thd->net,ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE); DBUG_RETURN(1); } - use_generate_table= (!using_limit && !conds && - !(specialflag & - (SPECIAL_NO_NEW_FUNC | SPECIAL_SAFE_MODE)) && - !(thd->options & - (OPTION_NOT_AUTO_COMMIT | OPTION_BEGIN))); -#ifdef HAVE_INNOBASE_DB - /* We need to add code to not generate table based on the table type */ - if (!innodb_skip) - use_generate_table=0; // Innodb can't use re-generate table -#endif - if (use_generate_table && ! thd->open_tables) - { - error=generate_table(thd,table_list,(TABLE*) 0); - if (error <= 0) - DBUG_RETURN(error); // Error or ok - } - if (!(table = open_ltable(thd,table_list, - limit != HA_POS_ERROR ? TL_WRITE_LOW_PRIORITY : - lock_type))) + if (!(table = open_ltable(thd, table_list, table_list->lock_type))) DBUG_RETURN(-1); table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); thd->proc_info="init"; - if (use_generate_table) - DBUG_RETURN(generate_table(thd,table_list,table)); table->map=1; if (setup_conds(thd,table_list,&conds) || setup_ftfuncs(thd)) DBUG_RETURN(-1); + /* Test if the user wants to delete all rows */ + if (!using_limit && (!conds || conds->const_item()) && + !(specialflag & (SPECIAL_NO_NEW_FUNC | SPECIAL_SAFE_MODE)) && !safe_update) + { + deleted= table->file->records; + if (!(error=table->file->delete_all_rows())) + { + error= -1; // ok + goto cleanup; + } + if (error != HA_ERR_WRONG_COMMAND) + { + table->file->print_error(error,MYF(0)); + error=0; + goto cleanup; + } + /* Handler didn't support fast delete; Delete rows one by one */ + } + table->used_keys=table->quick_keys=0; // Can't use 'only index' select=make_select(table,0,0,conds,&error); if (error) DBUG_RETURN(-1); if ((select && select->check_quick(test(thd->options & OPTION_SAFE_UPDATES), - limit)) || + limit)) || !limit) { delete select; @@ -181,19 +88,45 @@ int mysql_delete(THD *thd,TABLE_LIST *table_list,COND *conds,ha_rows limit, /* If running in safe sql mode, don't allow updates without keys */ if (!table->quick_keys) { - thd->lex.options|=QUERY_NO_INDEX_USED; - if ((thd->options & OPTION_SAFE_UPDATES) && limit == HA_POS_ERROR) + thd->lex.select_lex.options|=QUERY_NO_INDEX_USED; + if (safe_update && !using_limit) { delete select; send_error(&thd->net,ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE); DBUG_RETURN(1); } } - (void) table->file->extra(HA_EXTRA_NO_READCHECK); if (options & OPTION_QUICK) (void) table->file->extra(HA_EXTRA_QUICK); - init_read_record(&info,thd,table,select,-1,1); - ulong deleted=0L; + + if (order) + { + uint length; + SORT_FIELD *sortorder; + TABLE_LIST tables; + List<Item> fields; + List<Item> all_fields; + ha_rows examined_rows; + + bzero((char*) &tables,sizeof(tables)); + tables.table = table; + + table->io_cache = (IO_CACHE *) my_malloc(sizeof(IO_CACHE), + MYF(MY_FAE | MY_ZEROFILL)); + if (setup_order(thd, &tables, fields, all_fields, order) || + !(sortorder=make_unireg_sortorder(order, &length)) || + (table->found_records = filesort(table, sortorder, length, + (SQL_SELECT *) 0, 0L, HA_POS_ERROR, + &examined_rows)) + == HA_POS_ERROR) + { + delete select; + DBUG_RETURN(-1); // This will force out message + } + } + + init_read_record(&info,thd,table,select,1,1); + deleted=0L; init_ftfuncs(thd,1); thd->proc_info="updating"; while (!(error=info.read_record(&info)) && !thd->killed) @@ -221,24 +154,40 @@ int mysql_delete(THD *thd,TABLE_LIST *table_list,COND *conds,ha_rows limit, } thd->proc_info="end"; end_read_record(&info); - (void) table->file->extra(HA_EXTRA_READCHECK); + free_io_cache(table); // Will not do any harm if (options & OPTION_QUICK) (void) table->file->extra(HA_EXTRA_NORMAL); - using_transactions=table->file->has_transactions(); - if (deleted && (error <= 0 || !using_transactions)) + +cleanup: + transactional_table= table->file->has_transactions(); + log_delayed= (transactional_table || table->tmp_table); + if (deleted && (error <= 0 || !transactional_table)) { mysql_update_log.write(thd,thd->query, thd->query_length); if (mysql_bin_log.is_open()) { - Query_log_event qinfo(thd, thd->query, using_transactions); - if (mysql_bin_log.write(&qinfo) && using_transactions) + Query_log_event qinfo(thd, thd->query, thd->query_length, + log_delayed); + if (mysql_bin_log.write(&qinfo) && transactional_table) error=1; } - if (!using_transactions) + if (!log_delayed) thd->options|=OPTION_STATUS_NO_TRANS_UPDATE; } - if (using_transactions && ha_autocommit_or_rollback(thd,error >= 0)) - error=1; + if (transactional_table) + { + if (ha_autocommit_or_rollback(thd,error >= 0)) + error=1; + } + + /* + Store table for future invalidation or invalidate it in + the query cache if something changed + */ + if (deleted) + { + query_cache_invalidate3(thd, table_list, 1); + } if (thd->lock) { mysql_unlock_tables(thd, thd->lock); @@ -255,3 +204,399 @@ int mysql_delete(THD *thd,TABLE_LIST *table_list,COND *conds,ha_rows limit, DBUG_RETURN(0); } + +/*************************************************************************** + Delete multiple tables from join +***************************************************************************/ + +#define MEM_STRIP_BUF_SIZE current_thd->variables.sortbuff_size + +extern "C" int refposcmp2(void* arg, const void *a,const void *b) +{ + /* arg is a pointer to file->ref_length */ + return memcmp(a,b, *(int*) arg); +} + +multi_delete::multi_delete(THD *thd_arg, TABLE_LIST *dt, + uint num_of_tables_arg) + : delete_tables(dt), thd(thd_arg), deleted(0), + num_of_tables(num_of_tables_arg), error(0), + do_delete(0), transactional_tables(0), log_delayed(0), normal_tables(0) +{ + tempfiles = (Unique **) sql_calloc(sizeof(Unique *) * (num_of_tables-1)); +} + + +int +multi_delete::prepare(List<Item> &values) +{ + DBUG_ENTER("multi_delete::prepare"); + do_delete= 1; + thd->proc_info="deleting from main table"; + DBUG_RETURN(0); +} + + +bool +multi_delete::initialize_tables(JOIN *join) +{ + TABLE_LIST *walk; + Unique **tempfiles_ptr; + DBUG_ENTER("initialize_tables"); + + if ((thd->options & OPTION_SAFE_UPDATES) && error_if_full_join(join)) + DBUG_RETURN(1); + + table_map tables_to_delete_from=0; + for (walk= delete_tables ; walk ; walk=walk->next) + tables_to_delete_from|= walk->table->map; + + walk= delete_tables; + for (JOIN_TAB *tab=join->join_tab, *end=join->join_tab+join->tables; + tab < end; + tab++) + { + if (tab->table->map & tables_to_delete_from) + { + /* We are going to delete from this table */ + TABLE *tbl=walk->table=tab->table; + walk=walk->next; + /* Don't use KEYREAD optimization on this table */ + tbl->no_keyread=1; + tbl->used_keys= 0; + if (tbl->file->has_transactions()) + log_delayed= transactional_tables= 1; + else if (tbl->tmp_table != NO_TMP_TABLE) + log_delayed= 1; + else + normal_tables= 1; + } + } + walk= delete_tables; + tempfiles_ptr= tempfiles; + for (walk=walk->next ; walk ; walk=walk->next) + { + TABLE *table=walk->table; + *tempfiles_ptr++= new Unique (refposcmp2, + (void *) &table->file->ref_length, + table->file->ref_length, + MEM_STRIP_BUF_SIZE); + } + init_ftfuncs(thd,1); + DBUG_RETURN(thd->fatal_error != 0); +} + + +multi_delete::~multi_delete() +{ + for (table_being_deleted=delete_tables ; + table_being_deleted ; + table_being_deleted=table_being_deleted->next) + { + TABLE *t=table_being_deleted->table; + free_io_cache(t); // Alloced by unique + t->no_keyread=0; + } + + for (uint counter= 0; counter < num_of_tables-1; counter++) + { + if (tempfiles[counter]) + delete tempfiles[counter]; + } +} + + +bool multi_delete::send_data(List<Item> &values) +{ + int secure_counter= -1; + DBUG_ENTER("multi_delete::send_data"); + + for (table_being_deleted=delete_tables ; + table_being_deleted ; + table_being_deleted=table_being_deleted->next, secure_counter++) + { + TABLE *table=table_being_deleted->table; + + /* Check if we are using outer join and we didn't find the row */ + if (table->status & (STATUS_NULL_ROW | STATUS_DELETED)) + continue; + + table->file->position(table->record[0]); + + if (secure_counter < 0) + { + /* If this is the table we are scanning */ + table->status|= STATUS_DELETED; + if (!(error=table->file->delete_row(table->record[0]))) + deleted++; + else if (!table_being_deleted->next) + { + table->file->print_error(error,MYF(0)); + DBUG_RETURN(1); + } + } + else + { + error=tempfiles[secure_counter]->unique_add((char*) table->file->ref); + if (error) + { + error=-1; + DBUG_RETURN(1); + } + } + } + DBUG_RETURN(0); +} + +void multi_delete::send_error(uint errcode,const char *err) +{ + DBUG_ENTER("multi_delete::send_error"); + + /* First send error what ever it is ... */ + ::send_error(&thd->net,errcode,err); + + /* If nothing deleted return */ + if (!deleted) + DBUG_VOID_RETURN; + + /* Something already deleted so we have to invalidate cache */ + query_cache_invalidate3(thd, delete_tables, 1); + + /* Below can happen when thread is killed early ... */ + if (!table_being_deleted) + table_being_deleted=delete_tables; + + /* + If rows from the first table only has been deleted and it is + transactional, just do rollback. + The same if all tables are transactional, regardless of where we are. + In all other cases do attempt deletes ... + */ + if ((table_being_deleted->table->file->has_transactions() && + table_being_deleted == delete_tables) || !normal_tables) + ha_rollback_stmt(thd); + else if (do_delete) + { + VOID(do_deletes(1)); + } + DBUG_VOID_RETURN; +} + + +/* + Do delete from other tables. + Returns values: + 0 ok + 1 error +*/ + +int multi_delete::do_deletes(bool from_send_error) +{ + int local_error= 0, counter= 0; + DBUG_ENTER("do_deletes"); + + if (from_send_error) + { + /* Found out table number for 'table_being_deleted' */ + for (TABLE_LIST *aux=delete_tables; + aux != table_being_deleted; + aux=aux->next) + counter++; + } + else + table_being_deleted = delete_tables; + + do_delete= 0; + for (table_being_deleted=table_being_deleted->next; + table_being_deleted ; + table_being_deleted=table_being_deleted->next, counter++) + { + TABLE *table = table_being_deleted->table; + if (tempfiles[counter]->get(table)) + { + local_error=1; + break; + } + + READ_RECORD info; + init_read_record(&info,thd,table,NULL,0,1); + /* + Ignore any rows not found in reference tables as they may already have + been deleted by foreign key handling + */ + info.ignore_not_found_rows= 1; + while (!(local_error=info.read_record(&info)) && !thd->killed) + { + if ((local_error=table->file->delete_row(table->record[0]))) + { + table->file->print_error(local_error,MYF(0)); + break; + } + deleted++; + } + end_read_record(&info); + if (local_error == -1) // End of file + local_error = 0; + } + DBUG_RETURN(local_error); +} + + +/* + Send ok to the client + + return: 0 sucess + 1 error +*/ + +bool multi_delete::send_eof() +{ + thd->proc_info="deleting from reference tables"; + + /* Does deletes for the last n - 1 tables, returns 0 if ok */ + int local_error= do_deletes(0); // returns 0 if success + + /* reset used flags */ + thd->proc_info="end"; + + /* + Write the SQL statement to the binlog if we deleted + rows and we succeeded, or also in an error case when there + was a non-transaction-safe table involved, since + modifications in it cannot be rolled back. + */ + if (deleted && (error <= 0 || normal_tables)) + { + mysql_update_log.write(thd,thd->query,thd->query_length); + if (mysql_bin_log.is_open()) + { + Query_log_event qinfo(thd, thd->query, thd->query_length, + log_delayed); + if (mysql_bin_log.write(&qinfo) && !normal_tables) + local_error=1; // Log write failed: roll back the SQL statement + } + if (!log_delayed) + thd->options|=OPTION_STATUS_NO_TRANS_UPDATE; + } + /* Commit or rollback the current SQL statement */ + if (transactional_tables) + if (ha_autocommit_or_rollback(thd,local_error > 0)) + local_error=1; + + if (deleted) + query_cache_invalidate3(thd, delete_tables, 1); + + if (local_error) + ::send_error(&thd->net); + else + ::send_ok(&thd->net,deleted); + return 0; +} + + +/*************************************************************************** + TRUNCATE TABLE +****************************************************************************/ + +/* + Optimize delete of all rows by doing a full generate of the table + This will work even if the .ISM and .ISD tables are destroyed + + dont_send_ok should be set if: + - We should always wants to generate the table (even if the table type + normally can't safely do this. + - We don't want an ok to be sent to the end user. + - We don't want to log the truncate command + - If we want to have a name lock on the table on exit without errors. +*/ + +int mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) +{ + HA_CREATE_INFO create_info; + char path[FN_REFLEN]; + TABLE **table_ptr; + int error; + DBUG_ENTER("mysql_truncate"); + + /* If it is a temporary table, close and regenerate it */ + if (!dont_send_ok && (table_ptr=find_temporary_table(thd,table_list->db, + table_list->real_name))) + { + TABLE *table= *table_ptr; + HA_CREATE_INFO create_info; + table->file->info(HA_STATUS_AUTO | HA_STATUS_NO_LOCK); + bzero((char*) &create_info,sizeof(create_info)); + create_info.auto_increment_value= table->file->auto_increment_value; + db_type table_type=table->db_type; + + strmov(path,table->path); + *table_ptr= table->next; // Unlink table from list + close_temporary(table,0); + *fn_ext(path)=0; // Remove the .frm extension + ha_create_table(path, &create_info,1); + // We don't need to call invalidate() because this table is not in cache + if ((error= (int) !(open_temporary_table(thd, path, table_list->db, + table_list->real_name, 1)))) + (void) rm_temporary_table(table_type, path); + /* + Sasha: if we return here we will not have binloged the truncation and + we will not send_ok() to the client. + */ + goto end; + } + + (void) sprintf(path,"%s/%s/%s%s",mysql_data_home,table_list->db, + table_list->real_name,reg_ext); + fn_format(path,path,"","",4); + + if (!dont_send_ok) + { + db_type table_type; + if ((table_type=get_table_type(path)) == DB_TYPE_UNKNOWN) + { + my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, + table_list->real_name); + DBUG_RETURN(-1); + } + if (!ha_supports_generate(table_type)) + { + /* Probably InnoDB table */ + table_list->lock_type= TL_WRITE; + DBUG_RETURN(mysql_delete(thd, table_list, (COND*) 0, (ORDER*) 0, + HA_POS_ERROR, 0)); + } + if (lock_and_wait_for_table_name(thd, table_list)) + DBUG_RETURN(-1); + } + + bzero((char*) &create_info,sizeof(create_info)); + *fn_ext(path)=0; // Remove the .frm extension + error= ha_create_table(path,&create_info,1) ? -1 : 0; + query_cache_invalidate3(thd, table_list, 0); + +end: + if (!dont_send_ok) + { + if (!error) + { + mysql_update_log.write(thd,thd->query,thd->query_length); + if (mysql_bin_log.is_open()) + { + Query_log_event qinfo(thd, thd->query, thd->query_length, + thd->tmp_table); + mysql_bin_log.write(&qinfo); + } + send_ok(&thd->net); // This should return record count + } + VOID(pthread_mutex_lock(&LOCK_open)); + unlock_table_name(thd, table_list); + VOID(pthread_mutex_unlock(&LOCK_open)); + } + else if (error) + { + VOID(pthread_mutex_lock(&LOCK_open)); + unlock_table_name(thd, table_list); + VOID(pthread_mutex_unlock(&LOCK_open)); + } + DBUG_RETURN(error ? -1 : 0); +} diff --git a/sql/sql_do.cc b/sql/sql_do.cc index 57a6f88ed63..70124c2d796 100644 --- a/sql/sql_do.cc +++ b/sql/sql_do.cc @@ -25,7 +25,7 @@ int mysql_do(THD *thd, List<Item> &values) List_iterator<Item> li(values); Item *value; DBUG_ENTER("mysql_do"); - if (setup_fields(thd,0, values, 0, 0)) + if (setup_fields(thd,0, values, 0, 0, 0)) DBUG_RETURN(-1); while ((value = li++)) value->val_int(); diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc new file mode 100644 index 00000000000..a98b6c13a00 --- /dev/null +++ b/sql/sql_handler.cc @@ -0,0 +1,284 @@ +/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + + +/* HANDLER ... commands - direct access to ISAM */ + +#include "mysql_priv.h" +#include "sql_select.h" +#include <assert.h> + +/* TODO: + HANDLER blabla OPEN [ AS foobar ] [ (column-list) ] + + the most natural (easiest, fastest) way to do it is to + compute List<Item> field_list not in mysql_ha_read + but in mysql_ha_open, and then store it in TABLE structure. + + The problem here is that mysql_parse calls free_item to free all the + items allocated at the end of every query. The workaround would to + keep two item lists per THD - normal free_list and handler_items. + The second is to be freeed only on thread end. mysql_ha_open should + then do { handler_items=concat(handler_items, free_list); free_list=0; } + + But !!! do_cammand calls free_root at the end of every query and frees up + all the sql_alloc'ed memory. It's harder to work around... + */ + +#define HANDLER_TABLES_HACK(thd) { \ + TABLE *tmp=thd->open_tables; \ + thd->open_tables=thd->handler_tables; \ + thd->handler_tables=tmp; } + +static TABLE **find_table_ptr_by_name(THD *thd,const char *db, + const char *table_name, bool is_alias); + +int mysql_ha_open(THD *thd, TABLE_LIST *tables) +{ + HANDLER_TABLES_HACK(thd); + int err=open_tables(thd,tables); + HANDLER_TABLES_HACK(thd); + if (err) + return -1; + + // there can be only one table in *tables + if (!(tables->table->file->table_flags() & HA_CAN_SQL_HANDLER)) + { + my_printf_error(ER_ILLEGAL_HA,ER(ER_ILLEGAL_HA),MYF(0), tables->alias); + mysql_ha_close(thd, tables,1); + return -1; + } + + send_ok(&thd->net); + return 0; +} + +int mysql_ha_close(THD *thd, TABLE_LIST *tables, bool dont_send_ok) +{ + TABLE **ptr=find_table_ptr_by_name(thd, tables->db, tables->alias, 1); + + if (*ptr) + { + VOID(pthread_mutex_lock(&LOCK_open)); + close_thread_table(thd, ptr); + VOID(pthread_mutex_unlock(&LOCK_open)); + } + else + { + my_printf_error(ER_UNKNOWN_TABLE,ER(ER_UNKNOWN_TABLE),MYF(0), + tables->alias, "HANDLER"); + return -1; + } + if (!dont_send_ok) + send_ok(&thd->net); + return 0; +} + +int mysql_ha_closeall(THD *thd, TABLE_LIST *tables) +{ + TABLE **ptr=find_table_ptr_by_name(thd, tables->db, tables->real_name, 0); + if (*ptr) + close_thread_table(thd, ptr); + return 0; +} + +static enum enum_ha_read_modes rkey_to_rnext[]= + { RNEXT, RNEXT, RPREV, RNEXT, RPREV, RNEXT, RPREV }; + + +int mysql_ha_read(THD *thd, TABLE_LIST *tables, + enum enum_ha_read_modes mode, char *keyname, List<Item> *key_expr, + enum ha_rkey_function ha_rkey_mode, Item *cond, + ha_rows select_limit,ha_rows offset_limit) +{ + int err, keyno=-1; + TABLE *table=*find_table_ptr_by_name(thd, tables->db, tables->alias, 1); + if (!table) + { + my_printf_error(ER_UNKNOWN_TABLE,ER(ER_UNKNOWN_TABLE),MYF(0), + tables->alias,"HANDLER"); + return -1; + } + tables->table=table; + + if (cond && cond->fix_fields(thd,tables)) + return -1; + + table->file->init_table_handle_for_HANDLER(); // Only InnoDB requires it + + if (keyname) + { + if ((keyno=find_type(keyname, &table->keynames, 1+2)-1)<0) + { + my_printf_error(ER_KEY_DOES_NOT_EXITS,ER(ER_KEY_DOES_NOT_EXITS),MYF(0), + keyname,tables->alias); + return -1; + } + table->file->index_init(keyno); + } + + List<Item> list; + list.push_front(new Item_field(NULL,NULL,"*")); + List_iterator<Item> it(list); + uint num_rows; + it++; + + insert_fields(thd,tables,tables->db,tables->alias,&it); + + select_limit+=offset_limit; + send_fields(thd,list,1); + + HANDLER_TABLES_HACK(thd); + MYSQL_LOCK *lock=mysql_lock_tables(thd,&tables->table,1); + HANDLER_TABLES_HACK(thd); + if (!lock) + goto err0; // mysql_lock_tables() printed error message already + + table->file->init_table_handle_for_HANDLER(); // Only InnoDB requires it + + for (num_rows=0; num_rows < select_limit; ) + { + switch(mode) { + case RFIRST: + if (keyname) + err=table->file->index_first(table->record[0]); + else + { + if (!(err=table->file->rnd_init(1))) + err=table->file->rnd_next(table->record[0]); + } + mode=RNEXT; + break; + case RLAST: + DBUG_ASSERT(keyname != 0); + err=table->file->index_last(table->record[0]); + mode=RPREV; + break; + case RNEXT: + err=keyname ? + table->file->index_next(table->record[0]) : + table->file->rnd_next(table->record[0]); + break; + case RPREV: + DBUG_ASSERT(keyname != 0); + err=table->file->index_prev(table->record[0]); + break; + case RKEY: + { + DBUG_ASSERT(keyname != 0); + KEY *keyinfo=table->key_info+keyno; + KEY_PART_INFO *key_part=keyinfo->key_part; + uint key_len; + byte *key; + if (key_expr->elements > keyinfo->key_parts) + { + my_printf_error(ER_TOO_MANY_KEY_PARTS,ER(ER_TOO_MANY_KEY_PARTS), + MYF(0),keyinfo->key_parts); + goto err; + } + List_iterator_fast<Item> it_ke(*key_expr); + Item *item; + for (key_len=0 ; (item=it_ke++) ; key_part++) + { + item->save_in_field(key_part->field, 1); + key_len+=key_part->store_length; + } + if (!(key= (byte*) thd->calloc(ALIGN_SIZE(key_len)))) + { + send_error(&thd->net,ER_OUTOFMEMORY); + goto err; + } + key_copy(key, table, keyno, key_len); + err=table->file->index_read(table->record[0], + key,key_len,ha_rkey_mode); + mode=rkey_to_rnext[(int)ha_rkey_mode]; + break; + } + default: + send_error(&thd->net,ER_ILLEGAL_HA); + goto err; + } + + if (err) + { + if (err != HA_ERR_KEY_NOT_FOUND && err != HA_ERR_END_OF_FILE) + { + sql_print_error("mysql_ha_read: Got error %d when reading table", + err); + table->file->print_error(err,MYF(0)); + goto err; + } + goto ok; + } + if (cond) + { + err=err; + if (!cond->val_int()) + continue; + } + if (num_rows>=offset_limit) + { + if (!err) + { + String *packet = &thd->packet; + Item *item; + packet->length(0); + it.rewind(); + while ((item=it++)) + { + if (item->send(thd,packet)) + { + packet->free(); // Free used + my_error(ER_OUT_OF_RESOURCES,MYF(0)); + goto err; + } + } + my_net_write(&thd->net, (char*)packet->ptr(), packet->length()); + } + } + num_rows++; + } +ok: + mysql_unlock_tables(thd,lock); + send_eof(&thd->net); + return 0; +err: + mysql_unlock_tables(thd,lock); +err0: + return -1; +} + +static TABLE **find_table_ptr_by_name(THD *thd, const char *db, + const char *table_name, bool is_alias) +{ + int dblen; + TABLE **ptr; + + if (!db || ! *db) + db= thd->db ? thd->db : ""; + dblen=strlen(db)+1; + ptr=&(thd->handler_tables); + + for (TABLE *table=*ptr; table ; table=*ptr) + { + if (!memcmp(table->table_cache_key, db, dblen) && + !my_strcasecmp((is_alias ? table->table_name : table->real_name),table_name)) + break; + ptr=&(table->next); + } + return ptr; +} + diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 42c6d02576b..32071e5b083 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -25,7 +25,7 @@ static TABLE *delayed_get_table(THD *thd,TABLE_LIST *table_list); static int write_delayed(THD *thd,TABLE *table, enum_duplicates dup, char *query, uint query_length, bool log_on); static void end_delayed_insert(THD *thd); -static pthread_handler_decl(handle_delayed_insert,arg); +extern "C" pthread_handler_decl(handle_delayed_insert,arg); static void unlink_blobs(register TABLE *table); /* Define to force use of my_malloc() if the allocated memory block is big */ @@ -41,7 +41,8 @@ static void unlink_blobs(register TABLE *table); /* Check if insert fields are correct - Resets form->time_stamp if a timestamp value is set + Updates table->time_stamp to point to timestamp field or 0, depending on + if timestamp should be updated or not. */ static int @@ -60,7 +61,7 @@ check_insert_fields(THD *thd,TABLE *table,List<Item> &fields, if (grant_option && check_grant_all_columns(thd,INSERT_ACL,table)) return -1; - table->time_stamp=0; // This should be saved + table->time_stamp=0; // This is saved by caller } else { // Part field list @@ -73,45 +74,47 @@ check_insert_fields(THD *thd,TABLE *table,List<Item> &fields, } TABLE_LIST table_list; bzero((char*) &table_list,sizeof(table_list)); - table_list.alias= table_list.real_name= table->table_name; + table_list.db= table->table_cache_key; + table_list.real_name= table_list.alias= table->table_name; table_list.table=table; table_list.grant=table->grant; thd->dupp_field=0; - if (setup_tables(&table_list) || setup_fields(thd,&table_list,fields,1,0)) + if (setup_tables(&table_list) || + setup_fields(thd,&table_list,fields,1,0,0)) return -1; if (thd->dupp_field) { my_error(ER_FIELD_SPECIFIED_TWICE,MYF(0), thd->dupp_field->field_name); return -1; } + table->time_stamp=0; if (table->timestamp_field && // Don't set timestamp if used - table->timestamp_field->query_id == thd->query_id) - table->time_stamp=0; // This should be saved + table->timestamp_field->query_id != thd->query_id) + table->time_stamp= table->timestamp_field->offset()+1; } - // For the values we need select_priv + // For the values we need select_priv table->grant.want_privilege=(SELECT_ACL & ~table->grant.privilege); return 0; } int mysql_insert(THD *thd,TABLE_LIST *table_list, List<Item> &fields, - List<List_item> &values_list,enum_duplicates duplic, - thr_lock_type lock_type) + List<List_item> &values_list,enum_duplicates duplic) { int error; bool log_on= ((thd->options & OPTION_UPDATE_LOG) || - !(thd->master_access & PROCESS_ACL)); - bool using_transactions; + !(thd->master_access & SUPER_ACL)); + bool transactional_table, log_delayed, bulk_insert; uint value_count; - uint save_time_stamp; ulong counter = 1; ulonglong id; COPY_INFO info; TABLE *table; - List_iterator<List_item> its(values_list); + List_iterator_fast<List_item> its(values_list); List_item *values; char *query=thd->query; + thr_lock_type lock_type = table_list->lock_type; DBUG_ENTER("mysql_insert"); /* @@ -148,14 +151,10 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, List<Item> &fields, DBUG_RETURN(-1); thd->proc_info="init"; thd->used_tables=0; - save_time_stamp=table->time_stamp; values= its++; if (check_insert_fields(thd,table,fields,*values,1) || - setup_tables(table_list) || setup_fields(thd,table_list,*values,0,0)) - { - table->time_stamp=save_time_stamp; + setup_tables(table_list) || setup_fields(thd,table_list,*values,0,0,0)) goto abort; - } value_count= values->elements; while ((values = its++)) { @@ -165,18 +164,14 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, List<Item> &fields, my_printf_error(ER_WRONG_VALUE_COUNT_ON_ROW, ER(ER_WRONG_VALUE_COUNT_ON_ROW), MYF(0),counter); - table->time_stamp=save_time_stamp; goto abort; } - if (setup_fields(thd,table_list,*values,0,0)) - { - table->time_stamp=save_time_stamp; + if (setup_fields(thd,table_list,*values,0,0,0)) goto abort; - } } its.rewind (); /* - ** Fill in the given fields and dump it to the table file + Fill in the given fields and dump it to the table file */ info.records=info.deleted=info.copied=0; @@ -192,7 +187,19 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, List<Item> &fields, thd->proc_info="update"; if (duplic == DUP_IGNORE || duplic == DUP_REPLACE) table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); - while ((values = its++)) + if ((lock_type != TL_WRITE_DELAYED && !(specialflag & SPECIAL_SAFE_MODE)) && + values_list.elements >= MIN_ROWS_TO_USE_BULK_INSERT) + { + table->file->extra_opt(HA_EXTRA_WRITE_CACHE, + min(thd->variables.read_buff_size, + table->avg_row_length*values_list.elements)); + table->file->deactivate_non_unique_index(values_list.elements); + bulk_insert=1; + } + else + bulk_insert=0; + + while ((values= its++)) { if (fields.elements || !value_count) { @@ -253,28 +260,60 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, List<Item> &fields, info.copied=values_list.elements; end_delayed_insert(thd); } + query_cache_invalidate3(thd, table_list, 1); } else { + if (bulk_insert) + { + if (table->file->extra(HA_EXTRA_NO_CACHE)) + { + if (!error) + { + table->file->print_error(my_errno,MYF(0)); + error=1; + } + } + if (table->file->activate_all_index(thd)) + { + if (!error) + { + table->file->print_error(my_errno,MYF(0)); + error=1; + } + } + } if (id && values_list.elements != 1) thd->insert_id(id); // For update log else if (table->next_number_field) id=table->next_number_field->val_int(); // Return auto_increment value - using_transactions=table->file->has_transactions(); - if ((info.copied || info.deleted) && (error <= 0 || !using_transactions)) + + transactional_table= table->file->has_transactions(); + log_delayed= (transactional_table || table->tmp_table); + if ((info.copied || info.deleted) && (error <= 0 || !transactional_table)) { mysql_update_log.write(thd, thd->query, thd->query_length); if (mysql_bin_log.is_open()) { - Query_log_event qinfo(thd, thd->query, using_transactions); - if (mysql_bin_log.write(&qinfo) && using_transactions) + Query_log_event qinfo(thd, thd->query, thd->query_length, + log_delayed); + if (mysql_bin_log.write(&qinfo) && transactional_table) error=1; } - if (!using_transactions) + if (!log_delayed) thd->options|=OPTION_STATUS_NO_TRANS_UPDATE; } - if (using_transactions) + if (transactional_table) error=ha_autocommit_or_rollback(thd,error); + + /* + Store table for future invalidation or invalidate it in + the query cache if something changed + */ + if (info.copied || info.deleted) + { + query_cache_invalidate3(thd, table_list, 1); + } if (thd->lock) { mysql_unlock_tables(thd, thd->lock); @@ -282,13 +321,11 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, List<Item> &fields, } } thd->proc_info="end"; - table->time_stamp=save_time_stamp; // Restore auto timestamp ptr table->next_number_field=0; thd->count_cuted_fields=0; thd->next_insert_id=0; // Reset this if wrongly used if (duplic == DUP_IGNORE || duplic == DUP_REPLACE) table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); - if (error) goto abort; @@ -329,7 +366,7 @@ static int last_uniq_key(TABLE *table,uint keynr) /* -** Write a record to table with optional deleting of conflicting records + Write a record to table with optional deleting of conflicting records */ @@ -337,18 +374,18 @@ int write_record(TABLE *table,COPY_INFO *info) { int error; char *key=0; - + info->records++; if (info->handle_duplicates == DUP_REPLACE) { while ((error=table->file->write_row(table->record[0]))) { - if (error != HA_WRITE_SKIPP) + if (error != HA_WRITE_SKIP) goto err; uint key_nr; if ((int) (key_nr = table->file->get_dup_key(error)) < 0) { - error=HA_WRITE_SKIPP; /* Database can't find key */ + error=HA_WRITE_SKIP; /* Database can't find key */ goto err; } /* @@ -359,19 +396,19 @@ int write_record(TABLE *table,COPY_INFO *info) if (table->next_number_field && key_nr == table->next_number_index && table->file->auto_increment_column_changed) goto err; - if (table->file->option_flag() & HA_DUPP_POS) + if (table->file->table_flags() & HA_DUPP_POS) { if (table->file->rnd_pos(table->record[1],table->file->dupp_ref)) goto err; } else { - if (table->file->extra(HA_EXTRA_FLUSH_CACHE)) /* Not neaded with NISAM */ + if (table->file->extra(HA_EXTRA_FLUSH_CACHE)) /* Not needed with NISAM */ { error=my_errno; goto err; } - + if (!key) { if (!(key=(char*) my_safe_alloca(table->max_unique_length, @@ -416,15 +453,16 @@ int write_record(TABLE *table,COPY_INFO *info) err: if (key) my_afree(key); + info->last_errno= error; table->file->print_error(error,MYF(0)); return 1; } /****************************************************************************** - Check that all fields with arn't null_fields are used - if DONT_USE_DEFAULT_FIELDS isn't defined use default value for not - set fields. + Check that all fields with arn't null_fields are used + If DONT_USE_DEFAULT_FIELDS isn't defined use default value for not set + fields. ******************************************************************************/ static int check_null_fields(THD *thd __attribute__((unused)), @@ -447,10 +485,8 @@ static int check_null_fields(THD *thd __attribute__((unused)), } /***************************************************************************** -** Handling of delayed inserts -** -** A thread is created for each table that one uses with the DELAYED -** attribute. + Handling of delayed inserts + A thread is created for each table that one uses with the DELAYED attribute. *****************************************************************************/ class delayed_row :public ilink { @@ -499,6 +535,7 @@ public: bzero((char*) &thd.net,sizeof(thd.net)); // Safety thd.system_thread=1; + thd.host_or_ip= ""; bzero((char*) &info,sizeof(info)); pthread_mutex_init(&mutex,MY_MUTEX_INIT_FAST); pthread_cond_init(&cond,NULL); @@ -509,7 +546,7 @@ public: } ~delayed_insert() { - /* The following is not really neaded, but just for safety */ + /* The following is not really needed, but just for safety */ delayed_row *row; while ((row=rows.get())) delete row; @@ -524,8 +561,8 @@ public: thd.user=thd.host=0; thread_count--; delayed_insert_threads--; - VOID(pthread_cond_broadcast(&COND_thread_count)); /* Tell main we are ready */ VOID(pthread_mutex_unlock(&LOCK_thread_count)); + VOID(pthread_cond_broadcast(&COND_thread_count)); /* Tell main we are ready */ } /* The following is for checking when we can delete ourselves */ @@ -615,7 +652,7 @@ static TABLE *delayed_get_table(THD *thd,TABLE_LIST *table_list) } tmp->table_list= *table_list; // Needed to open table tmp->table_list.db= tmp->thd.db; - tmp->table_list.alias= tmp->table_list.real_name= tmp->thd.query; + tmp->table_list.alias= tmp->table_list.real_name=tmp->thd.query; tmp->lock(); pthread_mutex_lock(&tmp->mutex); if ((error=pthread_create(&tmp->thd.real_id,&connection_attrib, @@ -648,7 +685,7 @@ static TABLE *delayed_get_table(THD *thd,TABLE_LIST *table_list) /* Copy error message and abort */ thd->fatal_error=1; strmov(thd->net.last_error,tmp->thd.net.last_error); - thd->net.last_errno=thd->net.last_errno; + thd->net.last_errno=tmp->thd.net.last_errno; } tmp->unlock(); pthread_mutex_unlock(&LOCK_delayed_create); @@ -712,9 +749,9 @@ TABLE *delayed_insert::get_local_table(THD* client_thd) } client_thd->proc_info="allocating local table"; - copy= (TABLE*) sql_alloc(sizeof(*copy)+ - (table->fields+1)*sizeof(Field**)+ - table->reclength); + copy= (TABLE*) client_thd->alloc(sizeof(*copy)+ + (table->fields+1)*sizeof(Field**)+ + table->reclength); if (!copy) goto error; *copy= *table; @@ -732,7 +769,7 @@ TABLE *delayed_insert::get_local_table(THD* client_thd) found_next_number_field=table->found_next_number_field; for (org_field=table->field ; *org_field ; org_field++,field++) { - if (!(*field= (*org_field)->new_field(copy))) + if (!(*field= (*org_field)->new_field(&client_thd->mem_root,copy))) return 0; (*field)->move_field(adjust_ptrs); // Point at copy->record[0] if (*org_field == found_next_number_field) @@ -873,7 +910,7 @@ void kill_delayed_threads(void) * Create a new delayed insert thread */ -static pthread_handler_decl(handle_delayed_insert,arg) +extern "C" pthread_handler_decl(handle_delayed_insert,arg) { delayed_insert *di=(delayed_insert*) arg; THD *thd= &di->thd; @@ -897,17 +934,13 @@ static pthread_handler_decl(handle_delayed_insert,arg) #endif DBUG_ENTER("handle_delayed_insert"); - if (init_thr_lock() || - my_pthread_setspecific_ptr(THR_THD, thd) || - my_pthread_setspecific_ptr(THR_NET, &thd->net)) + if (init_thr_lock() || thd->store_globals()) { thd->fatal_error=1; strmov(thd->net.last_error,ER(thd->net.last_errno=ER_OUT_OF_RESOURCES)); goto end; } - thd->mysys_var=my_thread_var; - thd->dbug_thread_id=my_thread_id(); -#if !defined(__WIN__) && !defined(OS2) +#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__) sigset_t set; VOID(sigemptyset(&set)); // Get mask in use VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals)); @@ -961,23 +994,12 @@ static pthread_handler_decl(handle_delayed_insert,arg) if (!di->status && !di->stacked_inserts) { struct timespec abstime; -#if defined(HAVE_TIMESPEC_TS_SEC) - abstime.ts_sec=time((time_t*) 0)+(time_t) delayed_insert_timeout; - abstime.ts_nsec=0; -#elif defined(__WIN__) - abstime.tv_sec=time((time_t*) 0)+(time_t) delayed_insert_timeout; - abstime.tv_nsec=0; -#else - struct timeval tv; - gettimeofday(&tv,0); - abstime.tv_sec=tv.tv_sec+(time_t) delayed_insert_timeout; - abstime.tv_nsec=tv.tv_usec*1000; -#endif + set_timespec(abstime, delayed_insert_timeout); /* Information for pthread_kill */ di->thd.mysys_var->current_mutex= &di->mutex; di->thd.mysys_var->current_cond= &di->cond; - di->thd.proc_info=0; + di->thd.proc_info="Waiting for INSERT"; DBUG_PRINT("info",("Waiting for someone to insert rows")); while (!thd->killed) @@ -1012,6 +1034,7 @@ static pthread_handler_decl(handle_delayed_insert,arg) pthread_mutex_unlock(&di->thd.mysys_var->mutex); pthread_mutex_lock(&di->mutex); } + di->thd.proc_info=0; if (di->tables_in_use && ! thd->lock) { @@ -1157,7 +1180,7 @@ bool delayed_insert::handle_inserts(void) thd.net.last_errno = 0; // reset error for binlog if (write_record(table,&info)) { - info.error++; // Ignore errors + info.error_count++; // Ignore errors thread_safe_increment(delayed_insert_errors,&LOCK_delayed_status); row->log_query = 0; } @@ -1171,8 +1194,7 @@ bool delayed_insert::handle_inserts(void) mysql_update_log.write(&thd,row->query, row->query_length); if (using_bin_log) { - thd.query_length = row->query_length; - Query_log_event qinfo(&thd, row->query); + Query_log_event qinfo(&thd, row->query, row->query_length,0); mysql_bin_log.write(&qinfo); } } @@ -1200,6 +1222,7 @@ bool delayed_insert::handle_inserts(void) sql_print_error("%s",thd.net.last_error); goto err; } + query_cache_invalidate3(&thd, table, 1); if (thr_reschedule_write_lock(*thd.lock->locks)) { /* This should never happen */ @@ -1224,6 +1247,7 @@ bool delayed_insert::handle_inserts(void) sql_print_error("%s",thd.net.last_error); goto err; } + query_cache_invalidate3(&thd, table, 1); pthread_mutex_lock(&mutex); DBUG_RETURN(0); @@ -1243,7 +1267,7 @@ bool delayed_insert::handle_inserts(void) /*************************************************************************** -** store records in INSERT ... SELECT * + Store records in INSERT ... SELECT * ***************************************************************************/ int @@ -1251,13 +1275,12 @@ select_insert::prepare(List<Item> &values) { DBUG_ENTER("select_insert::prepare"); - save_time_stamp=table->time_stamp; if (check_insert_fields(thd,table,*fields,values,1)) DBUG_RETURN(1); restore_record(table,2); // Get empty record table->next_number_field=table->found_next_number_field; - thd->count_cuted_fields=1; /* calc cuted fields */ + thd->count_cuted_fields=1; // calc cuted fields thd->cuted_fields=0; if (info.handle_duplicates != DUP_REPLACE) table->file->extra(HA_EXTRA_WRITE_CACHE); @@ -1272,8 +1295,6 @@ select_insert::~select_insert() { if (table) { - if (save_time_stamp) - table->time_stamp=save_time_stamp; table->next_number_field=0; table->file->extra(HA_EXTRA_RESET); } @@ -1309,7 +1330,11 @@ void select_insert::send_error(uint errcode,const char *err) ::send_error(&thd->net,errcode,err); table->file->extra(HA_EXTRA_NO_CACHE); table->file->activate_all_index(thd); - ha_rollback(thd); + ha_rollback_stmt(thd); + if (info.copied || info.deleted) + { + query_cache_invalidate3(thd, table, 1); + } } @@ -1321,7 +1346,10 @@ bool select_insert::send_eof() table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); if ((error2=ha_autocommit_or_rollback(thd,error)) && ! error) error=error2; - + if (info.copied || info.deleted) + { + query_cache_invalidate3(thd, table, 1); + } if (error) { table->file->print_error(error,MYF(0)); @@ -1343,7 +1371,7 @@ bool select_insert::send_eof() mysql_update_log.write(thd,thd->query,thd->query_length); if (mysql_bin_log.is_open()) { - Query_log_event qinfo(thd, thd->query, + Query_log_event qinfo(thd, thd->query, thd->query_length, table->file->has_transactions()); mysql_bin_log.write(&qinfo); } @@ -1353,7 +1381,7 @@ bool select_insert::send_eof() /*************************************************************************** -** CREATE TABLE (SELECT) ... + CREATE TABLE (SELECT) ... ***************************************************************************/ int @@ -1369,7 +1397,6 @@ select_create::prepare(List<Item> &values) /* First field to copy */ field=table->field+table->fields - values.elements; - save_time_stamp=table->time_stamp; if (table->timestamp_field) // Don't set timestamp if used { table->timestamp_field->set_time(); @@ -1409,6 +1436,7 @@ bool select_create::send_data(List<Item> &values) extern HASH open_cache; + bool select_create::send_eof() { bool tmp=select_insert::send_eof(); @@ -1426,7 +1454,8 @@ bool select_create::send_eof() */ if (!table->tmp_table) hash_delete(&open_cache,(byte*) table); - lock=0; table=0; + lock=0; + table=0; VOID(pthread_mutex_unlock(&LOCK_open)); } return tmp; @@ -1454,11 +1483,11 @@ void select_create::abort() /***************************************************************************** -** Instansiate templates + Instansiate templates *****************************************************************************/ #ifdef __GNUC__ -template class List_iterator<List_item>; +template class List_iterator_fast<List_item>; template class I_List<delayed_insert>; template class I_List_iterator<delayed_insert>; template class I_List<delayed_row>; diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index a3c3db8947e..d5a225d95dd 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -142,20 +142,23 @@ LEX *lex_start(THD *thd, uchar *buf,uint length) lex->next_state=STATE_START; lex->end_of_query=(lex->ptr=buf)+length; lex->yylineno = 1; - lex->create_refs=lex->in_comment=0; + lex->select->create_refs=lex->in_comment=0; lex->length=0; - lex->in_sum_expr=0; - lex->expr_list.empty(); - lex->ftfunc_list.empty(); - lex->convert_set=thd->convert_set; + lex->select->in_sum_expr=0; + lex->select->expr_list.empty(); + lex->select->ftfunc_list.empty(); + lex->convert_set=(lex->thd=thd)->variables.convert_set; lex->yacc_yyss=lex->yacc_yyvs=0; lex->ignore_space=test(thd->sql_mode & MODE_IGNORE_SPACE); + lex->slave_thd_opt=0; + lex->sql_command=SQLCOM_END; + bzero(&lex->mi,sizeof(lex->mi)); return lex; } void lex_end(LEX *lex) { - lex->expr_list.delete_elements(); // If error when parsing sql-varargs + lex->select->expr_list.delete_elements(); // If error when parsing sql-varargs x_free(lex->yacc_yyss); x_free(lex->yacc_yyvs); } @@ -177,15 +180,14 @@ static int find_keyword(LEX *lex, uint len, bool function) udf_func *udf; if (function && using_udf_functions && (udf=find_udf((char*) tok, len))) { + lex->thd->safe_to_cache_query=0; + lex->yylval->udf=udf; switch (udf->returns) { case STRING_RESULT: - lex->yylval->udf=udf; return (udf->type == UDFTYPE_FUNCTION) ? UDF_CHAR_FUNC : UDA_CHAR_SUM; case REAL_RESULT: - lex->yylval->udf=udf; return (udf->type == UDFTYPE_FUNCTION) ? UDF_FLOAT_FUNC : UDA_FLOAT_SUM; case INT_RESULT: - lex->yylval->udf=udf; return (udf->type == UDFTYPE_FUNCTION) ? UDF_INT_FUNC : UDA_INT_SUM; } } @@ -196,12 +198,12 @@ static int find_keyword(LEX *lex, uint len, bool function) /* make a copy of token before ptr and set yytoklen */ -static inline LEX_STRING get_token(LEX *lex,uint length) +LEX_STRING get_token(LEX *lex,uint length) { LEX_STRING tmp; yyUnget(); // ptr points now after last token char tmp.length=lex->yytoklen=length; - tmp.str=(char*) sql_strmake((char*) lex->tok_start,tmp.length); + tmp.str=(char*) lex->thd->strmake((char*) lex->tok_start,tmp.length); return tmp; } @@ -250,7 +252,7 @@ static char *get_text(LEX *lex) str=lex->tok_start+1; end=lex->ptr-1; - if (!(start=(uchar*) sql_alloc((uint) (end-str)+1))) + if (!(start=(uchar*) lex->thd->alloc((uint) (end-str)+1))) return (char*) ""; // Sql_alloc has set error flag if (!found_escape) { @@ -337,7 +339,8 @@ static const char *longlong_str="9223372036854775807"; static const uint longlong_len=19; static const char *signed_longlong_str="-9223372036854775808"; static const uint signed_longlong_len=19; - +static const char *unsigned_longlong_str="18446744073709551615"; +static const uint unsigned_longlong_len=20; inline static uint int_token(const char *str,uint length) { @@ -393,12 +396,18 @@ inline static uint int_token(const char *str,uint length) else if (length < longlong_len) return LONG_NUM; else if (length > longlong_len) - return REAL_NUM; + { + if (length > unsigned_longlong_len) + return REAL_NUM; + cmp=unsigned_longlong_str; + smaller=ULONGLONG_NUM; + bigger=REAL_NUM; + } else { cmp=longlong_str; smaller=LONG_NUM; - bigger=REAL_NUM; + bigger= ULONGLONG_NUM; } } while (*cmp && *cmp++ == *str++) ; @@ -430,7 +439,7 @@ int yylex(void *arg) switch(state) { case STATE_OPERATOR_OR_IDENT: // Next is operator or keyword case STATE_START: // Start of token - // Skipp startspace + // Skip startspace for (c=yyGet() ; (state_map[c] == STATE_SKIP) ; c= yyGet()) { if (c == '\n') @@ -458,6 +467,11 @@ int yylex(void *arg) return((int) c); case STATE_IDENT: // Incomplete keyword or ident + if ((c == 'x' || c == 'X') && yyPeek() == '\'') + { // Found x'hex-number' + state=STATE_HEX_NUMBER; + break; + } #if defined(USE_MB) && defined(USE_MB_IDENT) if (use_mb(default_charset_info)) { @@ -493,7 +507,7 @@ int yylex(void *arg) length= (uint) (lex->ptr - lex->tok_start)-1; if (lex->ignore_space) { - for ( ; state_map[c] == STATE_SKIP ; c= yyGet()); + for (; state_map[c] == STATE_SKIP ; c= yyGet()); } if (c == '.' && (state_map[yyPeek()] == STATE_IDENT || state_map[yyPeek()] == STATE_NUMBER_IDENT)) @@ -509,6 +523,8 @@ int yylex(void *arg) yySkip(); // next state does a unget } yylval->lex_str=get_token(lex,length); + if (lex->convert_set) + lex->convert_set->convert((char*) yylval->lex_str.str,lex->yytoklen); return(IDENT); case STATE_IDENT_SEP: // Found ident and now '.' @@ -518,7 +534,7 @@ int yylex(void *arg) c=yyGet(); // should be '.' return((int) c); - case STATE_NUMBER_IDENT: // number or ident which starts with num + case STATE_NUMBER_IDENT: // number or ident which num-start while (isdigit((c = yyGet()))) ; if (state_map[c] != STATE_IDENT) { // Can't be identifier @@ -544,10 +560,10 @@ int yylex(void *arg) lex->tok_start[0] == '0' ) { // Varbinary while (isxdigit((c = yyGet()))) ; - if ((lex->ptr - lex->tok_start) >= 4) + if ((lex->ptr - lex->tok_start) >= 4 && state_map[c] != STATE_IDENT) { yylval->lex_str=get_token(lex,yyLength()); - yylval->lex_str.str+=2; // Skipp 0x + yylval->lex_str.str+=2; // Skip 0x yylval->lex_str.length-=2; lex->yytoklen-=2; return (HEX_NUM); @@ -597,10 +613,12 @@ int yylex(void *arg) case STATE_FOUND_IDENT: // Complete ident yylval->lex_str=get_token(lex,yyLength()); + if (lex->convert_set) + lex->convert_set->convert((char*) yylval->lex_str.str,lex->yytoklen); return(IDENT); case STATE_USER_VARIABLE_DELIMITER: - lex->tok_start=lex->ptr; // Skipp first ` + lex->tok_start=lex->ptr; // Skip first ` #ifdef USE_MB if (use_mb(default_charset_info)) { @@ -625,14 +643,17 @@ int yylex(void *arg) c != (uchar) NAMES_SEP_CHAR) ; } yylval->lex_str=get_token(lex,yyLength()); + if (lex->convert_set) + lex->convert_set->convert((char*) yylval->lex_str.str,lex->yytoklen); if (state_map[c] == STATE_USER_VARIABLE_DELIMITER) - yySkip(); // Skipp end ` + yySkip(); // Skip end ` return(IDENT); case STATE_SIGNED_NUMBER: // Incomplete signed number if (prev_state == STATE_OPERATOR_OR_IDENT) { - if (c == '-' && yyPeek() == '-' && isspace(yyPeek2())) + if (c == '-' && yyPeek() == '-' && + (isspace(yyPeek2()) || iscntrl(yyPeek2()))) state=STATE_COMMENT; else state= STATE_CHAR; // Must be operator @@ -672,7 +693,7 @@ int yylex(void *arg) { c = yyGet(); if (c == '-' || c == '+') - c = yyGet(); // Skipp sign + c = yyGet(); // Skip sign if (!isdigit(c)) { // No digit after sign state= STATE_CHAR; @@ -685,6 +706,21 @@ int yylex(void *arg) yylval->lex_str=get_token(lex,yyLength()); return(REAL_NUM); + case STATE_HEX_NUMBER: // Found x'hexstring' + yyGet(); // Skip ' + while (isxdigit((c = yyGet()))) ; + length=(lex->ptr - lex->tok_start); // Length of hexnum+3 + if (!(length & 1) || c != '\'') + { + return(ABORT_SYM); // Illegal hex constant + } + yyGet(); // get_token makes an unget + yylval->lex_str=get_token(lex,length); + yylval->lex_str.str+=2; // Skip x' + yylval->lex_str.length-=3; // Don't count x' and last ' + lex->yytoklen-=3; + return (HEX_NUM); + case STATE_CMP_OP: // Incomplete comparison operator if (state_map[yyPeek()] == STATE_CMP_OP || state_map[yyPeek()] == STATE_LONG_CMP_OP) @@ -734,7 +770,7 @@ int yylex(void *arg) return(TEXT_STRING); case STATE_COMMENT: // Comment - lex->options|= OPTION_FOUND_COMMENT; + lex->select_lex.options|= OPTION_FOUND_COMMENT; while ((c = yyGet()) != '\n' && c) ; yyUnget(); // Safety against eof state = STATE_START; // Try again @@ -746,7 +782,7 @@ int yylex(void *arg) break; } yySkip(); // Skip '*' - lex->options|= OPTION_FOUND_COMMENT; + lex->select_lex.options|= OPTION_FOUND_COMMENT; if (yyPeek() == '!') // MySQL command in comment { ulong version=MYSQL_VERSION_ID; @@ -816,14 +852,12 @@ int yylex(void *arg) } break; case STATE_USER_END: // end '@' of user@hostname - switch (state_map[yyPeek()]) - { + switch (state_map[yyPeek()]) { case STATE_STRING: case STATE_USER_VARIABLE_DELIMITER: break; case STATE_USER_END: - lex->next_state=STATE_USER_END; - yySkip(); + lex->next_state=STATE_SYSTEM_VAR; break; default: lex->next_state=STATE_HOSTNAME; @@ -838,6 +872,33 @@ int yylex(void *arg) c= yyGet()) ; yylval->lex_str=get_token(lex,yyLength()); return(LEX_HOSTNAME); + case STATE_SYSTEM_VAR: + yylval->lex_str.str=(char*) lex->ptr; + yylval->lex_str.length=1; + lex->next_state=STATE_IDENT_OR_KEYWORD; + yySkip(); // Skip '@' + return((int) '@'); + case STATE_IDENT_OR_KEYWORD: + /* + We come here when we have found two '@' in a row. + We should now be able to handle: + [(global | local | session) .]variable_name + */ + + while (state_map[c=yyGet()] == STATE_IDENT || + state_map[c] == STATE_NUMBER_IDENT) ; + if (c == '.') + lex->next_state=STATE_IDENT_SEP; + length= (uint) (lex->ptr - lex->tok_start)-1; + if ((tokval= find_keyword(lex,length,0))) + { + yyUnget(); // Put back 'c' + return(tokval); // Was keyword + } + yylval->lex_str=get_token(lex,length); + if (lex->convert_set) + lex->convert_set->convert((char*) yylval->lex_str.str,lex->yytoklen); + return(IDENT); } } } diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 7d1df04b852..a905871e629 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -22,8 +22,12 @@ class Table_ident; class sql_exchange; class LEX_COLUMN; -// The following hack is neaded because mysql_yacc.cc does not define -// YYSTYPE before including this file +/* + The following hack is needed because mysql_yacc.cc does not define + YYSTYPE before including this file +*/ + +#include "set_var.h" #ifdef MYSQL_YACC #define LEX_YYSTYPE void * @@ -54,99 +58,127 @@ enum enum_sql_command { SQLCOM_BEGIN, SQLCOM_LOAD_MASTER_TABLE, SQLCOM_CHANGE_MASTER, SQLCOM_RENAME_TABLE, SQLCOM_BACKUP_TABLE, SQLCOM_RESTORE_TABLE, SQLCOM_RESET, SQLCOM_PURGE, SQLCOM_SHOW_BINLOGS, - SQLCOM_SHOW_OPEN_TABLES, SQLCOM_DO, SQLCOM_EMPTY_QUERY, + SQLCOM_SHOW_OPEN_TABLES, SQLCOM_LOAD_MASTER_DATA, + SQLCOM_HA_OPEN, SQLCOM_HA_CLOSE, SQLCOM_HA_READ, + SQLCOM_SHOW_SLAVE_HOSTS, SQLCOM_DELETE_MULTI, SQLCOM_MULTI_UPDATE, + SQLCOM_SHOW_BINLOG_EVENTS, SQLCOM_SHOW_NEW_MASTER, SQLCOM_DO, + SQLCOM_EMPTY_QUERY, SQLCOM_END }; -enum lex_states { STATE_START, STATE_CHAR, STATE_IDENT, - STATE_IDENT_SEP, - STATE_IDENT_START, - STATE_FOUND_IDENT, - STATE_SIGNED_NUMBER, - STATE_REAL, - STATE_CMP_OP, - STATE_LONG_CMP_OP, - STATE_STRING, - STATE_COMMENT, - STATE_END, - STATE_OPERATOR_OR_IDENT, - STATE_NUMBER_IDENT, - STATE_INT_OR_REAL, - STATE_REAL_OR_POINT, - STATE_BOOL, - STATE_EOL, - STATE_ESCAPE, - STATE_LONG_COMMENT, - STATE_END_LONG_COMMENT, - STATE_COLON, - STATE_SET_VAR, - STATE_USER_END, - STATE_HOSTNAME, - STATE_SKIP, - STATE_USER_VARIABLE_DELIMITER +enum lex_states +{ + STATE_START, STATE_CHAR, STATE_IDENT, STATE_IDENT_SEP, STATE_IDENT_START, + STATE_FOUND_IDENT, STATE_SIGNED_NUMBER, STATE_REAL, STATE_HEX_NUMBER, + STATE_CMP_OP, STATE_LONG_CMP_OP, STATE_STRING, STATE_COMMENT, STATE_END, + STATE_OPERATOR_OR_IDENT, STATE_NUMBER_IDENT, STATE_INT_OR_REAL, + STATE_REAL_OR_POINT, STATE_BOOL, STATE_EOL, STATE_ESCAPE, STATE_LONG_COMMENT, + STATE_END_LONG_COMMENT, STATE_COLON, STATE_SET_VAR, STATE_USER_END, + STATE_HOSTNAME, STATE_SKIP, STATE_USER_VARIABLE_DELIMITER, STATE_SYSTEM_VAR, + STATE_IDENT_OR_KEYWORD }; typedef List<Item> List_item; typedef struct st_lex_master_info { - char* host, *user, *password,*log_file_name; + char *host, *user, *password, *log_file_name; uint port, connect_retry; ulonglong pos; + ulong server_id; + char *relay_log_name; + ulong relay_log_pos; } LEX_MASTER_INFO; + +enum sub_select_type +{ + UNSPECIFIED_TYPE, UNION_TYPE, INTERSECT_TYPE, EXCEPT_TYPE, OLAP_TYPE, NOT_A_SELECT +}; + +enum olap_type +{ + UNSPECIFIED_OLAP_TYPE, CUBE_TYPE, ROLLUP_TYPE +}; + +/* The state of the lex parsing for selects */ + +typedef struct st_select_lex +{ + enum sub_select_type linkage; + enum olap_type olap; + char *db,*db1,*table1,*db2,*table2; /* For outer join using .. */ + Item *where,*having; + ha_rows select_limit,offset_limit; + ulong options, table_join_options; + List<List_item> expr_list; + List<List_item> when_list; + SQL_LIST order_list,table_list,group_list; + List<Item> item_list; + List<String> interval_list,use_index, *use_index_ptr, + ignore_index, *ignore_index_ptr; + List<Item_func_match> ftfunc_list; + uint in_sum_expr, sort_default; + bool create_refs, braces; + st_select_lex *next; +} SELECT_LEX; + + /* The state of the lex parsing. This is saved in the THD struct */ -typedef struct st_lex { +typedef struct st_lex +{ uint yylineno,yytoklen; /* Simulate lex */ LEX_YYSTYPE yylval; + SELECT_LEX select_lex, *select, *last_selects; uchar *ptr,*tok_start,*tok_end,*end_of_query; char *length,*dec,*change,*name; - char *db,*db1,*table1,*db2,*table2; /* For outer join using .. */ char *backup_dir; /* For RESTORE/BACKUP */ char* to_log; /* For PURGE MASTER LOGS TO */ + char* x509_subject,*x509_issuer,*ssl_cipher; + enum SSL_type ssl_type; /* defined in violite.h */ String *wild; sql_exchange *exchange; - ha_rows select_limit,offset_limit; - List<List_item> expr_list; - List<List_item> when_list; - List<List_item> many_values; List<key_part_spec> col_list; List<Alter_drop> drop_list; List<Alter_column> alter_list; - List<String> interval_list,use_index,*use_index_ptr, - ignore_index, *ignore_index_ptr; - List<st_lex_user> users_list; + List<String> interval_list; + List<LEX_USER> users_list; List<LEX_COLUMN> columns; List<Key> key_list; List<create_field> create_list; - List<Item> item_list,*insert_list,field_list,value_list; - List<Item_func_match> ftfunc_list; - SQL_LIST order_list,table_list,group_list,proc_list; + List<Item> *insert_list,field_list,value_list; + List<List_item> many_values; + List<set_var_base> var_list; + SQL_LIST proc_list, auxilliary_table_list; TYPELIB *interval; create_field *last_field; - - Item *where,*having,*default_value; + Item *default_value; CONVERT *convert_set; + CONVERT *thd_convert_set; // Set with SET CHAR SET LEX_USER *grant_user; gptr yacc_yyss,yacc_yyvs; + THD *thd; udf_func udf; HA_CHECK_OPT check_opt; // check/repair options HA_CREATE_INFO create_info; LEX_MASTER_INFO mi; // used by CHANGE MASTER + USER_RESOURCES mqh; ulong thread_id,type; - ulong options; - ulong gemini_spin_retries; enum_sql_command sql_command; enum lex_states next_state; enum enum_duplicates duplicates; enum enum_tx_isolation tx_isolation; - uint in_sum_expr,grant,grant_tot_col,which_columns; + enum enum_ha_read_modes ha_read_mode; + enum ha_rkey_function ha_rkey_mode; + enum enum_enable_or_disable alter_keys_onoff; + enum enum_var_type option_type; + uint grant,grant_tot_col,which_columns, union_option; thr_lock_type lock_option; - bool create_refs,drop_primary,drop_if_exists,local_file; - bool in_comment,ignore_space,verbose; - + bool drop_primary, drop_if_exists, drop_temporary, local_file, olap; + bool in_comment,ignore_space,verbose,simple_alter; + uint slave_thd_opt; } LEX; diff --git a/sql/sql_list.cc b/sql/sql_list.cc index 7d5fc442121..1124605ca24 100644 --- a/sql/sql_list.cc +++ b/sql/sql_list.cc @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -20,3 +20,5 @@ #endif #include "mysql_priv.h" + +list_node end_of_list; diff --git a/sql/sql_list.h b/sql/sql_list.h index d21f2e658dc..542eef623f0 100644 --- a/sql/sql_list.h +++ b/sql/sql_list.h @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -41,25 +41,40 @@ public: /* ** basic single linked list ** Used for item and item_buffs. +** All list ends with a pointer to the 'end_of_list' element, which +** data pointer is a null pointer and the next pointer points to itself. +** This makes it very fast to traverse lists as we don't have to +** test for a specialend condition for list that can't contain a null +** pointer. */ +class list_node :public Sql_alloc +{ +public: + list_node *next; + void *info; + list_node(void *info_par,list_node *next_par) + :next(next_par),info(info_par) + {} + list_node() /* For end_of_list */ + { + info=0; + next= this; + } + friend class base_list; + friend class base_list_iterator; +}; + +extern list_node end_of_list; + class base_list :public Sql_alloc { protected: - class list_node :public Sql_alloc - { - public: - list_node *next; - void *info; - list_node(void *info_par,list_node *next_par) : next(next_par),info(info_par) {} - friend class base_list; - friend class base_list_iterator; - }; list_node *first,**last; public: uint elements; - inline void empty() { elements=0; first=0; last=&first;} + inline void empty() { elements=0; first= &end_of_list; last=&first;} inline base_list() { empty(); } inline base_list(const base_list &tmp) :Sql_alloc() { @@ -69,7 +84,7 @@ public: } inline bool push_back(void *info) { - if (((*last)=new list_node(info,0))) + if (((*last)=new list_node(info, &end_of_list))) { last= &(*last)->next; elements++; @@ -82,7 +97,7 @@ public: list_node *node=new list_node(info,first); if (node) { - if (!first) + if (last == &first) last= &node->next; first=node; elements++; @@ -96,22 +111,21 @@ public: delete *prev; *prev=node; if (!--elements) - { last= &first; - first=0; - } } inline void *pop(void) { - if (!first) return 0; + if (first == &end_of_list) return 0; list_node *tmp=first; first=first->next; if (!--elements) last= &first; return tmp->info; } - inline void *head() { return first ? first->info : 0; } - inline void **head_ref() { return first ? &first->info : 0; } + inline void *head() { return first->info; } + inline void **head_ref() { return first != &end_of_list ? &first->info : 0; } + inline bool is_empty() { return first == &end_of_list ; } + inline list_node *last_ref() { return &end_of_list; } friend class base_list_iterator; protected: @@ -129,7 +143,7 @@ protected: class base_list_iterator { base_list *list; - base_list::list_node **el,**prev,*current; + list_node **el,**prev,*current; public: base_list_iterator(base_list &list_par) :list(&list_par),el(&list_par.first), prev(0),current(0) @@ -137,16 +151,22 @@ public: inline void *next(void) { prev=el; - if (!(current= *el)) - return 0; + current= *el; el= ¤t->next; return current->info; } + inline void *next_fast(void) + { + list_node *tmp; + tmp= *el; + el= &tmp->next; + return tmp->info; + } inline void rewind(void) { el= &list->first; } - void *replace(void *element) + inline void *replace(void *element) { // Return old element void *tmp=current->info; current->info=element; @@ -155,7 +175,7 @@ public: void *replace(base_list &new_list) { void *ret_value=current->info; - if (new_list.first) + if (!new_list.is_empty()) { *new_list.last=current->next; current->info=new_list.first->info; @@ -182,7 +202,7 @@ public: } inline bool is_last(void) { - return *el == 0; + return el == &list->last_ref()->next; } }; @@ -200,7 +220,7 @@ public: void delete_elements(void) { list_node *element,*next; - for (element=first; element ; element=next) + for (element=first; element != &end_of_list; element=next) { next=element->next; delete (T*) element->info; @@ -215,18 +235,30 @@ template <class T> class List_iterator :public base_list_iterator public: List_iterator(List<T> &a) : base_list_iterator(a) {} inline T* operator++(int) { return (T*) base_list_iterator::next(); } - inline void rewind(void) { base_list_iterator::rewind(); } inline T *replace(T *a) { return (T*) base_list_iterator::replace(a); } inline T *replace(List<T> &a) { return (T*) base_list_iterator::replace(a); } - inline void remove(void) { base_list_iterator::remove(); } inline void after(T *a) { base_list_iterator::after(a); } inline T** ref(void) { return (T**) base_list_iterator::ref(); } - inline bool is_last(void) { return base_list_iterator::is_last(); } +}; + +template <class T> class List_iterator_fast :public base_list_iterator +{ +protected: + inline T *replace(T *a) { return (T*) 0; } + inline T *replace(List<T> &a) { return (T*) 0; } + inline void remove(void) { } + inline void after(T *a) { } + inline T** ref(void) { return (T**) 0; } + +public: + List_iterator_fast(List<T> &a) : base_list_iterator(a) {} + inline T* operator++(int) { return (T*) base_list_iterator::next_fast(); } + inline void rewind(void) { base_list_iterator::rewind(); } }; /* -** An simple intrusive list with automaticly removes element from list +** A simple intrusive list which automaticly removes element from list ** on delete (for THD element) */ diff --git a/sql/sql_load.cc b/sql/sql_load.cc index 1dcc8c2130e..62ed0fc5bed 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -20,6 +20,7 @@ #include "mysql_priv.h" #include <my_dir.h> #include <m_ctype.h> +#include "sql_repl.h" class READ_INFO { File file; @@ -32,6 +33,7 @@ class READ_INFO { int field_term_char,line_term_char,enclosed_char,escape_char; int *stack,*stack_pos; bool found_end_of_line,start_of_line,eof; + bool need_end_io_cache; IO_CACHE cache; NET *io_net; @@ -50,6 +52,22 @@ public: char unescape(char chr); int terminator(char *ptr,uint length); bool find_start_of_fields(); + /* + We need to force cache close before destructor is invoked to log + the last read block + */ + void end_io_cache() + { + ::end_io_cache(&cache); + need_end_io_cache = 0; + } + + /* + Either this method, or we need to make cache public + Arg must be set from mysql_load() since constructor does not see + either the table or THD value + */ + void set_io_cache_arg(void* arg) { cache.arg = arg; } }; static int read_fixed_length(THD *thd,COPY_INFO &info,TABLE *table, @@ -67,22 +85,31 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, File file; TABLE *table; int error; - uint save_skip_lines = ex->skip_lines; String *field_term=ex->field_term,*escaped=ex->escaped, *enclosed=ex->enclosed; bool is_fifo=0; - bool using_transactions; + LOAD_FILE_INFO lf_info; + char *db = table_list->db; // This is never null + /* If no current database, use database where table is located */ + char *tdb= thd->db ? thd->db : db; + bool transactional_table, log_delayed; DBUG_ENTER("mysql_load"); +#ifdef EMBEDDED_LIBRARY + read_file_from_client = 0; //server is always in the same process +#endif + if (escaped->length() > 1 || enclosed->length() > 1) { my_message(ER_WRONG_FIELD_TERMINATORS,ER(ER_WRONG_FIELD_TERMINATORS), MYF(0)); DBUG_RETURN(-1); } - if (!(table = open_ltable(thd,table_list,lock_type))) DBUG_RETURN(-1); + transactional_table= table->file->has_transactions(); + log_delayed= (transactional_table || table->tmp_table); + if (!fields.elements) { Field **field; @@ -92,7 +119,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, else { // Part field list thd->dupp_field=0; - if (setup_tables(table_list) || setup_fields(thd,table_list,fields,1,0)) + if (setup_tables(table_list) || setup_fields(thd,table_list,fields,1,0,0)) DBUG_RETURN(-1); if (thd->dupp_field) { @@ -103,7 +130,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, uint tot_length=0; bool use_blobs=0,use_timestamp=0; - List_iterator<Item> it(fields); + List_iterator_fast<Item> it(fields); Item_field *field; while ((field=(Item_field*) it++)) @@ -133,12 +160,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, if (read_file_from_client) { - char tmp [FN_REFLEN+1],*end; - DBUG_PRINT("info",("reading local file")); - tmp[0] = (char) 251; /* NULL_LENGTH */ - end=strnmov(tmp+1,ex->file_name,sizeof(tmp)-2); - (void) my_net_write(&thd->net,tmp,(uint) (end-tmp)); - (void) net_flush(&thd->net); + (void)net_request_file(&thd->net,ex->file_name); file = -1; } else @@ -148,10 +170,10 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, ex->file_name+=dirname_length(ex->file_name); #endif if (!dirname_length(ex->file_name) && - strlen(ex->file_name)+strlen(mysql_data_home)+strlen(thd->db)+3 < + strlen(ex->file_name)+strlen(mysql_data_home)+strlen(tdb)+3 < FN_REFLEN) { - (void) sprintf(name,"%s/%s/%s",mysql_data_home,thd->db,ex->file_name); + (void) sprintf(name,"%s/%s/%s",mysql_data_home,tdb,ex->file_name); unpack_filename(name,name); /* Convert to system format */ } else @@ -161,9 +183,10 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, MY_STAT stat_info; if (!my_stat(name,&stat_info,MYF(MY_WME))) DBUG_RETURN(-1); - - // the file must be: - if (!((stat_info.st_mode & S_IROTH) == S_IROTH && // readable by others + + // if we are not in slave thread, the file must be: + if (!thd->slave_thread && + !((stat_info.st_mode & S_IROTH) == S_IROTH && // readable by others #ifndef __EMX__ (stat_info.st_mode & S_IFLNK) != S_IFLNK && // and not a symlink #endif @@ -196,13 +219,28 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, DBUG_RETURN(-1); // Can't allocate buffers } + if (!opt_old_rpl_compat && mysql_bin_log.is_open()) + { + lf_info.thd = thd; + lf_info.ex = ex; + lf_info.db = db; + lf_info.table_name = table_list->real_name; + lf_info.fields = &fields; + lf_info.handle_dup = handle_duplicates; + lf_info.wrote_create_file = 0; + lf_info.last_pos_in_file = HA_POS_ERROR; + lf_info.log_delayed= log_delayed; + read_info.set_io_cache_arg((void*) &lf_info); + } restore_record(table,2); thd->count_cuted_fields=1; /* calc cuted fields */ thd->cuted_fields=0L; if (ex->line_term->length() && field_term->length()) { - while (ex->skip_lines--) + // ex->skip_lines needs to be preserved for logging + uint skip_lines = ex->skip_lines; + while (skip_lines--) { if (read_info.next_line()) break; @@ -214,7 +252,9 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, if (use_timestamp) table->time_stamp=0; table->next_number_field=table->found_next_number_field; - VOID(table->file->extra(HA_EXTRA_WRITE_CACHE)); + VOID(table->file->extra_opt(HA_EXTRA_WRITE_CACHE, + thd->variables.read_buff_size)); + table->bulk_insert= 1; if (handle_duplicates == DUP_IGNORE || handle_duplicates == DUP_REPLACE) table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); @@ -224,47 +264,88 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, error=read_fixed_length(thd,info,table,fields,read_info); else error=read_sep_field(thd,info,table,fields,read_info,*enclosed); - if (table->file->extra(HA_EXTRA_NO_CACHE) || - table->file->activate_all_index(thd)) - error=1; /* purecov: inspected */ + if (table->file->extra(HA_EXTRA_NO_CACHE)) + error=1; /* purecov: inspected */ + if (table->file->activate_all_index(thd)) + error=1; /* purecov: inspected */ table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); table->time_stamp=save_time_stamp; table->next_number_field=0; - if (thd->lock) - { - mysql_unlock_tables(thd, thd->lock); - thd->lock=0; - } } if (file >= 0) my_close(file,MYF(0)); free_blobs(table); /* if pack_blob was used */ table->copy_blobs=0; thd->count_cuted_fields=0; /* Don`t calc cuted fields */ - using_transactions = table->file->has_transactions(); + if (error) { - if (using_transactions) + if (transactional_table) ha_autocommit_or_rollback(thd,error); - DBUG_RETURN(-1); // Error on read + if (!opt_old_rpl_compat && mysql_bin_log.is_open()) + { + if (lf_info.wrote_create_file) + { + /* + Make sure last block (the one which caused the error) gets logged. + This is needed because otherwise after write of + (to the binlog, not to read_info (which is a cache)) + Delete_file_log_event the bad block will remain in read_info. + At the end of mysql_load(), the destructor of read_info will call + end_io_cache() which will flush read_info, so we will finally have + this in the binlog: + Append_block # The last successfull block + Delete_file + Append_block # The failing block + which is nonsense. + */ + read_info.end_io_cache(); + Delete_file_log_event d(thd, log_delayed); + mysql_bin_log.write(&d); + } + } + error= -1; // Error on read + goto err; } sprintf(name,ER(ER_LOAD_INFO),info.records,info.deleted, info.records-info.copied,thd->cuted_fields); send_ok(&thd->net,info.copied+info.deleted,0L,name); // on the slave thd->query is never initialized - if(!thd->slave_thread) + if (!thd->slave_thread) mysql_update_log.write(thd,thd->query,thd->query_length); - - if (!using_transactions) + + if (!log_delayed) thd->options|=OPTION_STATUS_NO_TRANS_UPDATE; - if (!read_file_from_client && mysql_bin_log.is_open()) + if (mysql_bin_log.is_open()) { - ex->skip_lines = save_skip_lines; - Load_log_event qinfo(thd, ex, table->table_cache_key, table->table_name, - fields, handle_duplicates); - mysql_bin_log.write(&qinfo); + if (opt_old_rpl_compat) + { + if (!read_file_from_client) + { + Load_log_event qinfo(thd, ex, db, table->table_name, fields, + handle_duplicates, log_delayed); + mysql_bin_log.write(&qinfo); + } + } + else + { + read_info.end_io_cache(); // make sure last block gets logged + if (lf_info.wrote_create_file) + { + Execute_load_log_event e(thd, log_delayed); + mysql_bin_log.write(&e); + } + } } - if (using_transactions) + if (transactional_table) error=ha_autocommit_or_rollback(thd,error); + query_cache_invalidate3(thd, table_list, 0); + +err: + if (thd->lock) + { + mysql_unlock_tables(thd, thd->lock); + thd->lock=0; + } DBUG_RETURN(error); } @@ -277,10 +358,12 @@ static int read_fixed_length(THD *thd,COPY_INFO &info,TABLE *table,List<Item> &fields, READ_INFO &read_info) { - List_iterator<Item> it(fields); + List_iterator_fast<Item> it(fields); Item_field *sql_field; + ulonglong id; DBUG_ENTER("read_fixed_length"); + id=0; /* No fields can be null in this format. mark all fields as not null */ while ((sql_field= (Item_field*) it++)) sql_field->field->set_notnull(); @@ -323,13 +406,23 @@ read_fixed_length(THD *thd,COPY_INFO &info,TABLE *table,List<Item> &fields, thd->cuted_fields++; /* To long row */ if (write_record(table,&info)) DBUG_RETURN(1); + /* + If auto_increment values are used, save the first one + for LAST_INSERT_ID() and for the binary/update log. + We can't use insert_id() as we don't want to touch the + last_insert_id_used flag. + */ + if (!id && thd->insert_id_used) + id= thd->last_insert_id; if (table->next_number_field) table->next_number_field->reset(); // Clear for next record - if (read_info.next_line()) // Skipp to next line + if (read_info.next_line()) // Skip to next line break; if (read_info.line_cuted) thd->cuted_fields++; /* To long row */ } + if (id && !read_info.error) + thd->insert_id(id); // For binary/update log DBUG_RETURN(test(read_info.error)); } @@ -340,13 +433,15 @@ read_sep_field(THD *thd,COPY_INFO &info,TABLE *table, List<Item> &fields, READ_INFO &read_info, String &enclosed) { - List_iterator<Item> it(fields); + List_iterator_fast<Item> it(fields); Item_field *sql_field; uint enclosed_length; + ulonglong id; DBUG_ENTER("read_sep_field"); enclosed_length=enclosed.length(); - + id=0; + for (;;it.rewind()) { if (thd->killed) @@ -375,7 +470,7 @@ read_sep_field(THD *thd,COPY_INFO &info,TABLE *table, { if (field->type() == FIELD_TYPE_TIMESTAMP) ((Field_timestamp*) field)->set_time(); - else + else if (field != table->next_number_field) thd->cuted_fields++; } continue; @@ -390,7 +485,7 @@ read_sep_field(THD *thd,COPY_INFO &info,TABLE *table, { // Last record if (sql_field == (Item_field*) fields.head()) break; - for ( ; sql_field ; sql_field=(Item_field*) it++) + for (; sql_field ; sql_field=(Item_field*) it++) { sql_field->field->set_null(); sql_field->field->reset(); @@ -399,13 +494,23 @@ read_sep_field(THD *thd,COPY_INFO &info,TABLE *table, } if (write_record(table,&info)) DBUG_RETURN(1); + /* + If auto_increment values are used, save the first one + for LAST_INSERT_ID() and for the binary/update log. + We can't use insert_id() as we don't want to touch the + last_insert_id_used flag. + */ + if (!id && thd->insert_id_used) + id= thd->last_insert_id; if (table->next_number_field) table->next_number_field->reset(); // Clear for next record - if (read_info.next_line()) // Skipp to next line + if (read_info.next_line()) // Skip to next line break; if (read_info.line_cuted) thd->cuted_fields++; /* To long row */ } + if (id && !read_info.error) + thd->insert_id(id); // For binary/update log DBUG_RETURN(test(read_info.error)); } @@ -430,8 +535,10 @@ READ_INFO::unescape(char chr) } - /* Read a line using buffering */ - /* If last line is empty (in line mode) then it isn't outputed */ +/* + Read a line using buffering + If last line is empty (in line mode) then it isn't outputed +*/ READ_INFO::READ_INFO(File file_par, uint tot_length, String &field_term, @@ -488,6 +595,22 @@ READ_INFO::READ_INFO(File file_par, uint tot_length, String &field_term, my_free((gptr) buffer,MYF(0)); /* purecov: inspected */ error=1; } + else + { + /* + init_io_cache() will not initialize read_function member + if the cache is READ_NET. The reason is explained in + mysys/mf_iocache.c. So we work around the problem with a + manual assignment + */ + if (get_it_from_net) + cache.read_function = _my_b_net_read; + + need_end_io_cache = 1; + if (!opt_old_rpl_compat && mysql_bin_log.is_open()) + cache.pre_read = cache.pre_close = + (IO_CACHE_CALLBACK) log_loaded_block; + } } } @@ -496,7 +619,8 @@ READ_INFO::~READ_INFO() { if (!error) { - end_io_cache(&cache); + if (need_end_io_cache) + ::end_io_cache(&cache); my_free((gptr) buffer,MYF(0)); error=1; } @@ -536,10 +660,10 @@ int READ_INFO::read_field() if (found_end_of_line) return 1; // One have to call next_line - /* Skipp until we find 'line_start' */ + /* Skip until we find 'line_start' */ if (start_of_line) - { // Skipp until line_start + { // Skip until line_start start_of_line=0; if (find_start_of_fields()) return 1; @@ -682,7 +806,7 @@ found_eof: /* ** One can't use fixed length with multi-byte charset ** */ - + int READ_INFO::read_fixed_length() { int chr; @@ -691,7 +815,7 @@ int READ_INFO::read_fixed_length() return 1; // One have to call next_line if (start_of_line) - { // Skipp until line_start + { // Skip until line_start start_of_line=0; if (find_start_of_fields()) return 1; diff --git a/sql/sql_manager.cc b/sql/sql_manager.cc index 53953c96d0b..0af6a80d4c2 100644 --- a/sql/sql_manager.cc +++ b/sql/sql_manager.cc @@ -32,7 +32,7 @@ pthread_t manager_thread; pthread_mutex_t LOCK_manager; pthread_cond_t COND_manager; -pthread_handler_decl(handle_manager,arg __attribute__((unused))) +extern "C" pthread_handler_decl(handle_manager,arg __attribute__((unused))) { int error = 0; ulong status; @@ -55,13 +55,7 @@ pthread_handler_decl(handle_manager,arg __attribute__((unused))) { if (reset_flush_time) { -#ifdef HAVE_TIMESPEC_TS_SEC - abstime.ts_sec = time(NULL)+flush_time; // Bsd 2.1 - abstime.ts_nsec = 0; -#else - abstime.tv_sec = time(NULL)+flush_time; // Linux or Solairs - abstime.tv_nsec = 0; -#endif + set_timespec(abstime, flush_time); reset_flush_time = FALSE; } while (!manager_status && !error && !abort_loop) diff --git a/sql/sql_map.cc b/sql/sql_map.cc index 4578b85d10a..e7e24f957c6 100644 --- a/sql/sql_map.cc +++ b/sql/sql_map.cc @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ diff --git a/sql/sql_map.h b/sql/sql_map.h index 34f2f755b43..632eb6e4f64 100644 --- a/sql/sql_map.h +++ b/sql/sql_map.h @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ diff --git a/sql/sql_olap.cc b/sql/sql_olap.cc new file mode 100644 index 00000000000..6eb4fbcaaf6 --- /dev/null +++ b/sql/sql_olap.cc @@ -0,0 +1,194 @@ +/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + + +/* + OLAP implementation by Sinisa Milivojevic <sinisa@mysql.com> + Inspired by code submitted by Srilakshmi <lakshmi@gdit.iiit.net> + + The ROLLUP code in this file has to be complitely rewritten as it's + not good enough to satisfy the goals of MySQL. + + In 4.1 we will replace this with a working, superior implementation + of ROLLUP. +*/ + +#ifdef DISABLED_UNTIL_REWRITTEN_IN_4_1 + +#ifdef __GNUC__ +#pragma implementation // gcc: Class implementation +#endif + +#include "mysql_priv.h" +#include "sql_select.h" + + +/**************************************************************************** + Functions that recursively actually creates new SELECT's + Returns 0 if OK, 1 if error, -1 if error already printed to client +****************************************************************************/ + + +static int make_new_olap_select(LEX *lex, SELECT_LEX *select_lex, List<Item> new_fields) +{ + THD *thd=current_thd; + Item *item, *new_item; + Item_null *constant= new Item_null("ALL"); + + SELECT_LEX *new_select = (SELECT_LEX *) thd->memdup((char*) select_lex, sizeof(*select_lex)); + if (!new_select) + return 1; + lex->last_selects->next=new_select; + new_select->linkage=OLAP_TYPE; + new_select->olap=NON_EXISTING_ONE; + new_select->group_list.elements=0; + new_select->group_list.first=(byte *)0; + new_select->group_list.next=(byte **)&new_select->group_list.first; + List<Item> privlist; + + List_iterator<Item> list_it(select_lex->item_list); + List_iterator<Item> new_it(new_fields); + + while((item=list_it++)) + { + bool not_found=true; + if (item->type()==Item::FIELD_ITEM) + { + Item_field *iif = (Item_field *)item; + new_it.rewind(); + while ((new_item=new_it++)) + { + if (new_item->type()==Item::FIELD_ITEM && + !strcmp(((Item_field*)new_item)->table_name,iif->table_name) && + !strcmp(((Item_field*)new_item)->field_name,iif->field_name)) + { + not_found= 0; + ((Item_field*)new_item)->db_name=iif->db_name; + Item_field *new_one=new Item_field(iif->db_name, iif->table_name, iif->field_name); + privlist.push_back(new_one); + if (add_to_list(new_select->group_list,new_one,1)) + return 1; + break; + } + } + } + if (not_found) + { + if (item->type() == Item::FIELD_ITEM) + privlist.push_back(constant); + else + privlist.push_back((Item*)thd->memdup((char *)item,item->size_of())); + } + } + new_select->item_list=privlist; + + lex->last_selects = new_select; + return 0; +} + +/**************************************************************************** + Functions that recursively creates combinations of queries for OLAP + Returns 0 if OK, 1 if error, -1 if error already printed to client +****************************************************************************/ + +static int olap_combos(List<Item> old_fields, List<Item> new_fields, Item *item, LEX *lex, + SELECT_LEX *select_lex, int position, int selection, int num_fields, + int num_new_fields) +{ + int sl_return = 0; + if(position == num_new_fields) + { + if(item) + new_fields.push_front(item); + sl_return = make_new_olap_select(lex, select_lex, new_fields); + } + else + { + if(item) + new_fields.push_front(item); + while ((num_fields - num_new_fields >= selection - position) && !sl_return) + { + item = old_fields.pop(); + sl_return = olap_combos(old_fields, new_fields, item, lex, select_lex, position+1, ++selection, num_fields, num_new_fields); + } + } + return sl_return; +} + + +/**************************************************************************** + Top level function for converting OLAP clauses to multiple selects + This is also a place where clauses treatment depends on OLAP type + Returns 0 if OK, 1 if error, -1 if error already printed to client +****************************************************************************/ + +int handle_olaps(LEX *lex, SELECT_LEX *select_lex) +{ + List<Item> item_list_copy, new_item_list; + item_list_copy.empty(); + new_item_list.empty(); + int count=select_lex->group_list.elements; + int sl_return=0; + +// a fix for UNION's + for (TABLE_LIST *cursor= (TABLE_LIST *)select_lex->table_list.first; + cursor; + cursor=cursor->next) + { + if (cursor->do_redirect) + { + cursor->table= ((TABLE_LIST*) cursor->table)->table; + cursor->do_redirect= 0; + } + } + + lex->last_selects=select_lex; + + for (ORDER *order=(ORDER *)select_lex->group_list.first ; order ; order=order->next) + item_list_copy.push_back(*(order->item)); + + List<Item> all_fields(select_lex->item_list); + + + if (setup_tables((TABLE_LIST *)select_lex->table_list.first) || + setup_fields(lex->thd,(TABLE_LIST *)select_lex->table_list.first,select_lex->item_list,1,&all_fields,1) || + setup_fields(lex->thd,(TABLE_LIST *)select_lex->table_list.first,item_list_copy,1,&all_fields,1)) + return -1; + + if (select_lex->olap == CUBE_TYPE) + { + for( int i=count-1; i>=0 && !sl_return; i--) + sl_return=olap_combos(item_list_copy, new_item_list, (Item *)0, lex, select_lex, 0, 0, count, i); + } + else if (select_lex->olap == ROLLUP_TYPE) + { + for( int i=count-1; i>=0 && !sl_return; i--) + { + Item *item; + item_list_copy.pop(); + List_iterator<Item> it(item_list_copy); + new_item_list.empty(); + while ((item = it++)) + new_item_list.push_front(item); + sl_return=make_new_olap_select(lex, select_lex, new_item_list); + } + } + else + sl_return=1; // impossible + return sl_return; +} + +#endif /* DISABLED_UNTIL_REWRITTEN_IN_4_1 */ diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 8daba09174e..6aa9640a579 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2003 MySQL AB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -14,22 +14,42 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - #include "mysql_priv.h" #include "sql_acl.h" #include "sql_repl.h" +#include "repl_failsafe.h" #include <m_ctype.h> -#include <thr_alarm.h> #include <myisam.h> #include <my_dir.h> #include <assert.h> #ifdef HAVE_INNOBASE_DB -#include "ha_innobase.h" +#include "ha_innodb.h" #endif +#ifdef HAVE_OPENSSL +/* + Without SSL the handshake consists of one packet. This packet + has both client capabilites and scrambled password. + With SSL the handshake might consist of two packets. If the first + packet (client capabilities) has CLIENT_SSL flag set, we have to + switch to SSL and read the second packet. The scrambled password + is in the second packet and client_capabilites field will be ignored. + Maybe it is better to accept flags other than CLIENT_SSL from the + second packet? +*/ +#define SSL_HANDSHAKE_SIZE 2 +#define NORMAL_HANDSHAKE_SIZE 6 +#define MIN_HANDSHAKE_SIZE 2 +#else +#define MIN_HANDSHAKE_SIZE 6 +#endif /* HAVE_OPENSSL */ #define SCRAMBLE_LENGTH 8 +#define MEM_ROOT_BLOCK_SIZE 8192 +#define MEM_ROOT_PREALLOC 8192 +#define TRANS_MEM_ROOT_BLOCK_SIZE 4096 +#define TRANS_MEM_ROOT_PREALLOC 4096 extern int yyparse(void); extern "C" pthread_mutex_t THR_LOCK_keycache; @@ -37,16 +57,17 @@ extern "C" pthread_mutex_t THR_LOCK_keycache; extern "C" int gethostname(char *name, int namelen); #endif -static int check_for_max_user_connections(const char *user, int u_length, - const char *host); -static void decrease_user_connections(const char *user, const char *host); +static int check_for_max_user_connections(USER_CONN *uc); +static void decrease_user_connections(USER_CONN *uc); static bool check_db_used(THD *thd,TABLE_LIST *tables); static bool check_merge_table_access(THD *thd, char *db, TABLE_LIST *tables); -static bool check_dup(THD *thd,const char *db,const char *name, - TABLE_LIST *tables); +static bool check_dup(const char *db, const char *name, TABLE_LIST *tables); static void mysql_init_query(THD *thd); static void remove_escape(char *name); static void refresh_status(void); +static bool append_file_to_dir(THD *thd, char **filename_ptr, + char *table_name); +static bool create_total_list(THD *thd, LEX *lex, TABLE_LIST **result); const char *any_db="*any*"; // Special symbol for check_access @@ -54,15 +75,12 @@ const char *command_name[]={ "Sleep", "Quit", "Init DB", "Query", "Field List", "Create DB", "Drop DB", "Refresh", "Shutdown", "Statistics", "Processlist", "Connect","Kill","Debug","Ping","Time","Delayed_insert","Change user", - "Binlog Dump","Table Dump", "Connect Out" + "Binlog Dump","Table Dump", "Connect Out", "Register Slave", + "Error" // Last command number }; bool volatile abort_slave = 0; -#ifdef HAVE_OPENSSL -extern VioSSLAcceptorFd* ssl_acceptor_fd; -#endif /* HAVE_OPENSSL */ - #ifdef __WIN__ static void test_signal(int sig_ptr) { @@ -70,22 +88,32 @@ static void test_signal(int sig_ptr) MessageBox(NULL,"Test signal","DBUG",MB_OK); #endif #if defined(OS2) - fprintf( stderr, "Test signal %d\n", sig_ptr); - fflush( stderr); + fprintf(stderr, "Test signal %d\n", sig_ptr); + fflush(stderr); #endif } static void init_signals(void) { int signals[7] = {SIGINT,SIGILL,SIGFPE,SIGSEGV,SIGTERM,SIGBREAK,SIGABRT } ; - for(int i=0 ; i < 7 ; i++) + for (int i=0 ; i < 7 ; i++) signal( signals[i], test_signal) ; } #endif -inline bool end_active_trans(THD *thd) +static void unlock_locked_tables(THD *thd) +{ + if (thd->locked_tables) + { + thd->lock=thd->locked_tables; + thd->locked_tables=0; // Will be automaticly closed + close_thread_tables(thd); // Free tables + } +} + +static bool end_active_trans(THD *thd) { int error=0; - if (thd->options & (OPTION_NOT_AUTO_COMMIT | OPTION_BEGIN | + if (thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN | OPTION_TABLE_LOCK)) { thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE); @@ -97,10 +125,68 @@ inline bool end_active_trans(THD *thd) } +static HASH hash_user_connections; +extern pthread_mutex_t LOCK_user_conn; + +static int get_or_create_user_conn(THD *thd, const char *user, + const char *host, + USER_RESOURCES *mqh) +{ + int return_val=0; + uint temp_len, user_len, host_len; + char temp_user[USERNAME_LENGTH+HOSTNAME_LENGTH+2]; + struct user_conn *uc; + + DBUG_ASSERT(user != 0); + DBUG_ASSERT(host != 0); + + user_len=strlen(user); + host_len=strlen(host); + temp_len= (strmov(strmov(temp_user, user)+1, host) - temp_user)+1; + (void) pthread_mutex_lock(&LOCK_user_conn); + if (!(uc = (struct user_conn *) hash_search(&hash_user_connections, + (byte*) temp_user, temp_len))) + { + /* First connection for user; Create a user connection object */ + if (!(uc= ((struct user_conn*) + my_malloc(sizeof(struct user_conn) + temp_len+1, + MYF(MY_WME))))) + { + send_error(¤t_thd->net, 0, NullS); // Out of memory + return_val=1; + goto end; + } + uc->user=(char*) (uc+1); + memcpy(uc->user,temp_user,temp_len+1); + uc->user_len= user_len; + uc->host=uc->user + uc->user_len + 1; + uc->len = temp_len; + uc->connections = 1; + uc->questions=uc->updates=uc->conn_per_hour=0; + uc->user_resources=*mqh; + if (max_user_connections && mqh->connections > max_user_connections) + uc->user_resources.connections = max_user_connections; + uc->intime=thd->thr_create_time; + if (hash_insert(&hash_user_connections, (byte*) uc)) + { + my_free((char*) uc,0); + send_error(¤t_thd->net, 0, NullS); // Out of memory + return_val=1; + goto end; + } + } + thd->user_connect=uc; +end: + (void) pthread_mutex_unlock(&LOCK_user_conn); + return return_val; + +} + + /* -** Check if user is ok -** Updates: -** thd->user, thd->master_access, thd->priv_user, thd->db, thd->db_access + Check if user is ok + Updates: + thd->user, thd->master_access, thd->priv_user, thd->db, thd->db_access */ static bool check_user(THD *thd,enum_server_command command, const char *user, @@ -108,6 +194,8 @@ static bool check_user(THD *thd,enum_server_command command, const char *user, { NET *net= &thd->net; thd->db=0; + thd->db_length=0; + USER_RESOURCES ur; if (passwd[0] && strlen(passwd) != SCRAMBLE_LENGTH) return 1; @@ -116,26 +204,26 @@ static bool check_user(THD *thd,enum_server_command command, const char *user, send_error(net,ER_OUT_OF_RESOURCES); return 1; } - thd->master_access=acl_getroot(thd->host, thd->ip, thd->user, + thd->master_access=acl_getroot(thd, thd->host, thd->ip, thd->user, passwd, thd->scramble, &thd->priv_user, protocol_version == 9 || !(thd->client_capabilities & - CLIENT_LONG_PASSWORD)); - DBUG_PRINT("general", - ("Capabilities: %d packet_length: %d Host: '%s' User: '%s' Using password: %s Access: %u db: '%s'", - thd->client_capabilities, thd->max_packet_length, - thd->host ? thd->host : thd->ip, thd->priv_user, + CLIENT_LONG_PASSWORD),&ur); + DBUG_PRINT("info", + ("Capabilities: %d packet_length: %ld Host: '%s' User: '%s' Using password: %s Access: %u db: '%s'", + thd->client_capabilities, thd->max_client_packet_length, + thd->host_or_ip, thd->priv_user, passwd[0] ? "yes": "no", thd->master_access, thd->db ? thd->db : "*none*")); if (thd->master_access & NO_ACCESS) { net_printf(net, ER_ACCESS_DENIED_ERROR, thd->user, - thd->host ? thd->host : thd->ip, + thd->host_or_ip, passwd[0] ? ER(ER_YES) : ER(ER_NO)); mysql_log.write(thd,COM_CONNECT,ER(ER_ACCESS_DENIED_ERROR), thd->user, - thd->host ? thd->host : thd->ip ? thd->ip : "unknown ip", + thd->host_or_ip, passwd[0] ? ER(ER_YES) : ER(ER_NO)); return(1); // Error already given } @@ -143,7 +231,7 @@ static bool check_user(THD *thd,enum_server_command command, const char *user, { VOID(pthread_mutex_lock(&LOCK_thread_count)); bool tmp=(thread_count - delayed_insert_threads >= max_connections && - !(thd->master_access & PROCESS_ACL)); + !(thd->master_access & SUPER_ACL)); VOID(pthread_mutex_unlock(&LOCK_thread_count)); if (tmp) { // Too many connections @@ -156,17 +244,21 @@ static bool check_user(THD *thd,enum_server_command command, const char *user, (char*) "%s@%s on %s" : (char*) "%s@%s as anonymous on %s"), user, - thd->host ? thd->host : thd->ip ? thd->ip : "unknown ip", + thd->host_or_ip, db ? db : (char*) ""); thd->db_access=0; - if (max_user_connections && - check_for_max_user_connections(user, strlen(user), thd->host)) + /* Don't allow user to connect if he has done too many queries */ + if ((ur.questions || ur.updates || ur.connections) && + get_or_create_user_conn(thd,user,thd->host_or_ip,&ur)) + return -1; + if (thd->user_connect && thd->user_connect->user_resources.connections && + check_for_max_user_connections(thd->user_connect)) return -1; if (db && db[0]) { bool error=test(mysql_change_db(thd,db)); - if (error) - decrease_user_connections(thd->user,thd->host); + if (error && thd->user_connect) + decrease_user_connections(thd->user_connect); return error; } else @@ -174,130 +266,69 @@ static bool check_user(THD *thd,enum_server_command command, const char *user, return 0; // ok } + /* -** check for maximum allowable user connections -** if mysql server is started with corresponding -** variable that is greater then 0 + Check for maximum allowable user connections, if the mysqld server is + started with corresponding variable that is greater then 0. */ -static HASH hash_user_connections; -extern pthread_mutex_t LOCK_user_conn; - -struct user_conn { - char *user; - uint len, connections; -}; - -static byte* get_key_conn(user_conn *buff, uint *length, - my_bool not_used __attribute__((unused))) +extern "C" byte *get_key_conn(user_conn *buff, uint *length, + my_bool not_used __attribute__((unused))) { *length=buff->len; return (byte*) buff->user; } -#define DEF_USER_COUNT 50 - -static void free_user(struct user_conn *uc) +extern "C" void free_user(struct user_conn *uc) { my_free((char*) uc,MYF(0)); } void init_max_user_conn(void) { - (void) hash_init(&hash_user_connections,DEF_USER_COUNT,0,0, - (hash_get_key) get_key_conn, (void (*)(void*)) free_user, + (void) hash_init(&hash_user_connections,max_connections,0,0, + (hash_get_key) get_key_conn, (hash_free_key) free_user, 0); } -static int check_for_max_user_connections(const char *user, int u_length, - const char *host) +static int check_for_max_user_connections(USER_CONN *uc) { - int error=1; - uint temp_len; - char temp_user[USERNAME_LENGTH+HOSTNAME_LENGTH+2]; - struct user_conn *uc; - if (!user) - user=""; - if (!host) - host=""; + int error=0; DBUG_ENTER("check_for_max_user_connections"); - DBUG_PRINT("enter",("user: '%s' host: '%s'", user, host)); - - temp_len= (uint) (strxnmov(temp_user, sizeof(temp_user), user, "@", host, - NullS) - temp_user); - (void) pthread_mutex_lock(&LOCK_user_conn); - uc = (struct user_conn *) hash_search(&hash_user_connections, - (byte*) temp_user, temp_len); - if (uc) /* user found ; check for no. of connections */ + + if (max_user_connections && + (max_user_connections <= (uint) uc->connections)) { - if (max_user_connections == (uint) uc->connections) - { - net_printf(&(current_thd->net),ER_TOO_MANY_USER_CONNECTIONS, temp_user); - goto end; - } - uc->connections++; + net_printf(&(current_thd->net),ER_TOO_MANY_USER_CONNECTIONS, uc->user); + error=1; + goto end; } - else + uc->connections++; + if (uc->user_resources.connections && + uc->conn_per_hour++ >= uc->user_resources.connections) { - /* the user is not found in the cache; Insert it */ - struct user_conn *uc= ((struct user_conn*) - my_malloc(sizeof(struct user_conn) + temp_len+1, - MYF(MY_WME))); - if (!uc) - { - send_error(¤t_thd->net, 0, NullS); // Out of memory - goto end; - } - uc->user=(char*) (uc+1); - memcpy(uc->user,temp_user,temp_len+1); - uc->len = temp_len; - uc->connections = 1; - if (hash_insert(&hash_user_connections, (byte*) uc)) - { - my_free((char*) uc,0); - send_error(¤t_thd->net, 0, NullS); // Out of memory - goto end; - } + net_printf(¤t_thd->net, ER_USER_LIMIT_REACHED, uc->user, + "max_connections", + (long) uc->user_resources.connections); + error=1; + goto end; } - error=0; - end: - (void) pthread_mutex_unlock(&LOCK_user_conn); DBUG_RETURN(error); } -static void decrease_user_connections(const char *user, const char *host) +static void decrease_user_connections(USER_CONN *uc) { - char temp_user[USERNAME_LENGTH+HOSTNAME_LENGTH+2]; - int temp_len; - struct user_conn *uc; - if (!max_user_connections) - return; - if (!user) - user=""; - if (!host) - host=""; DBUG_ENTER("decrease_user_connections"); - DBUG_PRINT("enter",("user: '%s' host: '%s'", user, host)); - - temp_len= (uint) (strxnmov(temp_user, sizeof(temp_user), user, "@", host, - NullS) - temp_user); - (void) pthread_mutex_lock(&LOCK_user_conn); - - uc = (struct user_conn *) hash_search(&hash_user_connections, - (byte*) temp_user, temp_len); - dbug_assert(uc != 0); // We should always find the user - if (!uc) - goto end; // Safety; Something went wrong - if (! --uc->connections) + if ((uc->connections && !--uc->connections) && !mqh_used) { /* Last connection for user; Delete it */ + (void) pthread_mutex_lock(&LOCK_user_conn); (void) hash_delete(&hash_user_connections,(byte*) uc); + (void) pthread_mutex_unlock(&LOCK_user_conn); } -end: - (void) pthread_mutex_unlock(&LOCK_user_conn); DBUG_VOID_RETURN; } @@ -309,31 +340,153 @@ void free_max_user_conn(void) /* -** check connnetion and get priviliges -** returns 0 on ok, -1 < if error is given > 0 on error. + Mark all commands that somehow changes a table + This is used to check number of updates / hour +*/ + +char uc_update_queries[SQLCOM_END]; + +void init_update_queries(void) +{ + uc_update_queries[SQLCOM_CREATE_TABLE]=1; + uc_update_queries[SQLCOM_CREATE_INDEX]=1; + uc_update_queries[SQLCOM_ALTER_TABLE]=1; + uc_update_queries[SQLCOM_UPDATE]=1; + uc_update_queries[SQLCOM_INSERT]=1; + uc_update_queries[SQLCOM_INSERT_SELECT]=1; + uc_update_queries[SQLCOM_DELETE]=1; + uc_update_queries[SQLCOM_TRUNCATE]=1; + uc_update_queries[SQLCOM_DROP_TABLE]=1; + uc_update_queries[SQLCOM_LOAD]=1; + uc_update_queries[SQLCOM_CREATE_DB]=1; + uc_update_queries[SQLCOM_DROP_DB]=1; + uc_update_queries[SQLCOM_REPLACE]=1; + uc_update_queries[SQLCOM_REPLACE_SELECT]=1; + uc_update_queries[SQLCOM_RENAME_TABLE]=1; + uc_update_queries[SQLCOM_BACKUP_TABLE]=1; + uc_update_queries[SQLCOM_RESTORE_TABLE]=1; + uc_update_queries[SQLCOM_DELETE_MULTI]=1; + uc_update_queries[SQLCOM_DROP_INDEX]=1; + uc_update_queries[SQLCOM_MULTI_UPDATE]=1; +} + + +/* + Check if maximum queries per hour limit has been reached + returns 0 if OK. + + In theory we would need a mutex in the USER_CONN structure for this to + be 100 % safe, but as the worst scenario is that we would miss counting + a couple of queries, this isn't critical. */ +static bool check_mqh(THD *thd, uint check_command) +{ + bool error=0; + time_t check_time = thd->start_time ? thd->start_time : time(NULL); + USER_CONN *uc=thd->user_connect; + DBUG_ENTER("check_mqh"); + DBUG_ASSERT(uc != 0); + + /* If more than a hour since last check, reset resource checking */ + if (check_time - uc->intime >= 3600) + { + (void) pthread_mutex_lock(&LOCK_user_conn); + uc->questions=1; + uc->updates=0; + uc->conn_per_hour=0; + uc->intime=check_time; + (void) pthread_mutex_unlock(&LOCK_user_conn); + } + /* Check that we have not done too many questions / hour */ + if (uc->user_resources.questions && + uc->questions++ >= uc->user_resources.questions) + { + net_printf(&thd->net, ER_USER_LIMIT_REACHED, uc->user, "max_questions", + (long) uc->user_resources.questions); + error=1; + goto end; + } + if (check_command < (uint) SQLCOM_END) + { + /* Check that we have not done too many updates / hour */ + if (uc->user_resources.updates && uc_update_queries[check_command] && + uc->updates++ >= uc->user_resources.updates) + { + net_printf(&thd->net, ER_USER_LIMIT_REACHED, uc->user, "max_updates", + (long) uc->user_resources.updates); + error=1; + goto end; + } + } +end: + DBUG_RETURN(error); +} + + +static void reset_mqh(THD *thd, LEX_USER *lu, bool get_them= 0) +{ + + (void) pthread_mutex_lock(&LOCK_user_conn); + if (lu) // for GRANT + { + USER_CONN *uc; + uint temp_len=lu->user.length+lu->host.length+2; + char temp_user[USERNAME_LENGTH+HOSTNAME_LENGTH+2]; + + memcpy(temp_user,lu->user.str,lu->user.length); + memcpy(temp_user+lu->user.length+1,lu->host.str,lu->host.length); + temp_user[lu->user.length]='\0'; temp_user[temp_len-1]=0; + if ((uc = (struct user_conn *) hash_search(&hash_user_connections, + (byte*) temp_user, temp_len))) + { + uc->questions=0; + get_mqh(temp_user,&temp_user[lu->user.length+1],uc); + uc->updates=0; + uc->conn_per_hour=0; + } + } + else // for FLUSH PRIVILEGES and FLUSH USER_RESOURCES + { + for (uint idx=0;idx < hash_user_connections.records; idx++) + { + USER_CONN *uc=(struct user_conn *) hash_element(&hash_user_connections, idx); + if (get_them) + get_mqh(uc->user,uc->host,uc); + uc->questions=0; + uc->updates=0; + uc->conn_per_hour=0; + } + } + (void) pthread_mutex_unlock(&LOCK_user_conn); +} + + +/* + Check connnetion and get priviliges + Returns 0 on ok, -1 < if error is given > 0 on error. +*/ + static int check_connections(THD *thd) { uint connect_errors=0; NET *net= &thd->net; - /* - ** store the connection details - */ + /* Store the connection details */ DBUG_PRINT("info", (("check_connections called by thread %d"), thd->thread_id)); - DBUG_PRINT("general",("New connection received on %s", + DBUG_PRINT("info",("New connection received on %s", vio_description(net->vio))); if (!thd->host) // If TCP/IP connection { char ip[30]; - if (vio_peer_addr(net->vio,ip)) + if (vio_peer_addr(net->vio, ip, &thd->peer_port)) return (ER_BAD_HOST_ERROR); if (!(thd->ip = my_strdup(ip,MYF(0)))) return (ER_OUT_OF_RESOURCES); + thd->host_or_ip=thd->ip; #if !defined(HAVE_SYS_UN_H) || defined(HAVE_mit_thread) /* Fast local hostname resolve for Win32 */ if (!strcmp(thd->ip,"127.0.0.1")) @@ -344,68 +497,58 @@ check_connections(THD *thd) { vio_in_addr(net->vio,&thd->remote.sin_addr); thd->host=ip_to_hostname(&thd->remote.sin_addr,&connect_errors); + /* Cut very long hostnames to avoid possible overflows */ + if (thd->host) + thd->host[min(strlen(thd->host), HOSTNAME_LENGTH)]= 0; if (connect_errors > max_connect_errors) return(ER_HOST_IS_BLOCKED); } - DBUG_PRINT("general",("Host: %s ip: %s", - thd->host ? thd->host : "unknown host", - thd->ip ? thd->ip : "unknown ip")); + DBUG_PRINT("info",("Host: %s ip: %s", + thd->host ? thd->host : "unknown host", + thd->ip ? thd->ip : "unknown ip")); if (acl_check_host(thd->host,thd->ip)) return(ER_HOST_NOT_PRIVILEGED); } else /* Hostname given means that the connection was on a socket */ { - DBUG_PRINT("general",("Host: %s",thd->host)); - thd->ip=0; + DBUG_PRINT("info",("Host: %s",thd->host)); + thd->host_or_ip= thd->host; + thd->ip= 0; + thd->peer_port= 0; bzero((char*) &thd->remote,sizeof(struct sockaddr)); } + /* Ensure that wrong hostnames doesn't cause buffer overflows */ vio_keepalive(net->vio, TRUE); - /* nasty, but any other way? */ - uint pkt_len = 0; + ulong pkt_len=0; { /* buff[] needs to big enough to hold the server_version variable */ char buff[SERVER_VERSION_LENGTH + SCRAMBLE_LENGTH+32],*end; int client_flags = CLIENT_LONG_FLAG | CLIENT_CONNECT_WITH_DB; + if (opt_using_transactions) client_flags|=CLIENT_TRANSACTIONS; #ifdef HAVE_COMPRESS client_flags |= CLIENT_COMPRESS; #endif /* HAVE_COMPRESS */ +#ifdef HAVE_OPENSSL + if (ssl_acceptor_fd) + client_flags |= CLIENT_SSL; /* Wow, SSL is avalaible! */ +#endif /* HAVE_OPENSSL */ - end=strmov(buff,server_version)+1; + end=strnmov(buff,server_version,SERVER_VERSION_LENGTH)+1; int4store((uchar*) end,thd->thread_id); end+=4; memcpy(end,thd->scramble,SCRAMBLE_LENGTH+1); end+=SCRAMBLE_LENGTH +1; -#ifdef HAVE_OPENSSL - if (ssl_acceptor_fd) - client_flags |= CLIENT_SSL; /* Wow, SSL is avalaible! */ - /* - * Without SSL the handshake consists of one packet. This packet - * has both client capabilites and scrambled password. - * With SSL the handshake might consist of two packets. If the first - * packet (client capabilities) has CLIENT_SSL flag set, we have to - * switch to SSL and read the second packet. The scrambled password - * is in the second packet and client_capabilites field will be ignored. - * Maybe it is better to accept flags other than CLIENT_SSL from the - * second packet? - */ -#define SSL_HANDSHAKE_SIZE 2 -#define NORMAL_HANDSHAKE_SIZE 6 -#define MIN_HANDSHAKE_SIZE 2 - -#else -#define MIN_HANDSHAKE_SIZE 6 -#endif /* HAVE_OPENSSL */ int2store(end,client_flags); - end[2]=MY_CHARSET_CURRENT; + end[2]=(char) MY_CHARSET_CURRENT; int2store(end+3,thd->server_status); bzero(end+5,13); end+=18; - if (net_write_command(net,protocol_version, buff, + if (net_write_command(net,(uchar) protocol_version, buff, (uint) (end-buff)) || - (pkt_len=my_net_read(net)) == packet_error || + (pkt_len= my_net_read(net)) == packet_error || pkt_len < MIN_HANDSHAKE_SIZE) { inc_host_errors(&thd->remote.sin_addr); @@ -417,30 +560,31 @@ check_connections(THD *thd) #endif if (connect_errors) reset_host_errors(&thd->remote.sin_addr); - if (thd->packet.alloc(net_buffer_length)) + if (thd->packet.alloc(thd->variables.net_buffer_length)) return(ER_OUT_OF_RESOURCES); thd->client_capabilities=uint2korr(net->read_pos); if (thd->client_capabilities & CLIENT_IGNORE_SPACE) thd->sql_mode|= MODE_IGNORE_SPACE; #ifdef HAVE_OPENSSL - DBUG_PRINT("info", - ("pkt_len:%d, client capabilities: %d", - pkt_len, thd->client_capabilities) ); + DBUG_PRINT("info", ("client capabilities: %d", thd->client_capabilities)); if (thd->client_capabilities & CLIENT_SSL) { - DBUG_PRINT("info", ("Agreed to change IO layer to SSL") ); /* Do the SSL layering. */ DBUG_PRINT("info", ("IO layer change in progress...")); - VioSocket* vio_socket = my_reinterpret_cast(VioSocket*)(net->vio); - VioSSL* vio_ssl = ssl_acceptor_fd->accept(vio_socket); - net->vio = my_reinterpret_cast(NetVio*) (vio_ssl); + if (sslaccept(ssl_acceptor_fd, net->vio, thd->variables.net_wait_timeout)) + { + DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)", + pkt_len)); + inc_host_errors(&thd->remote.sin_addr); + return(ER_HANDSHAKE_ERROR); + } DBUG_PRINT("info", ("Reading user information over SSL layer")); if ((pkt_len=my_net_read(net)) == packet_error || pkt_len < NORMAL_HANDSHAKE_SIZE) { - DBUG_PRINT("info", ("pkt_len:%d", pkt_len)); - DBUG_PRINT("error", ("Failed to read user information")); + DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)", + pkt_len)); inc_host_errors(&thd->remote.sin_addr); return(ER_HANDSHAKE_ERROR); } @@ -456,18 +600,18 @@ check_connections(THD *thd) } #endif - thd->max_packet_length=uint3korr(net->read_pos+2); + thd->max_client_packet_length=uint3korr(net->read_pos+2); char *user= (char*) net->read_pos+5; char *passwd= strend(user)+1; char *db=0; if (thd->client_capabilities & CLIENT_CONNECT_WITH_DB) db=strend(passwd)+1; if (thd->client_capabilities & CLIENT_INTERACTIVE) - thd->inactive_timeout=net_interactive_timeout; + thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout; if ((thd->client_capabilities & CLIENT_TRANSACTIONS) && opt_using_transactions) thd->net.return_status= &thd->server_status; - net->timeout=net_read_timeout; + net->read_timeout=(uint) thd->variables.net_read_timeout; if (check_user(thd,COM_CONNECT, user, passwd, db, 1)) return (-1); thd->password=test(passwd[0]); @@ -485,31 +629,31 @@ pthread_handler_decl(handle_one_connection,arg) pthread_detach_this_thread(); -#if !defined( __WIN__) && !defined(OS2) /* Win32 calls this in pthread_create */ - if (my_thread_init()) // needed to be called first before we call - // DBUG_ macros +#if !defined( __WIN__) && !defined(OS2) // Win32 calls this in pthread_create + // The following calls needs to be done before we call DBUG_ macros + if (!(test_flags & TEST_NO_THREADS) & my_thread_init()) { close_connection(&thd->net,ER_OUT_OF_RESOURCES); - statistic_increment(aborted_connects,&LOCK_thread_count); + statistic_increment(aborted_connects,&LOCK_status); end_thread(thd,0); return 0; } #endif - // handle_one_connection() is the only way a thread would start - // and would always be on top of the stack - // therefore, the thread stack always starts at the address of the first - // local variable of handle_one_connection, which is thd - // we need to know the start of the stack so that we could check for - // stack overruns - + /* + handle_one_connection() is the only way a thread would start + and would always be on top of the stack, therefore, the thread + stack always starts at the address of the first local variable + of handle_one_connection, which is thd. We need to know the + start of the stack so that we could check for stack overruns. + */ DBUG_PRINT("info", ("handle_one_connection called by thread %d\n", thd->thread_id)); // now that we've called my_thread_init(), it is safe to call DBUG_* #if defined(__WIN__) init_signals(); // IRENA; testing ? -#elif !defined(OS2) +#elif !defined(OS2) && !defined(__NETWARE__) sigset_t set; VOID(sigemptyset(&set)); // Get mask in use VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals)); @@ -517,7 +661,7 @@ pthread_handler_decl(handle_one_connection,arg) if (thd->store_globals()) { close_connection(&thd->net,ER_OUT_OF_RESOURCES); - statistic_increment(aborted_connects,&LOCK_thread_count); + statistic_increment(aborted_connects,&LOCK_status); end_thread(thd,0); return 0; } @@ -526,24 +670,23 @@ pthread_handler_decl(handle_one_connection,arg) { int error; NET *net= &thd->net; - - thd->mysys_var=my_thread_var; - thd->dbug_thread_id=my_thread_id(); thd->thread_stack= (char*) &thd; if ((error=check_connections(thd))) { // Wrong permissions if (error > 0) - net_printf(net,error,thd->host ? thd->host : (thd->ip ? thd->ip : "")); + net_printf(net,error,thd->host_or_ip); #ifdef __NT__ if (vio_type(net->vio) == VIO_TYPE_NAMEDPIPE) sleep(1); /* must wait after eof() */ #endif - statistic_increment(aborted_connects,&LOCK_thread_count); + statistic_increment(aborted_connects,&LOCK_status); goto end_thread; } - - if (thd->max_join_size == HA_POS_ERROR) +#ifdef __NETWARE__ + netware_reg_user(thd->ip, thd->user, "MySQL"); +#endif + if (thd->variables.max_join_size == HA_POS_ERROR) thd->options |= OPTION_BIG_SELECTS; if (thd->client_capabilities & CLIENT_COMPRESS) net->compress=1; // Use compression @@ -552,27 +695,30 @@ pthread_handler_decl(handle_one_connection,arg) thd->command=COM_SLEEP; thd->version=refresh_version; thd->set_time(); - init_sql_alloc(&thd->mem_root,8192,8192); + init_sql_alloc(&thd->mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC); + init_sql_alloc(&thd->transaction.mem_root, + TRANS_MEM_ROOT_BLOCK_SIZE, TRANS_MEM_ROOT_PREALLOC); while (!net->error && net->vio != 0 && !thd->killed) { if (do_command(thd)) break; } + if (thd->user_connect) + decrease_user_connections(thd->user_connect); free_root(&thd->mem_root,MYF(0)); if (net->error && net->vio != 0) { - if (!thd->killed && opt_warnings) - sql_print_error(ER(ER_NEW_ABORTING_CONNECTION), - thd->thread_id,(thd->db ? thd->db : "unconnected"), - thd->user ? thd->user : "unauthenticated", - (thd->host ? thd->host : thd->ip ? thd->ip : "unknown"), - (net->last_errno ? ER(net->last_errno) : - ER(ER_UNKNOWN_ERROR))); + if (!thd->killed && thd->variables.log_warnings) + sql_print_error(ER(ER_NEW_ABORTING_CONNECTION), + thd->thread_id,(thd->db ? thd->db : "unconnected"), + thd->user ? thd->user : "unauthenticated", + thd->host_or_ip, + (net->last_errno ? ER(net->last_errno) : + ER(ER_UNKNOWN_ERROR))); send_error(net,net->last_errno,NullS); - thread_safe_increment(aborted_threads,&LOCK_thread_count); + statistic_increment(aborted_threads,&LOCK_status); } - - decrease_user_connections(thd->user,thd->host); + end_thread: close_connection(net); end_thread(thd,1); @@ -591,7 +737,7 @@ end_thread: Used when creating the initial grant tables */ -pthread_handler_decl(handle_bootstrap,arg) +extern "C" pthread_handler_decl(handle_bootstrap,arg) { THD *thd=(THD*) arg; FILE *file=bootstrap_file; @@ -608,23 +754,23 @@ pthread_handler_decl(handle_bootstrap,arg) pthread_detach_this_thread(); thd->thread_stack= (char*) &thd; - thd->mysys_var=my_thread_var; - thd->dbug_thread_id=my_thread_id(); -#if !defined(__WIN__) && !defined(OS2) +#if !defined(__WIN__) && !defined(OS2) && !defined(__NETWARE__) sigset_t set; VOID(sigemptyset(&set)); // Get mask in use VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals)); #endif - if (thd->max_join_size == (ulong) ~0L) + if (thd->variables.max_join_size == HA_POS_ERROR) thd->options |= OPTION_BIG_SELECTS; thd->proc_info=0; thd->version=refresh_version; - thd->priv_user=thd->user=(char*)"boot"; + thd->priv_user=thd->user=(char*) my_strdup("boot", MYF(MY_WME)); buff= (char*) thd->net.buff; - init_sql_alloc(&thd->mem_root,8192,8192); + init_sql_alloc(&thd->mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC); + init_sql_alloc(&thd->transaction.mem_root, + TRANS_MEM_ROOT_BLOCK_SIZE, TRANS_MEM_ROOT_PREALLOC); while (fgets(buff, thd->net.max_packet, file)) { uint length=(uint) strlen(buff); @@ -632,22 +778,31 @@ pthread_handler_decl(handle_bootstrap,arg) length--; buff[length]=0; thd->current_tablenr=0; - thd->query= thd->memdup(buff,length+1); + thd->query_length=length; + thd->query= thd->memdup_w_gap(buff, length+1, thd->db_length+1); + thd->query[length] = '\0'; thd->query_id=query_id++; + if (mqh_used && thd->user_connect && check_mqh(thd, SQLCOM_END)) + { + thd->net.error = 0; + close_thread_tables(thd); // Free tables + free_root(&thd->mem_root,MYF(MY_KEEP_PREALLOC)); + break; + } mysql_parse(thd,thd->query,length); close_thread_tables(thd); // Free tables if (thd->fatal_error) break; free_root(&thd->mem_root,MYF(MY_KEEP_PREALLOC)); + free_root(&thd->transaction.mem_root,MYF(MY_KEEP_PREALLOC)); } - thd->priv_user=thd->user=0; /* thd->fatal_error should be set in case something went wrong */ end: (void) pthread_mutex_lock(&LOCK_thread_count); thread_count--; - (void) pthread_cond_broadcast(&COND_thread_count); (void) pthread_mutex_unlock(&LOCK_thread_count); + (void) pthread_cond_broadcast(&COND_thread_count); my_thread_end(); pthread_exit(0); DBUG_RETURN(0); // Never reached @@ -668,7 +823,7 @@ int mysql_table_dump(THD* thd, char* db, char* tbl_name, int fd) int error = 0; DBUG_ENTER("mysql_table_dump"); db = (db && db[0]) ? db : thd->db; - if (!(table_list = (TABLE_LIST*) sql_calloc(sizeof(TABLE_LIST)))) + if (!(table_list = (TABLE_LIST*) thd->calloc(sizeof(TABLE_LIST)))) DBUG_RETURN(1); // out of memory table_list->db = db; table_list->real_name = table_list->alias = tbl_name; @@ -693,6 +848,7 @@ int mysql_table_dump(THD* thd, char* db, char* tbl_name, int fd) goto err; thd->free_list = 0; + thd->query_length=(uint) strlen(tbl_name); thd->query = tbl_name; if ((error = mysqld_dump_create_info(thd, table, -1))) { @@ -700,14 +856,11 @@ int mysql_table_dump(THD* thd, char* db, char* tbl_name, int fd) goto err; } net_flush(&thd->net); - error = table->file->dump(thd,fd); - if (error) + if ((error= table->file->dump(thd,fd))) my_error(ER_GET_ERRNO, MYF(0)); err: - close_thread_tables(thd); - DBUG_RETURN(error); } @@ -717,67 +870,95 @@ err: bool do_command(THD *thd) { char *packet; - uint old_timeout,packet_length; - bool error=0; + uint old_timeout; + ulong packet_length; NET *net; enum enum_server_command command; - // commands which will always take a long time should be marked with - // this so that they will not get logged to the slow query log - bool slow_command=FALSE; DBUG_ENTER("do_command"); net= &thd->net; thd->current_tablenr=0; packet=0; - old_timeout=net->timeout; - net->timeout=thd->inactive_timeout; /* Wait max for 8 hours */ + old_timeout=net->read_timeout; + // Wait max for 8 hours + net->read_timeout=(uint) thd->variables.net_wait_timeout; net->last_error[0]=0; // Clear error message net->last_errno=0; net_new_transaction(net); if ((packet_length=my_net_read(net)) == packet_error) { - DBUG_PRINT("general",("Got error reading command from socket %s", - vio_description(net->vio) )); - return TRUE; + DBUG_PRINT("info",("Got error %d reading command from socket %s", + net->error, + vio_description(net->vio))); + /* Check if we can continue without closing the connection */ + if (net->error != 3) + DBUG_RETURN(TRUE); // We have to close it. + send_error(net,net->last_errno,NullS); + net->error= 0; + DBUG_RETURN(FALSE); } else { packet=(char*) net->read_pos; command = (enum enum_server_command) (uchar) packet[0]; - DBUG_PRINT("general",("Command on %s = %d (%s)", - vio_description(net->vio), command, - command_name[command])); - } - net->timeout=old_timeout; /* Timeout */ + if (command >= COM_END) + command= COM_END; // Wrong command + DBUG_PRINT("info",("Command on %s = %d (%s)", + vio_description(net->vio), command, + command_name[command])); + } + net->read_timeout=old_timeout; // restore it + DBUG_RETURN(dispatch_command(command,thd, packet+1, (uint) packet_length)); +} + + +bool dispatch_command(enum enum_server_command command, THD *thd, + char* packet, uint packet_length) +{ + NET *net= &thd->net; + bool error=0; + /* + Commands which will always take a long time should be marked with + this so that they will not get logged to the slow query log + */ + bool slow_command=FALSE; + DBUG_ENTER("dispatch_command"); + thd->command=command; + thd->set_time(); VOID(pthread_mutex_lock(&LOCK_thread_count)); thd->query_id=query_id; if (command != COM_STATISTICS && command != COM_PING) query_id++; thread_running++; VOID(pthread_mutex_unlock(&LOCK_thread_count)); - thd->set_time(); - thd->lex.options=0; // We store status here - switch(command) { + + thd->lex.select_lex.options=0; // We store status here + switch (command) { case COM_INIT_DB: - thread_safe_increment(com_stat[SQLCOM_CHANGE_DB],&LOCK_thread_count); - if (!mysql_change_db(thd,packet+1)) + statistic_increment(com_stat[SQLCOM_CHANGE_DB],&LOCK_status); + if (!mysql_change_db(thd,packet)) mysql_log.write(thd,command,"%s",thd->db); break; + case COM_REGISTER_SLAVE: + { + if (!register_slave(thd, (uchar*)packet, packet_length)) + send_ok(&thd->net); + break; + } case COM_TABLE_DUMP: { - thread_safe_increment(com_other,&LOCK_thread_count); + statistic_increment(com_other, &LOCK_status); slow_command = TRUE; - char* data = packet + 1; - uint db_len = *(uchar *)data; - uint tbl_len = *(uchar *)(data + db_len + 1); - char* db = sql_alloc(db_len + tbl_len + 2); - memcpy(db, data + 1, db_len); + uint db_len = *(uchar*)packet; + uint tbl_len = *(uchar*)(packet + db_len + 1); + char* db = thd->alloc(db_len + tbl_len + 2); + memcpy(db, packet + 1, db_len); char* tbl_name = db + db_len; *tbl_name++ = 0; - memcpy(tbl_name, data + db_len + 2, tbl_len); + memcpy(tbl_name, packet + db_len + 2, tbl_len); tbl_name[tbl_len] = 0; if (mysql_table_dump(thd, db, tbl_name, -1)) send_error(&thd->net); // dump to NET @@ -785,18 +966,23 @@ bool do_command(THD *thd) } case COM_CHANGE_USER: { - thread_safe_increment(com_other,&LOCK_thread_count); - char *user= (char*) packet+1; + thd->change_user(); + clear_error_message(thd); // If errors from rollback + + statistic_increment(com_other,&LOCK_status); + char *user= (char*) packet; char *passwd= strend(user)+1; char *db= strend(passwd)+1; /* Save user and privileges */ uint save_master_access=thd->master_access; uint save_db_access= thd->db_access; + uint save_db_length= thd->db_length; char *save_user= thd->user; char *save_priv_user= thd->priv_user; char *save_db= thd->db; thd->user=0; + USER_CONN *save_uc= thd->user_connect; if ((uint) ((uchar*) db - net->read_pos) > packet_length) { // Check if protocol is ok @@ -809,11 +995,13 @@ bool do_command(THD *thd) thd->master_access=save_master_access; thd->db_access=save_db_access; thd->db=save_db; + thd->db_length=save_db_length; thd->user=save_user; thd->priv_user=save_priv_user; break; } - decrease_user_connections (save_user, thd->host); + if (max_connections && save_uc) + decrease_user_connections(save_uc); x_free((gptr) save_db); x_free((gptr) save_user); thd->password=test(passwd[0]); @@ -822,28 +1010,38 @@ bool do_command(THD *thd) case COM_QUERY: { + packet_length--; // Remove end null + /* Remove garage at start and end of query */ + while (isspace(packet[0]) && packet_length > 0) + { + packet++; + packet_length--; + } char *pos=packet+packet_length; // Point at end null - /* Remove garage at end of query */ - while (packet_length > 0 && pos[-1] == ';') + while (packet_length > 0 && (pos[-1] == ';' || isspace(pos[-1]))) { pos--; packet_length--; } - *pos=0; - if (!(thd->query= (char*) thd->memdup((gptr) (packet+1),packet_length))) + /* We must allocate some extra memory for query cache */ + if (!(thd->query= (char*) thd->memdup_w_gap((gptr) (packet), + packet_length, + thd->db_length+2))) break; - thd->packet.shrink(net_buffer_length); // Reclaim some memory + thd->query[packet_length]=0; + thd->packet.shrink(thd->variables.net_buffer_length);// Reclaim some memory if (!(specialflag & SPECIAL_NO_PRIOR)) my_pthread_setprio(pthread_self(),QUERY_PRIOR); mysql_log.write(thd,command,"%s",thd->query); - DBUG_PRINT("query",("%s",thd->query)); - mysql_parse(thd,thd->query,packet_length-1); + DBUG_PRINT("query",("%-.4096s",thd->query)); + /* thd->query_length is set by mysql_parse() */ + mysql_parse(thd,thd->query,packet_length); if (!(specialflag & SPECIAL_NO_PRIOR)) my_pthread_setprio(pthread_self(),WAIT_PRIOR); DBUG_PRINT("info",("query ready")); break; } - case COM_FIELD_LIST: // This isn't actually neaded + case COM_FIELD_LIST: // This isn't actually needed #ifdef DONT_ALLOW_SHOW_COMMANDS send_error(&thd->net,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ break; @@ -851,7 +1049,7 @@ bool do_command(THD *thd) { char *fields; TABLE_LIST table_list; - thread_safe_increment(com_stat[SQLCOM_SHOW_FIELDS],&LOCK_thread_count); + statistic_increment(com_stat[SQLCOM_SHOW_FIELDS],&LOCK_status); bzero((char*) &table_list,sizeof(table_list)); if (!(table_list.db=thd->db)) { @@ -859,8 +1057,11 @@ bool do_command(THD *thd) break; } thd->free_list=0; - table_list.alias= table_list.real_name= thd->strdup(packet+1); - thd->query=fields=thd->strdup(strend(packet+1)+1); + table_list.alias= table_list.real_name= thd->strdup(packet); + packet=strend(packet)+1; + // command not cachable => no gap for data base name + if (!(thd->query=fields=thd->memdup(packet,thd->query_length+1))) + break; mysql_log.write(thd,command,"%s %s",table_list.real_name,fields); if (lower_case_table_names) casedn_str(table_list.real_name); @@ -883,10 +1084,10 @@ bool do_command(THD *thd) error=TRUE; // End server break; - case COM_CREATE_DB: + case COM_CREATE_DB: // QQ: To be removed { - char *db=thd->strdup(packet+1); - thread_safe_increment(com_stat[SQLCOM_CREATE_DB],&LOCK_thread_count); + statistic_increment(com_stat[SQLCOM_CREATE_DB],&LOCK_status); + char *db=thd->strdup(packet); // null test to handle EOM if (!db || !strip_sp(db) || check_db_name(db)) { @@ -895,44 +1096,50 @@ bool do_command(THD *thd) } if (check_access(thd,CREATE_ACL,db,0,1)) break; - mysql_log.write(thd,command,packet+1); - mysql_create_db(thd,db,0); + mysql_log.write(thd,command,packet); + mysql_create_db(thd,db,0,0); break; } - case COM_DROP_DB: + case COM_DROP_DB: // QQ: To be removed { - char *db=thd->strdup(packet+1); - thread_safe_increment(com_stat[SQLCOM_DROP_DB],&LOCK_thread_count); + statistic_increment(com_stat[SQLCOM_DROP_DB],&LOCK_status); + char *db=thd->strdup(packet); // null test to handle EOM if (!db || !strip_sp(db) || check_db_name(db)) { net_printf(&thd->net,ER_WRONG_DB_NAME, db ? db : "NULL"); break; } - if (check_access(thd,DROP_ACL,db,0,1) || end_active_trans(thd)) + if (check_access(thd,DROP_ACL,db,0,1)) break; + if (thd->locked_tables || thd->active_transaction()) + { + send_error(&thd->net,ER_LOCK_OR_ACTIVE_TRANSACTION); + break; + } mysql_log.write(thd,command,db); - mysql_rm_db(thd,db,0); + mysql_rm_db(thd,db,0,0); break; } case COM_BINLOG_DUMP: { - thread_safe_increment(com_other,&LOCK_thread_count); + statistic_increment(com_other,&LOCK_status); slow_command = TRUE; - if(check_access(thd, FILE_ACL, any_db)) + if (check_global_access(thd, REPL_SLAVE_ACL)) break; mysql_log.write(thd,command, 0); ulong pos; ushort flags; uint32 slave_server_id; - pos = uint4korr(packet + 1); - flags = uint2korr(packet + 5); - pthread_mutex_lock(&LOCK_server_id); - kill_zombie_dump_threads(slave_server_id = uint4korr(packet+7)); + /* TODO: The following has to be changed to an 8 byte integer */ + pos = uint4korr(packet); + flags = uint2korr(packet + 4); + thd->server_id=0; /* avoid suicide */ + kill_zombie_dump_threads(slave_server_id = uint4korr(packet+6)); thd->server_id = slave_server_id; - pthread_mutex_unlock(&LOCK_server_id); - mysql_binlog_send(thd, thd->strdup(packet + 11), pos, flags); + mysql_binlog_send(thd, thd->strdup(packet + 10), (my_off_t) pos, flags); + unregister_slave(thd,1,1); // fake COM_QUIT -- if we get here, the thread needs to terminate error = TRUE; net->error = 0; @@ -940,20 +1147,18 @@ bool do_command(THD *thd) } case COM_REFRESH: { - uint options=(uchar) packet[1]; - thread_safe_increment(com_stat[SQLCOM_FLUSH],&LOCK_thread_count); - if (check_access(thd,RELOAD_ACL,any_db)) + statistic_increment(com_stat[SQLCOM_FLUSH],&LOCK_status); + ulong options= (ulong) (uchar) packet[0]; + if (check_global_access(thd,RELOAD_ACL)) break; mysql_log.write(thd,command,NullS); - if (reload_acl_and_cache(thd, options, (TABLE_LIST*) 0)) - send_error(net,0); - else - send_eof(net); + /* error sending is deferred to reload_acl_and_cache */ + reload_acl_and_cache(thd, options, (TABLE_LIST*) 0) ; break; } case COM_SHUTDOWN: - thread_safe_increment(com_other,&LOCK_thread_count); - if (check_access(thd,SHUTDOWN_ACL,any_db)) + statistic_increment(com_other,&LOCK_status); + if (check_global_access(thd,SHUTDOWN_ACL)) break; /* purecov: inspected */ DBUG_PRINT("quit",("Got shutdown command")); mysql_log.write(thd,command,NullS); @@ -967,6 +1172,7 @@ bool do_command(THD *thd) close_connection(net); close_thread_tables(thd); // Free before kill free_root(&thd->mem_root,MYF(0)); + free_root(&thd->transaction.mem_root,MYF(0)); kill_mysql(); error=TRUE; break; @@ -974,11 +1180,11 @@ bool do_command(THD *thd) case COM_STATISTICS: { mysql_log.write(thd,command,NullS); - thread_safe_increment(com_stat[SQLCOM_SHOW_STATUS],&LOCK_thread_count); + statistic_increment(com_stat[SQLCOM_SHOW_STATUS],&LOCK_status); char buff[200]; ulong uptime = (ulong) (thd->start_time - start_time); sprintf((char*) buff, - "Uptime: %ld Threads: %d Questions: %lu Slow queries: %ld Opens: %ld Flush tables: %ld Open tables: %d Queries per second avg: %.3f", + "Uptime: %ld Threads: %d Questions: %lu Slow queries: %ld Opens: %ld Flush tables: %ld Open tables: %u Queries per second avg: %.3f", uptime, (int) thread_count,thd->query_id,long_query_count, opened_tables,refresh_version, cached_tables(), @@ -993,12 +1199,12 @@ bool do_command(THD *thd) break; } case COM_PING: - thread_safe_increment(com_other,&LOCK_thread_count); + statistic_increment(com_other,&LOCK_status); send_ok(net); // Tell client we are alive break; case COM_PROCESS_INFO: - thread_safe_increment(com_stat[SQLCOM_SHOW_PROCESSLIST],&LOCK_thread_count); - if (!thd->priv_user[0] && check_process_priv(thd)) + statistic_increment(com_stat[SQLCOM_SHOW_PROCESSLIST],&LOCK_status); + if (!thd->priv_user[0] && check_global_access(thd,PROCESS_ACL)) break; mysql_log.write(thd,command,NullS); mysqld_list_processes(thd,thd->master_access & PROCESS_ACL ? NullS : @@ -1006,14 +1212,14 @@ bool do_command(THD *thd) break; case COM_PROCESS_KILL: { - thread_safe_increment(com_stat[SQLCOM_KILL],&LOCK_thread_count); - ulong id=(ulong) uint4korr(packet+1); + statistic_increment(com_stat[SQLCOM_KILL],&LOCK_status); + ulong id=(ulong) uint4korr(packet); kill_one_thread(thd,id); break; } case COM_DEBUG: - thread_safe_increment(com_other,&LOCK_thread_count); - if (check_process_priv(thd)) + statistic_increment(com_other,&LOCK_status); + if (check_global_access(thd, SUPER_ACL)) break; /* purecov: inspected */ mysql_print_status(thd); mysql_log.write(thd,command,NullS); @@ -1023,6 +1229,7 @@ bool do_command(THD *thd) case COM_CONNECT: // Impossible here case COM_TIME: // Impossible from client case COM_DELAYED_INSERT: + case COM_END: default: send_error(net, ER_UNKNOWN_COM_ERROR); break; @@ -1044,8 +1251,9 @@ bool do_command(THD *thd) { thd->proc_info="logging slow query"; - if ((ulong) (thd->start_time - thd->time_after_lock) > long_query_time || - ((thd->lex.options & + if ((ulong) (thd->start_time - thd->time_after_lock) > + thd->variables.long_query_time || + ((thd->lex.select_lex.options & (QUERY_NO_INDEX_USED | QUERY_NO_GOOD_INDEX_USED)) && (specialflag & SPECIAL_LONG_LOG_FORMAT))) { @@ -1060,7 +1268,7 @@ bool do_command(THD *thd) thd->query=0; thread_running--; VOID(pthread_mutex_unlock(&LOCK_thread_count)); - thd->packet.shrink(net_buffer_length); // Reclaim some memory + thd->packet.shrink(thd->variables.net_buffer_length); // Reclaim some memory free_root(&thd->mem_root,MYF(MY_KEEP_PREALLOC)); DBUG_RETURN(error); } @@ -1076,33 +1284,47 @@ mysql_execute_command(void) int res=0; THD *thd=current_thd; LEX *lex= &thd->lex; - TABLE_LIST *tables=(TABLE_LIST*) lex->table_list.first; + TABLE_LIST *tables=(TABLE_LIST*) lex->select_lex.table_list.first; + SELECT_LEX *select_lex = lex->select; DBUG_ENTER("mysql_execute_command"); if (thd->slave_thread) { - // skip if we are in the slave thread, some table - // rules have been given and the table list says the query should not be - // replicated - if(table_rules_on && tables && !tables_ok(thd,tables)) + /* + Skip if we are in the slave thread, some table rules have been + given and the table list says the query should not be replicated + */ + if (table_rules_on && tables && !tables_ok(thd,tables)) DBUG_VOID_RETURN; - // this is a workaround to deal with the shortcoming - // in 3.23.44-3.23.46 masters - // in RELEASE_LOCK() logging. We re-write SELECT RELEASE_LOCK() as - // DO RELEASE_LOCK() +#ifndef TO_BE_DELETED + /* + This is a workaround to deal with the shortcoming in 3.23.44-3.23.46 + masters in RELEASE_LOCK() logging. We re-write SELECT RELEASE_LOCK() + as DO RELEASE_LOCK() + */ if (lex->sql_command == SQLCOM_SELECT) { lex->sql_command = SQLCOM_DO; - lex->insert_list = &lex->item_list; + lex->insert_list = &select_lex->item_list; } +#endif } - thread_safe_increment(com_stat[lex->sql_command],&LOCK_thread_count); + /* + Skip if we are in the slave thread, some table rules have been given + and the table list says the query should not be replicated + */ + if ((lex->select_lex.next && create_total_list(thd,lex,&tables)) || + (table_rules_on && tables && thd->slave_thread && + !tables_ok(thd,tables))) + DBUG_VOID_RETURN; + + statistic_increment(com_stat[lex->sql_command],&LOCK_status); switch (lex->sql_command) { case SQLCOM_SELECT: { select_result *result; - if (lex->options & SELECT_DESCRIBE) + if (select_lex->options & SELECT_DESCRIBE) lex->exchange=0; if (tables) { @@ -1120,10 +1342,12 @@ mysql_execute_command(void) break; // Error message is given } - thd->offset_limit=lex->offset_limit; - thd->select_limit=lex->select_limit+lex->offset_limit; - if (thd->select_limit < lex->select_limit) + thd->offset_limit=select_lex->offset_limit; + thd->select_limit=select_lex->select_limit+select_lex->offset_limit; + if (thd->select_limit < select_lex->select_limit) thd->select_limit= HA_POS_ERROR; // no limit + if (thd->select_limit == HA_POS_ERROR) + select_lex->options&= ~OPTION_FOUND_ROWS; if (lex->exchange) { @@ -1148,8 +1372,8 @@ mysql_execute_command(void) { res= -1; #ifdef DELETE_ITEMS - delete lex->having; - delete lex->where; + delete select_lex->having; + delete select_lex->where; #endif break; } @@ -1159,6 +1383,8 @@ mysql_execute_command(void) Normal select: Change lock if we are using SELECT HIGH PRIORITY, FOR UPDATE or IN SHARE MODE + + TODO: Delete the following loop when locks is set by sql_yacc */ TABLE_LIST *table; for (table = tables ; table ; table=table->next) @@ -1167,22 +1393,11 @@ mysql_execute_command(void) if (!(res=open_and_lock_tables(thd,tables))) { - res=mysql_select(thd,tables,lex->item_list, - lex->where, - (ORDER*) lex->order_list.first, - (ORDER*) lex->group_list.first, - lex->having, - (ORDER*) lex->proc_list.first, - lex->options | thd->options, - result); - if (res) - result->abort(); + query_cache_store_query(thd, tables); + res=handle_select(thd, lex, result); } - delete result; -#ifdef DELETE_ITEMS - delete lex->having; - delete lex->where; -#endif + else + delete result; break; } case SQLCOM_DO: @@ -1194,63 +1409,104 @@ mysql_execute_command(void) break; case SQLCOM_PURGE: - { - if (check_process_priv(thd)) - goto error; - res = purge_master_logs(thd, lex->to_log); - break; - } + { + if (check_global_access(thd, SUPER_ACL)) + goto error; + res = purge_master_logs(thd, lex->to_log); + break; + } + case SQLCOM_SHOW_NEW_MASTER: + { + if (check_global_access(thd, REPL_SLAVE_ACL)) + goto error; +#ifndef WORKING_NEW_MASTER + net_printf(&thd->net, ER_NOT_SUPPORTED_YET, "SHOW NEW MASTER"); + res= 1; +#else + res = show_new_master(thd); +#endif + break; + } + case SQLCOM_SHOW_SLAVE_HOSTS: + { + if (check_global_access(thd, REPL_SLAVE_ACL)) + goto error; + res = show_slave_hosts(thd); + break; + } + case SQLCOM_SHOW_BINLOG_EVENTS: + { + if (check_global_access(thd, REPL_SLAVE_ACL)) + goto error; + res = show_binlog_events(thd); + break; + } case SQLCOM_BACKUP_TABLE: - { - if (check_db_used(thd,tables) || - check_table_access(thd,SELECT_ACL, tables) || - check_access(thd, FILE_ACL, any_db)) - goto error; /* purecov: inspected */ - res = mysql_backup_table(thd, tables); + { + if (check_db_used(thd,tables) || + check_table_access(thd,SELECT_ACL, tables) || + check_global_access(thd, FILE_ACL)) + goto error; /* purecov: inspected */ + res = mysql_backup_table(thd, tables); - break; - } + break; + } case SQLCOM_RESTORE_TABLE: - { - if (check_db_used(thd,tables) || - check_table_access(thd,INSERT_ACL, tables) || - check_access(thd, FILE_ACL, any_db)) - goto error; /* purecov: inspected */ - res = mysql_restore_table(thd, tables); - break; - } + { + if (check_db_used(thd,tables) || + check_table_access(thd, INSERT_ACL, tables) || + check_global_access(thd, FILE_ACL)) + goto error; /* purecov: inspected */ + res = mysql_restore_table(thd, tables); + break; + } case SQLCOM_CHANGE_MASTER: - { - if(check_access(thd, PROCESS_ACL, any_db)) - goto error; - res = change_master(thd); - break; - } + { + if (check_global_access(thd, SUPER_ACL)) + goto error; + LOCK_ACTIVE_MI; + res = change_master(thd,active_mi); + UNLOCK_ACTIVE_MI; + break; + } case SQLCOM_SHOW_SLAVE_STAT: - { - if (check_process_priv(thd)) - goto error; - res = show_master_info(thd); - break; - } + { + if (check_global_access(thd, SUPER_ACL)) + goto error; + LOCK_ACTIVE_MI; + res = show_master_info(thd,active_mi); + UNLOCK_ACTIVE_MI; + break; + } case SQLCOM_SHOW_MASTER_STAT: - { - if (check_process_priv(thd)) - goto error; - res = show_binlog_info(thd); - break; - } + { + if (check_global_access(thd, SUPER_ACL)) + goto error; + res = show_binlog_info(thd); + break; + } + + case SQLCOM_LOAD_MASTER_DATA: // sync with master + if (check_global_access(thd, SUPER_ACL)) + goto error; + if (end_active_trans(thd)) + res= -1; + else + res = load_master_data(thd); + break; + #ifdef HAVE_INNOBASE_DB case SQLCOM_SHOW_INNODB_STATUS: { - if (check_process_priv(thd)) + if (check_global_access(thd, SUPER_ACL)) goto error; res = innodb_show_status(thd); break; } #endif - case SQLCOM_LOAD_MASTER_TABLE: + case SQLCOM_LOAD_MASTER_TABLE: + { if (!tables->db) tables->db=thd->db; if (check_access(thd,CREATE_ACL,tables->db,&tables->grant.privilege)) @@ -1263,51 +1519,69 @@ mysql_execute_command(void) bool error=check_grant(thd,CREATE_ACL,tables); tables->next=tmp_table_list; if (error) - goto error; + goto error; } if (strlen(tables->real_name) > NAME_LEN) { net_printf(&thd->net,ER_WRONG_TABLE_NAME,tables->real_name); break; } - - thd->last_nx_table = tables->real_name; - thd->last_nx_db = tables->db; - if (fetch_nx_table(thd, &glob_mi)) - break; // fetch_nx_table did send the error to the client - send_ok(&thd->net); + LOCK_ACTIVE_MI; + // fetch_master_table will send the error to the client on failure + if (!fetch_master_table(thd, tables->db, tables->real_name, + active_mi, 0)) + { + send_ok(&thd->net); + } + UNLOCK_ACTIVE_MI; break; - + } case SQLCOM_CREATE_TABLE: + { + ulong want_priv= ((lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) ? + CREATE_TMP_ACL : CREATE_ACL); if (!tables->db) tables->db=thd->db; - if (check_access(thd,CREATE_ACL,tables->db,&tables->grant.privilege) || + if (check_access(thd,want_priv,tables->db,&tables->grant.privilege) || check_merge_table_access(thd, tables->db, (TABLE_LIST *) lex->create_info.merge_list.first)) goto error; /* purecov: inspected */ - if (grant_option) + if (grant_option && want_priv != CREATE_TMP_ACL) { /* Check that the first table has CREATE privilege */ TABLE_LIST *tmp_table_list=tables->next; tables->next=0; - bool error=check_grant(thd,CREATE_ACL,tables); + bool error=check_grant(thd, want_priv, tables); tables->next=tmp_table_list; if (error) goto error; } if (strlen(tables->real_name) > NAME_LEN) { - net_printf(&thd->net,ER_WRONG_TABLE_NAME,tables->real_name); + net_printf(&thd->net, ER_WRONG_TABLE_NAME, tables->alias); res=0; break; } - if (lex->item_list.elements) // With select +#ifndef HAVE_READLINK + lex->create_info.data_file_name=lex->create_info.index_file_name=0; +#else + /* Fix names if symlinked tables */ + if (append_file_to_dir(thd, &lex->create_info.data_file_name, + tables->real_name) || + append_file_to_dir(thd,&lex->create_info.index_file_name, + tables->real_name)) + { + res=-1; + break; + } +#endif + if (select_lex->item_list.elements) // With select { select_result *result; if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) && - check_dup(thd,tables->db,tables->real_name,tables->next)) + check_dup(tables->db, tables->real_name, tables->next)) { net_printf(&thd->net,ER_INSERT_TABLE_USED,tables->real_name); DBUG_VOID_RETURN; @@ -1317,34 +1591,26 @@ mysql_execute_command(void) TABLE_LIST *table; if (check_table_access(thd, SELECT_ACL, tables->next)) goto error; // Error message is given + /* TODO: Delete the following loop when locks is set by sql_yacc */ for (table = tables->next ; table ; table=table->next) table->lock_type= lex->lock_option; } - thd->offset_limit=lex->offset_limit; - thd->select_limit=lex->select_limit+lex->offset_limit; - if (thd->select_limit < lex->select_limit) + thd->offset_limit=select_lex->offset_limit; + thd->select_limit=select_lex->select_limit+select_lex->offset_limit; + if (thd->select_limit < select_lex->select_limit) thd->select_limit= HA_POS_ERROR; // No limit + /* Skip first table, which is the table we are creating */ + lex->select_lex.table_list.first= + (byte*) (((TABLE_LIST *) lex->select_lex.table_list.first)->next); if (!(res=open_and_lock_tables(thd,tables->next))) { - if ((result=new select_create(tables->db ? tables->db : thd->db, - tables->real_name, &lex->create_info, - lex->create_list, - lex->key_list, - lex->item_list,lex->duplicates))) - { - res=mysql_select(thd,tables->next,lex->item_list, - lex->where, - (ORDER*) lex->order_list.first, - (ORDER*) lex->group_list.first, - lex->having, - (ORDER*) lex->proc_list.first, - lex->options | thd->options, - result); - if (res) - result->abort(); - delete result; - } + if ((result=new select_create(tables->db ? tables->db : thd->db, + tables->real_name, &lex->create_info, + lex->create_list, + lex->key_list, + select_lex->item_list,lex->duplicates))) + res=handle_select(thd, lex, result); else res= -1; } @@ -1359,6 +1625,7 @@ mysql_execute_command(void) send_ok(&thd->net); } break; + } case SQLCOM_CREATE_INDEX: if (!tables->db) tables->db=thd->db; @@ -1373,38 +1640,45 @@ mysql_execute_command(void) break; case SQLCOM_SLAVE_START: - start_slave(thd); + { + LOCK_ACTIVE_MI; + start_slave(thd,active_mi,1 /* net report*/); + UNLOCK_ACTIVE_MI; break; + } case SQLCOM_SLAVE_STOP: - /* - if the client thread has locked tables, a deadlock is possible. - Assume that - - the client thread does LOCK TABLE t READ. - - then the master updates t. - - then the SQL slave thread wants to update t, + /* + If the client thread has locked tables, a deadlock is possible. + Assume that + - the client thread does LOCK TABLE t READ. + - then the master updates t. + - then the SQL slave thread wants to update t, so it waits for the client thread because t is locked by it. - - then the client thread does SLAVE STOP. + - then the client thread does SLAVE STOP. SLAVE STOP waits for the SQL slave thread to terminate its update t, which waits for the client thread because t is locked by it. - To prevent that, refuse SLAVE STOP if the - client thread has locked tables - */ - if (thd->locked_tables || thd->active_transaction()) - { - send_error(&thd->net,ER_LOCK_OR_ACTIVE_TRANSACTION); - break; - } - stop_slave(thd); + To prevent that, refuse SLAVE STOP if the + client thread has locked tables + */ + if (thd->locked_tables || thd->active_transaction()) + { + send_error(&thd->net,ER_LOCK_OR_ACTIVE_TRANSACTION); break; - + } + { + LOCK_ACTIVE_MI; + stop_slave(thd,active_mi,1/* net report*/); + UNLOCK_ACTIVE_MI; + break; + } case SQLCOM_ALTER_TABLE: #if defined(DONT_ALLOW_SHOW_COMMANDS) send_error(&thd->net,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ break; #else { - uint priv=0; - if (lex->name && strlen(lex->name) > NAME_LEN) + ulong priv=0; + if (lex->name && (!lex->name[0] || strlen(lex->name) > NAME_LEN)) { net_printf(&thd->net,ER_WRONG_TABLE_NAME,lex->name); res=0; @@ -1412,10 +1686,10 @@ mysql_execute_command(void) } if (!tables->db) tables->db=thd->db; - if (!lex->db) - lex->db=tables->db; + if (!select_lex->db) + select_lex->db=tables->db; if (check_access(thd,ALTER_ACL,tables->db,&tables->grant.privilege) || - check_access(thd,INSERT_ACL | CREATE_ACL,lex->db,&priv) || + check_access(thd,INSERT_ACL | CREATE_ACL,select_lex->db,&priv) || check_merge_table_access(thd, tables->db, (TABLE_LIST *) lex->create_info.merge_list.first)) @@ -1431,22 +1705,27 @@ mysql_execute_command(void) TABLE_LIST tmp_table; bzero((char*) &tmp_table,sizeof(tmp_table)); tmp_table.real_name=lex->name; - tmp_table.db=lex->db; + tmp_table.db=select_lex->db; tmp_table.grant.privilege=priv; if (check_grant(thd,INSERT_ACL | CREATE_ACL,tables)) goto error; } } + /* Don't yet allow changing of symlinks with ALTER TABLE */ + lex->create_info.data_file_name=lex->create_info.index_file_name=0; /* ALTER TABLE ends previous transaction */ if (end_active_trans(thd)) res= -1; else - res= mysql_alter_table(thd, lex->db, lex->name, + { + res= mysql_alter_table(thd, select_lex->db, lex->name, &lex->create_info, tables, lex->create_list, lex->key_list, lex->drop_list, lex->alter_list, - (ORDER *) lex->order_list.first, - lex->drop_primary, lex->duplicates); + (ORDER *) select_lex->order_list.first, + lex->drop_primary, lex->duplicates, + lex->alter_keys_onoff, lex->simple_alter); + } break; } #endif @@ -1470,11 +1749,12 @@ mysql_execute_command(void) old_list.next=new_list.next=0; if (check_grant(thd,ALTER_ACL,&old_list) || (!test_all_bits(table->next->grant.privilege, - INSERT_ACL | CREATE_ACL) && + INSERT_ACL | CREATE_ACL) && check_grant(thd,INSERT_ACL | CREATE_ACL, &new_list))) goto error; } } + query_cache_invalidate3(thd, tables, 0); if (end_active_trans(thd)) res= -1; else if (mysql_rename_tables(thd,tables)) @@ -1487,7 +1767,7 @@ mysql_execute_command(void) DBUG_VOID_RETURN; #else { - if (check_process_priv(thd)) + if (check_global_access(thd, SUPER_ACL)) goto error; res = show_binlogs(thd); break; @@ -1508,21 +1788,21 @@ mysql_execute_command(void) } #endif case SQLCOM_REPAIR: - { - if (check_db_used(thd,tables) || - check_table_access(thd,SELECT_ACL | INSERT_ACL, tables)) - goto error; /* purecov: inspected */ - res = mysql_repair_table(thd, tables, &lex->check_opt); - break; - } + { + if (check_db_used(thd,tables) || + check_table_access(thd,SELECT_ACL | INSERT_ACL, tables)) + goto error; /* purecov: inspected */ + res = mysql_repair_table(thd, tables, &lex->check_opt); + break; + } case SQLCOM_CHECK: - { - if (check_db_used(thd,tables) || - check_table_access(thd, SELECT_ACL | EXTRA_ACL , tables)) - goto error; /* purecov: inspected */ - res = mysql_check_table(thd, tables, &lex->check_opt); - break; - } + { + if (check_db_used(thd,tables) || + check_table_access(thd, SELECT_ACL | EXTRA_ACL , tables)) + goto error; /* purecov: inspected */ + res = mysql_check_table(thd, tables, &lex->check_opt); + break; + } case SQLCOM_ANALYZE: { if (check_db_used(thd,tables) || @@ -1564,22 +1844,43 @@ mysql_execute_command(void) goto error; if (grant_option && check_grant(thd,UPDATE_ACL,tables)) goto error; - if (lex->item_list.elements != lex->value_list.elements) + if (select_lex->item_list.elements != lex->value_list.elements) { send_error(&thd->net,ER_WRONG_VALUE_COUNT); DBUG_VOID_RETURN; } - res = mysql_update(thd,tables, - lex->item_list, - lex->value_list, - lex->where, - lex->select_limit, - lex->duplicates, - lex->lock_option); - -#ifdef DELETE_ITEMS - delete lex->where; -#endif + if (select_lex->table_list.elements == 1) + { + res= mysql_update(thd,tables, + select_lex->item_list, + lex->value_list, + select_lex->where, + (ORDER *) select_lex->order_list.first, + select_lex->select_limit, + lex->duplicates); + } + else + { + const char *msg= 0; + lex->sql_command= SQLCOM_MULTI_UPDATE; + if (select_lex->order_list.elements) + msg="ORDER BY"; + else if (select_lex->select_limit && select_lex->select_limit != + HA_POS_ERROR) + msg="LIMIT"; + if (msg) + { + net_printf(&thd->net, ER_WRONG_USAGE, "UPDATE", msg); + res= 1; + break; + } + res= mysql_multi_update(thd,tables, + &select_lex->item_list, + &lex->value_list, + select_lex->where, + select_lex->options, + lex->duplicates); + } break; case SQLCOM_INSERT: if (check_access(thd,INSERT_ACL,tables->db,&tables->grant.privilege)) @@ -1587,29 +1888,30 @@ mysql_execute_command(void) if (grant_option && check_grant(thd,INSERT_ACL,tables)) goto error; res = mysql_insert(thd,tables,lex->field_list,lex->many_values, - lex->duplicates, - lex->lock_option); + lex->duplicates); break; case SQLCOM_REPLACE: - if (check_access(thd,INSERT_ACL | UPDATE_ACL | DELETE_ACL, + if (check_access(thd,INSERT_ACL | DELETE_ACL, tables->db,&tables->grant.privilege)) goto error; /* purecov: inspected */ - if (grant_option && check_grant(thd,INSERT_ACL | UPDATE_ACL | DELETE_ACL, + if (grant_option && check_grant(thd,INSERT_ACL | DELETE_ACL, tables)) goto error; res = mysql_insert(thd,tables,lex->field_list,lex->many_values, - DUP_REPLACE, - lex->lock_option); + DUP_REPLACE); break; case SQLCOM_REPLACE_SELECT: case SQLCOM_INSERT_SELECT: { - // Check that we have modify privileges for the first table and - // select privileges for the rest + + /* + Check that we have modify privileges for the first table and + select privileges for the rest + */ { - uint privilege= (lex->sql_command == SQLCOM_INSERT_SELECT ? - INSERT_ACL : INSERT_ACL | UPDATE_ACL | DELETE_ACL); + ulong privilege= (lex->sql_command == SQLCOM_INSERT_SELECT ? + INSERT_ACL : INSERT_ACL | DELETE_ACL); TABLE_LIST *save_next=tables->next; tables->next=0; if (check_access(thd, privilege, @@ -1622,51 +1924,50 @@ mysql_execute_command(void) } select_result *result; - thd->offset_limit=lex->offset_limit; - thd->select_limit=lex->select_limit+lex->offset_limit; - if (thd->select_limit < lex->select_limit) + thd->offset_limit=select_lex->offset_limit; + thd->select_limit=select_lex->select_limit+select_lex->offset_limit; + if (thd->select_limit < select_lex->select_limit) thd->select_limit= HA_POS_ERROR; // No limit - if (check_dup(thd,tables->db,tables->real_name,tables->next)) + if (check_dup(tables->db, tables->real_name, tables->next)) { net_printf(&thd->net,ER_INSERT_TABLE_USED,tables->real_name); DBUG_VOID_RETURN; } - tables->lock_type=TL_WRITE; // update first table { + /* TODO: Delete the following loop when locks is set by sql_yacc */ TABLE_LIST *table; for (table = tables->next ; table ; table=table->next) table->lock_type= lex->lock_option; } - if (!(res=open_and_lock_tables(thd,tables))) + + /* Skip first table, which is the table we are inserting in */ + lex->select_lex.table_list.first= + (byte*) (((TABLE_LIST *) lex->select_lex.table_list.first)->next); + if (!(res=open_and_lock_tables(thd, tables))) { if ((result=new select_insert(tables->table,&lex->field_list, - lex->sql_command == SQLCOM_REPLACE_SELECT ? - DUP_REPLACE : DUP_IGNORE))) - { - res=mysql_select(thd,tables->next,lex->item_list, - lex->where, - (ORDER*) lex->order_list.first, - (ORDER*) lex->group_list.first, - lex->having, - (ORDER*) lex->proc_list.first, - lex->options | thd->options, - result); - delete result; - } - else - res= -1; + lex->duplicates))) + res=handle_select(thd,lex,result); } -#ifdef DELETE_ITEMS - delete lex->having; - delete lex->where; -#endif + else + res= -1; break; } case SQLCOM_TRUNCATE: - lex->where=0; - lex->select_limit=HA_POS_ERROR; - /* Fall through */ + if (check_access(thd,DELETE_ACL,tables->db,&tables->grant.privilege)) + goto error; /* purecov: inspected */ + /* + Don't allow this within a transaction because we want to use + re-generate table + */ + if (thd->locked_tables || thd->active_transaction()) + { + send_error(&thd->net,ER_LOCK_OR_ACTIVE_TRANSACTION,NullS); + goto error; + } + res=mysql_truncate(thd,tables); + break; case SQLCOM_DELETE: { if (check_access(thd,DELETE_ACL,tables->db,&tables->grant.privilege)) @@ -1675,24 +1976,88 @@ mysql_execute_command(void) goto error; // Set privilege for the WHERE clause tables->grant.want_privilege=(SELECT_ACL & ~tables->grant.privilege); - /* TRUNCATE ends previous transaction */ - if (lex->sql_command == SQLCOM_TRUNCATE && end_active_trans(thd)) - res= -1; - else - res = mysql_delete(thd,tables,lex->where,lex->select_limit, - lex->lock_option, lex->options); + res = mysql_delete(thd,tables, select_lex->where, + (ORDER*) select_lex->order_list.first, + select_lex->select_limit, select_lex->options); break; } - case SQLCOM_DROP_TABLE: + case SQLCOM_DELETE_MULTI: + { + TABLE_LIST *aux_tables=(TABLE_LIST *)thd->lex.auxilliary_table_list.first; + TABLE_LIST *auxi; + uint table_count=0; + multi_delete *result; + + /* sql_yacc guarantees that tables and aux_tables are not zero */ + if (check_db_used(thd, tables) || check_db_used(thd,aux_tables) || + check_table_access(thd,SELECT_ACL, tables) || + check_table_access(thd,DELETE_ACL, aux_tables)) + goto error; + if ((thd->options & OPTION_SAFE_UPDATES) && !select_lex->where) + { + send_error(&thd->net,ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE); + goto error; + } + for (auxi=(TABLE_LIST*) aux_tables ; auxi ; auxi=auxi->next) { - if (check_table_access(thd,DROP_ACL,tables)) - goto error; /* purecov: inspected */ - if (end_active_trans(thd)) - res= -1; - else - res = mysql_rm_table(thd,tables,lex->drop_if_exists); + table_count++; + /* All tables in aux_tables must be found in FROM PART */ + TABLE_LIST *walk; + for (walk=(TABLE_LIST*) tables ; walk ; walk=walk->next) + { + if (!strcmp(auxi->real_name,walk->real_name) && + !strcmp(walk->db,auxi->db)) + break; + } + if (!walk) + { + net_printf(&thd->net,ER_NONUNIQ_TABLE,auxi->real_name); + goto error; + } + walk->lock_type= auxi->lock_type; + // Store address to table as we need it later + auxi->table= my_reinterpret_cast(TABLE *) (walk); + } + if (add_item_to_list(new Item_null())) + { + res= -1; + break; } + tables->grant.want_privilege=(SELECT_ACL & ~tables->grant.privilege); + thd->proc_info="init"; + if ((res=open_and_lock_tables(thd,tables))) + break; + /* Fix tables-to-be-deleted-from list to point at opened tables */ + for (auxi=(TABLE_LIST*) aux_tables ; auxi ; auxi=auxi->next) + auxi->table= (my_reinterpret_cast(TABLE_LIST*) (auxi->table))->table; + + if (!thd->fatal_error && (result= new multi_delete(thd,aux_tables, + table_count))) + { + res=mysql_select(thd,tables,select_lex->item_list, + select_lex->where, + (ORDER *)NULL,(ORDER *)NULL,(Item *)NULL, + (ORDER *)NULL, + select_lex->options | thd->options | + SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK, + result); + delete result; + } + else + res= -1; // Error is not sent + close_thread_tables(thd); break; + } + case SQLCOM_DROP_TABLE: + { + if (check_table_access(thd,DROP_ACL,tables)) + goto error; /* purecov: inspected */ + if (end_active_trans(thd)) + res= -1; + else + res = mysql_rm_table(thd,tables,lex->drop_if_exists); + } + break; case SQLCOM_DROP_INDEX: if (!tables->db) tables->db=thd->db; @@ -1707,49 +2072,42 @@ mysql_execute_command(void) break; case SQLCOM_SHOW_DATABASES: #if defined(DONT_ALLOW_SHOW_COMMANDS) - send_error(&thd->net,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ + send_error(&thd->net,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ DBUG_VOID_RETURN; #else if ((specialflag & SPECIAL_SKIP_SHOW_DB) && - check_process_priv(thd)) + check_global_access(thd, SHOW_DB_ACL)) goto error; res= mysqld_show_dbs(thd, (lex->wild ? lex->wild->ptr() : NullS)); break; #endif case SQLCOM_SHOW_PROCESSLIST: - if (!thd->priv_user[0] && check_process_priv(thd)) + if (!thd->priv_user[0] && check_global_access(thd,PROCESS_ACL)) break; mysqld_list_processes(thd,thd->master_access & PROCESS_ACL ? NullS : thd->priv_user,lex->verbose); break; case SQLCOM_SHOW_STATUS: - res= mysqld_show(thd,(lex->wild ? lex->wild->ptr() : NullS),status_vars); + res= mysqld_show(thd,(lex->wild ? lex->wild->ptr() : NullS),status_vars, + OPT_GLOBAL); break; case SQLCOM_SHOW_VARIABLES: res= mysqld_show(thd, (lex->wild ? lex->wild->ptr() : NullS), - init_vars); + init_vars, lex->option_type); break; case SQLCOM_SHOW_LOGS: -#ifdef DONT_ALLOW_SHOW_COMMANDS - send_error(&thd->net,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ - DBUG_VOID_RETURN; -#else - { - if (grant_option && check_access(thd, FILE_ACL, any_db)) - goto error; - res= mysqld_show_logs(thd); - break; - } -#endif + { + res= mysqld_show_logs(thd); + break; + } case SQLCOM_SHOW_TABLES: /* FALL THROUGH */ - case SQLCOM_SHOW_OPEN_TABLES: #ifdef DONT_ALLOW_SHOW_COMMANDS send_error(&thd->net,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ DBUG_VOID_RETURN; #else { - char *db=lex->db ? lex->db : thd->db; + char *db=select_lex->db ? select_lex->db : thd->db; if (!db) { send_error(&thd->net,ER_NO_DB_ERROR); /* purecov: inspected */ @@ -1764,34 +2122,32 @@ mysql_execute_command(void) if (check_access(thd,SELECT_ACL,db,&thd->col_access)) goto error; /* purecov: inspected */ /* grant is checked in mysqld_show_tables */ - if (lex->sql_command == SQLCOM_SHOW_OPEN_TABLES) - res= mysqld_show_open_tables(thd,db, - (lex->wild ? lex->wild->ptr() : NullS)); - else if (lex->options & SELECT_DESCRIBE) + if (select_lex->options & SELECT_DESCRIBE) res= mysqld_extend_show_tables(thd,db, - (lex->wild ? lex->wild->ptr() : NullS)); + (lex->wild ? lex->wild->ptr() : NullS)); else res= mysqld_show_tables(thd,db, (lex->wild ? lex->wild->ptr() : NullS)); break; } #endif + case SQLCOM_SHOW_OPEN_TABLES: + res= mysqld_show_open_tables(thd,(lex->wild ? lex->wild->ptr() : NullS)); + break; case SQLCOM_SHOW_FIELDS: #ifdef DONT_ALLOW_SHOW_COMMANDS send_error(&thd->net,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */ DBUG_VOID_RETURN; #else { - char *db=tables->db ? tables->db : thd->db; - if (!db) + char *db=tables->db; + if (!*db) { send_error(&thd->net,ER_NO_DB_ERROR); /* purecov: inspected */ goto error; /* purecov: inspected */ } remove_escape(db); // Fix escaped '_' remove_escape(tables->real_name); - if (!tables->db) - tables->db=thd->db; if (check_access(thd,SELECT_ACL | EXTRA_ACL,db,&thd->col_access)) goto error; /* purecov: inspected */ tables->grant.privilege=thd->col_access; @@ -1809,7 +2165,7 @@ mysql_execute_command(void) DBUG_VOID_RETURN; #else { - char *db=tables->db ? tables->db : thd->db; + char *db=tables->db; if (!db) { send_error(&thd->net,ER_NO_DB_ERROR); /* purecov: inspected */ @@ -1829,7 +2185,7 @@ mysql_execute_command(void) } #endif case SQLCOM_CHANGE_DB: - mysql_change_db(thd,lex->db); + mysql_change_db(thd,select_lex->db); break; case SQLCOM_LOAD: { @@ -1850,7 +2206,7 @@ mysql_execute_command(void) goto error; } if (check_access(thd,privilege,tables->db,&tables->grant.privilege) || - grant_option && check_grant(thd,privilege,tables)) + grant_option && check_grant(thd,privilege,tables)) goto error; } res=mysql_load(thd, lex->exchange, tables, lex->field_list, @@ -1858,74 +2214,26 @@ mysql_execute_command(void) break; } case SQLCOM_SET_OPTION: - { - uint org_options=thd->options; - thd->options=lex->options; - thd->update_lock_default= ((thd->options & OPTION_LOW_PRIORITY_UPDATES) ? - TL_WRITE_LOW_PRIORITY : TL_WRITE); - thd->default_select_limit=lex->select_limit; - thd->tx_isolation=lex->tx_isolation; - if (thd->gemini_spin_retries != lex->gemini_spin_retries) - { - thd->gemini_spin_retries= lex->gemini_spin_retries; - ha_set_spin_retries(thd->gemini_spin_retries); - } - DBUG_PRINT("info",("options: %ld limit: %ld", - thd->options,(long) thd->default_select_limit)); - - /* Check if auto_commit mode changed */ - if ((org_options ^ lex->options) & OPTION_NOT_AUTO_COMMIT) - { - if ((org_options & OPTION_NOT_AUTO_COMMIT)) - { - /* We changed to auto_commit mode */ - thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE); - thd->server_status|= SERVER_STATUS_AUTOCOMMIT; - if (ha_commit(thd)) - { - res= -1; - break; - } - } - else - { - thd->options&= ~(ulong) (OPTION_STATUS_NO_TRANS_UPDATE); - thd->server_status&= ~SERVER_STATUS_AUTOCOMMIT; - } - } - send_ok(&thd->net); + if (!(res=sql_set_variables(thd, &lex->var_list))) + send_ok(&thd->net); break; - } case SQLCOM_UNLOCK_TABLES: - if (thd->locked_tables) - { - thd->lock=thd->locked_tables; - thd->locked_tables=0; // Will be automaticly closed - } + unlock_locked_tables(thd); if (thd->options & OPTION_TABLE_LOCK) { end_active_trans(thd); thd->options&= ~(ulong) (OPTION_TABLE_LOCK); } if (thd->global_read_lock) - { - thd->global_read_lock=0; - pthread_mutex_lock(&LOCK_open); - global_read_lock--; - pthread_cond_broadcast(&COND_refresh); - pthread_mutex_unlock(&LOCK_open); - } + unlock_global_read_lock(thd); send_ok(&thd->net); break; case SQLCOM_LOCK_TABLES: - if (thd->locked_tables) - { - thd->lock=thd->locked_tables; - thd->locked_tables=0; // Will be automaticly closed - close_thread_tables(thd); - } + unlock_locked_tables(thd); if (check_db_used(thd,tables) || end_active_trans(thd)) goto error; + if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, tables)) + goto error; thd->in_lock_tables=1; thd->options|= OPTION_TABLE_LOCK; if (!(res=open_and_lock_tables(thd,tables))) @@ -1939,30 +2247,57 @@ mysql_execute_command(void) thd->in_lock_tables=0; break; case SQLCOM_CREATE_DB: + { + if (!strip_sp(lex->name) || check_db_name(lex->name)) { - if (!strip_sp(lex->name) || check_db_name(lex->name)) - { - net_printf(&thd->net,ER_WRONG_DB_NAME, lex->name); - break; - } - if (check_access(thd,CREATE_ACL,lex->name,0,1)) - break; - mysql_create_db(thd,lex->name,lex->create_info.options); + net_printf(&thd->net,ER_WRONG_DB_NAME, lex->name); break; } + /* + If in a slave thread : + CREATE DATABASE DB was certainly not preceded by USE DB. + For that reason, db_ok() in sql/slave.cc did not check the + do_db/ignore_db. And as this query involves no tables, tables_ok() + above was not called. So we have to check rules again here. + */ + if (thd->slave_thread && + (!db_ok(lex->name, replicate_do_db, replicate_ignore_db) || + !db_ok_with_wild_table(lex->name))) + break; + + if (check_access(thd,CREATE_ACL,lex->name,0,1)) + break; + res=mysql_create_db(thd,lex->name,lex->create_info.options,0); + break; + } case SQLCOM_DROP_DB: + { + if (!strip_sp(lex->name) || check_db_name(lex->name)) { - if (!strip_sp(lex->name) || check_db_name(lex->name)) - { - net_printf(&thd->net,ER_WRONG_DB_NAME, lex->name); - break; - } - if (check_access(thd,DROP_ACL,lex->name,0,1) || - end_active_trans(thd)) - break; - mysql_rm_db(thd,lex->name,lex->drop_if_exists); + net_printf(&thd->net,ER_WRONG_DB_NAME, lex->name); break; } + /* + If in a slave thread : + DROP DATABASE DB may not be preceded by USE DB. + For that reason, maybe db_ok() in sql/slave.cc did not check the + do_db/ignore_db. And as this query involves no tables, tables_ok() + above was not called. So we have to check rules again here. + */ + if (thd->slave_thread && + (!db_ok(lex->name, replicate_do_db, replicate_ignore_db) || + !db_ok_with_wild_table(lex->name))) + break; + if (check_access(thd,DROP_ACL,lex->name,0,1)) + break; + if (thd->locked_tables || thd->active_transaction()) + { + send_error(&thd->net,ER_LOCK_OR_ACTIVE_TRANSACTION); + goto error; + } + res=mysql_rm_db(thd,lex->name,lex->drop_if_exists,0); + break; + } case SQLCOM_CREATE_FUNCTION: if (check_access(thd,INSERT_ACL,"mysql",0,1)) break; @@ -1983,99 +2318,131 @@ mysql_execute_command(void) res= -1; #endif break; - case SQLCOM_REVOKE: - case SQLCOM_GRANT: - { - if (tables && !tables->db) - tables->db=thd->db; - if (check_access(thd, lex->grant | lex->grant_tot_col | GRANT_ACL, - tables && tables->db ? tables->db : lex->db, - tables ? &tables->grant.privilege : 0, - tables ? 0 : 1)) - goto error; - - /* Check that the user isn't trying to change a password for another - user if he doesn't have UPDATE privilege to the MySQL database */ - - if (thd->user) // If not replication - { - LEX_USER *user; - List_iterator <LEX_USER> user_list(lex->users_list); - while ((user=user_list++)) - { - if (user->password.str && - (strcmp(thd->user,user->user.str) || - user->host.str && - my_strcasecmp(user->host.str, thd->host ? thd->host : thd->ip))) - { - if (check_access(thd, UPDATE_ACL, "mysql",0,1)) - goto error; - break; // We are allowed to do changes - } - } - } - if (tables) - { - if (grant_option && check_grant(thd, - (lex->grant | lex->grant_tot_col | - GRANT_ACL), - tables)) - goto error; - res = mysql_table_grant(thd,tables,lex->users_list, lex->columns, - lex->grant, lex->sql_command == SQLCOM_REVOKE); - if(!res) - { - mysql_update_log.write(thd, thd->query,thd->query_length); - if (mysql_bin_log.is_open()) - { - Query_log_event qinfo(thd, thd->query); - mysql_bin_log.write(&qinfo); - } - } - } - else - { - if (lex->columns.elements) - { - net_printf(&thd->net,ER_ILLEGAL_GRANT_FOR_TABLE); - res=1; - } - else - res = mysql_grant(thd, lex->db, lex->users_list, lex->grant, - lex->sql_command == SQLCOM_REVOKE); - if (!res) - { - mysql_update_log.write(thd, thd->query,thd->query_length); - if (mysql_bin_log.is_open()) - { - Query_log_event qinfo(thd, thd->query); - mysql_bin_log.write(&qinfo); - } - } - } - break; - } + case SQLCOM_REVOKE: + case SQLCOM_GRANT: + { + if (check_access(thd, lex->grant | lex->grant_tot_col | GRANT_ACL, + tables && tables->db ? tables->db : select_lex->db, + tables ? &tables->grant.privilege : 0, + tables ? 0 : 1)) + goto error; + + /* + Check that the user isn't trying to change a password for another + user if he doesn't have UPDATE privilege to the MySQL database + */ + + if (thd->user) // If not replication + { + LEX_USER *user; + List_iterator <LEX_USER> user_list(lex->users_list); + while ((user=user_list++)) + { + if (user->password.str && + (strcmp(thd->user,user->user.str) || + user->host.str && + my_strcasecmp(user->host.str, thd->host_or_ip))) + { + if (check_access(thd, UPDATE_ACL, "mysql",0,1)) + goto error; + break; // We are allowed to do changes + } + } + } + if (tables) + { + if (grant_option && check_grant(thd, + (lex->grant | lex->grant_tot_col | + GRANT_ACL), + tables)) + goto error; + if (!(res = mysql_table_grant(thd,tables,lex->users_list, lex->columns, + lex->grant, + lex->sql_command == SQLCOM_REVOKE))) + { + mysql_update_log.write(thd, thd->query, thd->query_length); + if (mysql_bin_log.is_open()) + { + Query_log_event qinfo(thd, thd->query, thd->query_length, 0); + mysql_bin_log.write(&qinfo); + } + } + } + else + { + if (lex->columns.elements) + { + send_error(&thd->net,ER_ILLEGAL_GRANT_FOR_TABLE); + res=1; + } + else + res = mysql_grant(thd, select_lex->db, lex->users_list, lex->grant, + lex->sql_command == SQLCOM_REVOKE); + if (!res) + { + mysql_update_log.write(thd, thd->query, thd->query_length); + if (mysql_bin_log.is_open()) + { + Query_log_event qinfo(thd, thd->query, thd->query_length, 0); + mysql_bin_log.write(&qinfo); + } + if (mqh_used && lex->sql_command == SQLCOM_GRANT) + { + List_iterator <LEX_USER> str_list(lex->users_list); + LEX_USER *user; + while ((user=str_list++)) + reset_mqh(thd,user); + } + } + } + break; + } case SQLCOM_FLUSH: case SQLCOM_RESET: - if (check_access(thd,RELOAD_ACL,any_db) || check_db_used(thd, tables)) + if (check_global_access(thd,RELOAD_ACL) || check_db_used(thd, tables)) goto error; - if (reload_acl_and_cache(thd, lex->type, tables)) - send_error(&thd->net,0); - else - send_ok(&thd->net); + /* error sending is deferred to reload_acl_and_cache */ + reload_acl_and_cache(thd, lex->type, tables) ; break; case SQLCOM_KILL: kill_one_thread(thd,lex->thread_id); break; case SQLCOM_SHOW_GRANTS: res=0; - if ((thd->priv_user && !strcmp(thd->priv_user,lex->grant_user->user.str)) || + if ((thd->priv_user && + !strcmp(thd->priv_user,lex->grant_user->user.str)) || !check_access(thd, SELECT_ACL, "mysql",0,1)) { res = mysql_show_grants(thd,lex->grant_user); } break; + case SQLCOM_HA_OPEN: + if (check_db_used(thd,tables) || + check_table_access(thd,SELECT_ACL, tables)) + goto error; + res = mysql_ha_open(thd, tables); + break; + case SQLCOM_HA_CLOSE: + if (check_db_used(thd,tables)) + goto error; + res = mysql_ha_close(thd, tables); + break; + case SQLCOM_HA_READ: + if (check_db_used(thd,tables) || + check_table_access(thd,SELECT_ACL, tables)) + goto error; + res = mysql_ha_read(thd, tables, lex->ha_read_mode, lex->backup_dir, + lex->insert_list, lex->ha_rkey_mode, select_lex->where, + select_lex->select_limit, select_lex->offset_limit); + break; + case SQLCOM_BEGIN: + if (thd->locked_tables) + { + thd->lock=thd->locked_tables; + thd->locked_tables=0; // Will be automaticly closed + close_thread_tables(thd); // Free tables + } if (end_active_trans(thd)) { res= -1; @@ -2094,13 +2461,17 @@ mysql_execute_command(void) even if there is a problem with the OPTION_AUTO_COMMIT flag (Which of course should never happen...) */ + { thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE); thd->server_status&= ~SERVER_STATUS_IN_TRANS; if (!ha_commit(thd)) + { send_ok(&thd->net); + } else res= -1; break; + } case SQLCOM_ROLLBACK: thd->server_status&= ~SERVER_STATUS_IN_TRANS; if (!ha_rollback(thd)) @@ -2128,49 +2499,54 @@ error: /**************************************************************************** -** Get the user (global) and database privileges for all used tables -** Returns true (error) if we can't get the privileges and we don't use -** table/column grants. -** The idea of EXTRA_ACL is that one will be granted access to the table if -** one has the asked privilege on any column combination of the table; For -** example to be able to check a table one needs to have SELECT privilege on -** any column of the table. + Get the user (global) and database privileges for all used tables + Returns true (error) if we can't get the privileges and we don't use + table/column grants. + The idea of EXTRA_ACL is that one will be granted access to the table if + one has the asked privilege on any column combination of the table; For + example to be able to check a table one needs to have SELECT privilege on + any column of the table. ****************************************************************************/ bool -check_access(THD *thd,uint want_access,const char *db, uint *save_priv, - bool dont_check_global_grants) +check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, + bool dont_check_global_grants, bool no_errors) { - uint db_access,dummy; + DBUG_ENTER("check_access"); + DBUG_PRINT("enter",("want_access: %lu master_access: %lu", want_access, + thd->master_access)); + ulong db_access,dummy; if (save_priv) *save_priv=0; else save_priv= &dummy; - if (!db && !thd->db && !dont_check_global_grants) + if ((!db || !db[0]) && !thd->db && !dont_check_global_grants) { - send_error(&thd->net,ER_NO_DB_ERROR); /* purecov: tested */ - return TRUE; /* purecov: tested */ + if (!no_errors) + send_error(&thd->net,ER_NO_DB_ERROR); /* purecov: tested */ + DBUG_RETURN(TRUE); /* purecov: tested */ } if ((thd->master_access & want_access) == want_access) { *save_priv=thd->master_access; - return FALSE; + DBUG_RETURN(FALSE); } - if ((want_access & ~thd->master_access) & ~(DB_ACLS | EXTRA_ACL) || + if (((want_access & ~thd->master_access) & ~(DB_ACLS | EXTRA_ACL)) || ! db && dont_check_global_grants) { // We can never grant this - net_printf(&thd->net,ER_ACCESS_DENIED_ERROR, - thd->priv_user, - thd->host ? thd->host : (thd->ip ? thd->ip : "unknown"), - thd->password ? ER(ER_YES) : ER(ER_NO));/* purecov: tested */ - return TRUE; /* purecov: tested */ + if (!no_errors) + net_printf(&thd->net,ER_ACCESS_DENIED_ERROR, + thd->priv_user, + thd->host_or_ip, + thd->password ? ER(ER_YES) : ER(ER_NO));/* purecov: tested */ + DBUG_RETURN(TRUE); /* purecov: tested */ } if (db == any_db) - return FALSE; // Allow select on anything - + DBUG_RETURN(FALSE); // Allow select on anything + if (db && (!thd->db || strcmp(db,thd->db))) db_access=acl_get(thd->host, thd->ip, (char*) &thd->remote.sin_addr, thd->priv_user, db); /* purecov: inspected */ @@ -2184,30 +2560,41 @@ check_access(THD *thd,uint want_access,const char *db, uint *save_priv, if (db_access == want_access || ((grant_option && !dont_check_global_grants) && !(want_access & ~TABLE_ACLS))) - return FALSE; /* Ok */ - net_printf(&thd->net,ER_DBACCESS_DENIED_ERROR, - thd->priv_user, - thd->host ? thd->host : (thd->ip ? thd->ip : "unknown"), - db ? db : thd->db ? thd->db : "unknown"); /* purecov: tested */ - return TRUE; /* purecov: tested */ + DBUG_RETURN(FALSE); /* Ok */ + if (!no_errors) + net_printf(&thd->net,ER_DBACCESS_DENIED_ERROR, + thd->priv_user, + thd->host_or_ip, + db ? db : thd->db ? thd->db : "unknown"); /* purecov: tested */ + DBUG_RETURN(TRUE); /* purecov: tested */ } -bool check_process_priv(THD *thd) +/* check for global access and give descriptive error message if it fails */ + +bool check_global_access(THD *thd, ulong want_access) { - return (check_access(thd ? thd : current_thd,PROCESS_ACL,any_db)); + char command[128]; + if ((thd->master_access & want_access) == want_access) + return 0; + get_privilege_desc(command, sizeof(command), want_access); + net_printf(&thd->net,ER_SPECIFIC_ACCESS_DENIED_ERROR, + command); + return 1; } /* -** Check the privilege for all used tables. Table privileges are cached -** in the table list for GRANT checking + Check the privilege for all used tables. Table privileges are cached + in the table list for GRANT checking */ bool -check_table_access(THD *thd,uint want_access,TABLE_LIST *tables) +check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables, + bool no_errors) { - uint found=0,found_access=0; + uint found=0; + ulong found_access=0; TABLE_LIST *org_tables=tables; for (; tables ; tables=tables->next) { @@ -2220,20 +2607,20 @@ check_table_access(THD *thd,uint want_access,TABLE_LIST *tables) tables->grant.privilege=found_access; else { - if (check_access(thd,want_access,tables->db,&tables->grant.privilege)) + if (check_access(thd,want_access,tables->db,&tables->grant.privilege, + 0, no_errors)) return TRUE; // Access denied found_access=tables->grant.privilege; found=1; } } - else if (check_access(thd,want_access,tables->db,&tables->grant.privilege)) - return TRUE; // Access denied + else if (check_access(thd,want_access,tables->db,&tables->grant.privilege, + 0, no_errors)) + return TRUE; } if (grant_option) - { - want_access &= ~EXTRA_ACL; // Remove SHOW attribute - return check_grant(thd,want_access,org_tables); - } + return check_grant(thd,want_access & ~EXTRA_ACL,org_tables, + test(want_access & EXTRA_ACL), no_errors); return FALSE; } @@ -2337,40 +2724,77 @@ bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, int *yystacksize) /**************************************************************************** - Initialize global thd variables neaded for query + Initialize global thd variables needed for query ****************************************************************************/ static void mysql_init_query(THD *thd) { DBUG_ENTER("mysql_init_query"); - thd->lex.item_list.empty(); + thd->lex.select_lex.item_list.empty(); thd->lex.value_list.empty(); - thd->lex.table_list.elements=0; - thd->free_list=0; - - thd->lex.table_list.first=0; - thd->lex.table_list.next= (byte**) &thd->lex.table_list.first; + thd->lex.select_lex.table_list.elements=0; + thd->free_list=0; thd->lex.union_option=0; + thd->lex.select = &thd->lex.select_lex; + thd->lex.select_lex.table_list.first=0; + thd->lex.select_lex.table_list.next= (byte**) &thd->lex.select_lex.table_list.first; + thd->lex.select_lex.next=0; + thd->lex.olap=0; + thd->lex.select->olap= UNSPECIFIED_OLAP_TYPE; thd->fatal_error=0; // Safety thd->last_insert_id_used=thd->query_start_used=thd->insert_id_used=0; + thd->rand_used=0; thd->sent_row_count=thd->examined_row_count=0; + thd->safe_to_cache_query=1; DBUG_VOID_RETURN; } void mysql_init_select(LEX *lex) { - lex->where=lex->having=0; - lex->select_limit=current_thd->default_select_limit; - lex->offset_limit=0L; - lex->options=0; + SELECT_LEX *select_lex = lex->select; + select_lex->where=select_lex->having=0; + select_lex->select_limit= lex->thd->variables.select_limit; + select_lex->offset_limit=0; + select_lex->options=0; + select_lex->linkage=UNSPECIFIED_TYPE; + select_lex->olap= UNSPECIFIED_OLAP_TYPE; lex->exchange = 0; lex->proc_list.first=0; - lex->order_list.elements=lex->group_list.elements=0; - lex->order_list.first=0; - lex->order_list.next= (byte**) &lex->order_list.first; - lex->group_list.first=0; - lex->group_list.next= (byte**) &lex->group_list.first; + select_lex->order_list.empty(); + select_lex->group_list.empty(); + select_lex->next = (SELECT_LEX *)NULL; +} + + +bool +mysql_new_select(LEX *lex) +{ + SELECT_LEX *select_lex = (SELECT_LEX *) lex->thd->calloc(sizeof(SELECT_LEX)); + if (!select_lex) + return 1; + lex->select->next=select_lex; + lex->select=select_lex; + select_lex->table_list.next= (byte**) &select_lex->table_list.first; + select_lex->item_list.empty(); + select_lex->when_list.empty(); + select_lex->expr_list.empty(); + select_lex->interval_list.empty(); + select_lex->use_index.empty(); + select_lex->ftfunc_list.empty(); + return 0; +} + + +void mysql_init_multi_delete(LEX *lex) +{ + lex->sql_command = SQLCOM_DELETE_MULTI; + mysql_init_select(lex); + lex->select->select_limit=lex->thd->select_limit=HA_POS_ERROR; + lex->auxilliary_table_list=lex->select_lex.table_list; + lex->select->table_list.elements=0; + lex->select->table_list.first=0; + lex->select->table_list.next= (byte**) &(lex->select->table_list.first); } @@ -2381,26 +2805,36 @@ mysql_parse(THD *thd,char *inBuf,uint length) mysql_init_query(thd); thd->query_length = length; - LEX *lex=lex_start(thd, (uchar*) inBuf, length); - if (!yyparse() && ! thd->fatal_error) - mysql_execute_command(); - thd->proc_info="freeing items"; - free_items(thd); /* Free strings used by items */ - lex_end(lex); + if (query_cache_send_result_to_client(thd, inBuf, length) <= 0) + { + LEX *lex=lex_start(thd, (uchar*) inBuf, length); + if (!yyparse() && ! thd->fatal_error) + { + if (mqh_used && thd->user_connect && + check_mqh(thd, thd->lex.sql_command)) + { + thd->net.error = 0; + } + else + { + mysql_execute_command(); + query_cache_end_of_result(&thd->net); + } + } + else + { + DBUG_PRINT("info",("Command aborted. Fatal_error: %d", + thd->fatal_error)); + query_cache_abort(&thd->net); + } + thd->proc_info="freeing items"; + free_items(thd); /* Free strings used by items */ + lex_end(lex); + } DBUG_VOID_RETURN; } -inline static void -link_in_list(SQL_LIST *list,byte *element,byte **next) -{ - list->elements++; - (*list->next)=element; - list->next=next; - *next=0; -} - - /***************************************************************************** ** Store field definition for create ** Return 0 if ok @@ -2496,6 +2930,7 @@ bool add_field_to_list(char *field_name, enum_field_types type, case FIELD_TYPE_STRING: case FIELD_TYPE_VAR_STRING: case FIELD_TYPE_NULL: + case FIELD_TYPE_GEOMETRY: break; case FIELD_TYPE_DECIMAL: if (!length) @@ -2643,7 +3078,7 @@ bool add_field_to_list(char *field_name, enum_field_types type, if (new_field->length >= MAX_FIELD_WIDTH || (!new_field->length && !(new_field->flags & BLOB_FLAG) && - type != FIELD_TYPE_STRING)) + type != FIELD_TYPE_STRING && type != FIELD_TYPE_VAR_STRING)) { net_printf(&thd->net,ER_TOO_BIG_FIELDLENGTH,field_name, MAX_FIELD_WIDTH-1); /* purecov: inspected */ @@ -2674,7 +3109,7 @@ void store_position_for_column(const char *name) } bool -add_proc_to_list(Item *item) +add_proc_to_list(THD* thd, Item *item) { ORDER *order; Item **item_ptr; @@ -2685,7 +3120,7 @@ add_proc_to_list(Item *item) *item_ptr= item; order->item=item_ptr; order->free_me=0; - link_in_list(¤t_lex->proc_list,(byte*) order,(byte**) &order->next); + thd->lex.proc_list.link_in_list((byte*) order,(byte**) &order->next); return 0; } @@ -2694,6 +3129,8 @@ add_proc_to_list(Item *item) static void remove_escape(char *name) { + if (!*name) // For empty DB names + return; char *to; #ifdef USE_MB char *strend=name+(uint) strlen(name); @@ -2713,7 +3150,7 @@ static void remove_escape(char *name) } #endif if (*name == '\\' && name[1]) - name++; // Skipp '\\' + name++; // Skip '\\' *to++= *name; } *to=0; @@ -2737,22 +3174,39 @@ bool add_to_list(SQL_LIST &list,Item *item,bool asc) order->asc = asc; order->free_me=0; order->used=0; - link_in_list(&list,(byte*) order,(byte**) &order->next); + list.link_in_list((byte*) order,(byte**) &order->next); DBUG_RETURN(0); } +/* + Add a table to list of used tables + + SYNOPSIS + add_table_to_list() + table Table to add + alias alias for table (or null if no alias) + table_options A set of the following bits: + TL_OPTION_UPDATING Table will be updated + TL_OPTION_FORCE_INDEX Force usage of index + lock_type How table should be locked + use_index List of indexed used in USE INDEX + ignore_index List of indexed used in IGNORE INDEX + + RETURN + 0 Error + # Pointer to TABLE_LIST element added to the total table list +*/ + TABLE_LIST *add_table_to_list(Table_ident *table, LEX_STRING *alias, - bool updating, - thr_lock_type flags, + ulong table_options, + thr_lock_type lock_type, List<String> *use_index, - List<String> *ignore_index - ) + List<String> *ignore_index) { register TABLE_LIST *ptr; THD *thd=current_thd; char *alias_str; - const char *current_db; DBUG_ENTER("add_table_to_list"); if (!table) @@ -2767,17 +3221,35 @@ TABLE_LIST *add_table_to_list(Table_ident *table, LEX_STRING *alias, } if (!alias) /* Alias is case sensitive */ - if (!(alias_str=sql_strmake(alias_str,table->table.length))) + if (!(alias_str=thd->memdup(alias_str,table->table.length+1))) DBUG_RETURN(0); - if (lower_case_table_names) - casedn_str(table->table.str); + if (!(ptr = (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST)))) DBUG_RETURN(0); /* purecov: inspected */ - ptr->db= table->db.str; + if (table->db.str) + { + ptr->db= table->db.str; + ptr->db_length= table->db.length; + } + else if (thd->db) + { + ptr->db= thd->db; + ptr->db_length= thd->db_length; + } + else + { + ptr->db= (char*) ""; + ptr->db_length= 0; + } + + ptr->alias= alias_str; + if (lower_case_table_names) + casedn_str(table->table.str); ptr->real_name=table->table.str; - ptr->alias=alias_str; - ptr->lock_type=flags; - ptr->updating=updating; + ptr->real_name_length=table->table.length; + ptr->lock_type= lock_type; + ptr->updating= test(table_options & TL_OPTION_UPDATING); + ptr->force_index= test(table_options & TL_OPTION_FORCE_INDEX); if (use_index) ptr->use_index=(List<String> *) thd->memdup((gptr) use_index, sizeof(*use_index)); @@ -2786,34 +3258,128 @@ TABLE_LIST *add_table_to_list(Table_ident *table, LEX_STRING *alias, sizeof(*ignore_index)); /* check that used name is unique */ - current_db=thd->db ? thd->db : ""; - - if (flags != TL_IGNORE) + if (lock_type != TL_IGNORE) { - for (TABLE_LIST *tables=(TABLE_LIST*) thd->lex.table_list.first ; tables ; + for (TABLE_LIST *tables=(TABLE_LIST*) thd->lex.select->table_list.first ; + tables ; tables=tables->next) { - if (!strcmp(alias_str,tables->alias) && - !strcmp(ptr->db ? ptr->db : current_db, - tables->db ? tables->db : current_db)) + if (!strcmp(alias_str,tables->alias) && !strcmp(ptr->db, tables->db)) { net_printf(&thd->net,ER_NONUNIQ_TABLE,alias_str); /* purecov: tested */ DBUG_RETURN(0); /* purecov: tested */ } } } - link_in_list(&thd->lex.table_list,(byte*) ptr,(byte**) &ptr->next); + thd->lex.select->table_list.link_in_list((byte*) ptr,(byte**) &ptr->next); DBUG_RETURN(ptr); } +/* + Set lock for all tables in current select level + + SYNOPSIS: + set_lock_for_tables() + lock_type Lock to set for tables + + NOTE: + If lock is a write lock, then tables->updating is set 1 + This is to get tables_ok to know that the table is updated by the + query +*/ + +void set_lock_for_tables(thr_lock_type lock_type) +{ + THD *thd=current_thd; + bool for_update= lock_type >= TL_READ_NO_INSERT; + DBUG_ENTER("set_lock_for_tables"); + DBUG_PRINT("enter", ("lock_type: %d for_update: %d", lock_type, + for_update)); + + for (TABLE_LIST *tables= (TABLE_LIST*) thd->lex.select->table_list.first ; + tables ; + tables=tables->next) + { + tables->lock_type= lock_type; + tables->updating= for_update; + } + DBUG_VOID_RETURN; +} + + +/* +** This is used for UNION to create a new table list of all used tables +** The table_list->table entry in all used tables are set to point +** to the entries in this list. +*/ + +static bool create_total_list(THD *thd, LEX *lex, TABLE_LIST **result) +{ + /* Handle the case when we are not using union */ + if (!lex->select_lex.next) + { + *result= (TABLE_LIST*) lex->select_lex.table_list.first; + return 0; + } + + SELECT_LEX *sl; + TABLE_LIST **new_table_list= result, *aux; + + *new_table_list=0; // end result list + for (sl= &lex->select_lex; sl; sl=sl->next) + { + if (sl->order_list.first && sl->next && !sl->braces) + { + net_printf(&thd->net,ER_WRONG_USAGE,"UNION","ORDER BY"); + return 1; + } + if ((aux= (TABLE_LIST*) sl->table_list.first)) + { + TABLE_LIST *next; + for (; aux; aux=next) + { + TABLE_LIST *cursor; + next= aux->next; + for (cursor= *result; cursor; cursor=cursor->next) + if (!strcmp(cursor->db,aux->db) && + !strcmp(cursor->real_name,aux->real_name) && + !strcmp(cursor->alias, aux->alias)) + break; + if (!cursor) + { + /* Add not used table to the total table list */ + if (!(cursor = (TABLE_LIST *) thd->memdup((char*) aux, + sizeof(*aux)))) + { + send_error(&thd->net,0); + return 1; + } + *new_table_list= cursor; + new_table_list= &cursor->next; + *new_table_list=0; // end result list + } + else + aux->shared=1; // Mark that it's used twice + aux->table= my_reinterpret_cast(TABLE *) (cursor); + } + } + } + return 0; +} + + void add_join_on(TABLE_LIST *b,Item *expr) { - if (!b->on_expr) - b->on_expr=expr; - else + if (expr) { - // This only happens if you have both a right and left join - b->on_expr=new Item_cond_and(b->on_expr,expr); + if (!b->on_expr) + b->on_expr=expr; + else + { + // This only happens if you have both a right and left join + b->on_expr=new Item_cond_and(b->on_expr,expr); + } + b->on_expr->top_level_item(); } } @@ -2825,43 +3391,59 @@ void add_join_natural(TABLE_LIST *a,TABLE_LIST *b) /* Check if name is used in table list */ -static bool check_dup(THD *thd,const char *db,const char *name, - TABLE_LIST *tables) +static bool check_dup(const char *db, const char *name, TABLE_LIST *tables) { - const char *thd_db=thd->db ? thd->db : any_db; for (; tables ; tables=tables->next) - if (!strcmp(name,tables->real_name) && - !strcmp(db ? db : thd_db, tables->db ? tables->db : thd_db)) + if (!strcmp(name,tables->real_name) && !strcmp(db,tables->db)) return 1; return 0; } -bool reload_acl_and_cache(THD *thd, uint options, TABLE_LIST *tables) + +/* + Reload/resets privileges and the different caches +*/ + +bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables) { bool result=0; - + bool error_already_sent=0; select_errors=0; /* Write if more errors */ - // mysql_log.flush(); // Flush log if (options & REFRESH_GRANT) { - acl_reload(); - grant_reload(); + acl_reload(thd); + grant_reload(thd); + if (mqh_used) + reset_mqh(thd,(LEX_USER *) NULL,true); } if (options & REFRESH_LOG) { - mysql_log.new_file(0); - mysql_update_log.new_file(0); - mysql_bin_log.new_file(0); - mysql_slow_log.new_file(0); + mysql_log.new_file(1); + mysql_update_log.new_file(1); + mysql_bin_log.new_file(1); + mysql_slow_log.new_file(1); if (ha_flush_logs()) result=1; + if (flush_error_log()) + result=1; + } +#ifdef HAVE_QUERY_CACHE + if (options & REFRESH_QUERY_CACHE_FREE) + { + query_cache.pack(); // FLUSH QUERY CACHE + options &= ~REFRESH_QUERY_CACHE; //don't flush all cache, just free memory } + if (options & (REFRESH_TABLES | REFRESH_QUERY_CACHE)) + { + query_cache.flush(); // RESET QUERY CACHE + } +#endif /*HAVE_QUERY_CACHE*/ if (options & (REFRESH_TABLES | REFRESH_READ_LOCK)) { - if ((options & REFRESH_READ_LOCK) && thd && ! thd->global_read_lock) + if ((options & REFRESH_READ_LOCK) && thd) { - thd->global_read_lock=1; - thread_safe_increment(global_read_lock,&LOCK_open); + if (lock_global_read_lock(thd)) + return 1; } result=close_cached_tables(thd,(options & REFRESH_FAST) ? 0 : 1, tables); } @@ -2872,36 +3454,86 @@ bool reload_acl_and_cache(THD *thd, uint options, TABLE_LIST *tables) if (options & REFRESH_THREADS) flush_thread_cache(); if (options & REFRESH_MASTER) - reset_master(); - if (options & REFRESH_SLAVE) - reset_slave(); - - return result; + if (reset_master(thd)) + result=1; +#ifdef OPENSSL + if (options & REFRESH_DES_KEY_FILE) + { + if (des_key_file) + result=load_des_key_file(des_key_file); + } +#endif + if (options & REFRESH_SLAVE) + { + LOCK_ACTIVE_MI; + if (reset_slave(thd, active_mi)) + { + result=1; + /* + reset_slave() sends error itself. + If it didn't, one would either change reset_slave()'s prototype, to + pass *errorcode and *errmsg to it when it's called or + change reset_slave to use my_error() to register the error. + */ + error_already_sent=1; + } + UNLOCK_ACTIVE_MI; + } + if (options & REFRESH_USER_RESOURCES) + reset_mqh(thd,(LEX_USER *) NULL); + + if (thd && !error_already_sent) + { + if (result) + send_error(&thd->net,0); + else + send_ok(&thd->net); + } + + return result; } +/* + kill on thread + + SYNOPSIS + kill_one_thread() + thd Thread class + id Thread id + + NOTES + This is written such that we have a short lock on LOCK_thread_count +*/ + void kill_one_thread(THD *thd, ulong id) { - VOID(pthread_mutex_lock(&LOCK_thread_count)); // For unlink from list - I_List_iterator<THD> it(threads); THD *tmp; uint error=ER_NO_SUCH_THREAD; + VOID(pthread_mutex_lock(&LOCK_thread_count)); // For unlink from list + I_List_iterator<THD> it(threads); while ((tmp=it++)) { if (tmp->thread_id == id) { - if ((thd->master_access & PROCESS_ACL) || - !strcmp(thd->user,tmp->user)) - { - tmp->prepare_to_die(); - error=0; - } - else - error=ER_KILL_DENIED_ERROR; - break; // Found thread + pthread_mutex_lock(&tmp->LOCK_delete); // Lock from delete + break; } } VOID(pthread_mutex_unlock(&LOCK_thread_count)); + if (tmp) + { + if ((thd->master_access & SUPER_ACL) || + !strcmp(thd->user,tmp->user)) + { + tmp->awake(1 /*prepare to die*/); + error=0; + } + else + error=ER_KILL_DENIED_ERROR; + pthread_mutex_unlock(&tmp->LOCK_delete); + } + if (!error) send_ok(&thd->net); else @@ -2922,3 +3554,54 @@ static void refresh_status(void) pthread_mutex_unlock(&LOCK_status); pthread_mutex_unlock(&THR_LOCK_keycache); } + + + /* If pointer is not a null pointer, append filename to it */ + +static bool append_file_to_dir(THD *thd, char **filename_ptr, char *table_name) +{ + char buff[FN_REFLEN],*ptr, *end; + if (!*filename_ptr) + return 0; // nothing to do + + /* Check that the filename is not too long and it's a hard path */ + if (strlen(*filename_ptr)+strlen(table_name) >= FN_REFLEN-1 || + !test_if_hard_path(*filename_ptr)) + { + my_error(ER_WRONG_TABLE_NAME, MYF(0), *filename_ptr); + return 1; + } + /* Fix is using unix filename format on dos */ + strmov(buff,*filename_ptr); + end=convert_dirname(buff, *filename_ptr, NullS); + if (!(ptr=thd->alloc((uint) (end-buff)+(uint) strlen(table_name)+1))) + return 1; // End of memory + *filename_ptr=ptr; + strxmov(ptr,buff,table_name,NullS); + return 0; +} + +/* + Check if the select is a simple select (not an union) + + SYNOPSIS + check_simple_select() + + RETURN VALUES + 0 ok + 1 error ; In this case the error messege is sent to the client +*/ + +bool check_simple_select() +{ + THD *thd= current_thd; + if (thd->lex.select != &thd->lex.select_lex) + { + char command[80]; + strmake(command, thd->lex.yylval->symbol.str, + min(thd->lex.yylval->symbol.length, sizeof(command)-1)); + net_printf(&thd->net, ER_CANT_USE_OPTION_HERE, command); + return 1; + } + return 0; +} diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc index 9eee8ccf7ac..1bb89060167 100644 --- a/sql/sql_rename.cc +++ b/sql/sql_rename.cc @@ -31,19 +31,21 @@ static TABLE_LIST *rename_tables(THD *thd, TABLE_LIST *table_list, bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list) { - bool error=1, cerror; - TABLE_LIST *ren_table= 0; + bool error=1; + TABLE_LIST *ren_table=0; DBUG_ENTER("mysql_rename_tables"); - - /* Avoid problems with a rename on a table that we have locked or - if the user is trying to to do this in a transcation context */ + + /* + Avoid problems with a rename on a table that we have locked or + if the user is trying to to do this in a transcation context + */ if (thd->locked_tables || thd->active_transaction()) { my_error(ER_LOCK_OR_ACTIVE_TRANSACTION,MYF(0)); DBUG_RETURN(1); } - + VOID(pthread_mutex_lock(&LOCK_open)); if (lock_table_names(thd, table_list)) goto err; @@ -68,24 +70,19 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list) for (table=table_list ; table->next != ren_table ; table=table->next->next) ; - table=table->next->next; // Skipp error table + table=table->next->next; // Skip error table /* Revert to old names */ rename_tables(thd, table, 1); error= 1; } /* Lets hope this doesn't fail as the result will be messy */ - if ((cerror=ha_commit_rename(thd))) - { - my_error(ER_GET_ERRNO,MYF(0),cerror); - error= 1; - } - else if (!error) + if (!error) { mysql_update_log.write(thd,thd->query,thd->query_length); if (mysql_bin_log.is_open()) { - Query_log_event qinfo(thd, thd->query); + Query_log_event qinfo(thd, thd->query, thd->query_length, 0); mysql_bin_log.write(&qinfo); } send_ok(&thd->net); @@ -122,7 +119,7 @@ rename_tables(THD *thd, TABLE_LIST *table_list, bool skip_error) if (!access(name,F_OK)) { my_error(ER_TABLE_EXISTS_ERROR,MYF(0),name); - return ren_table; // This can't be skipped + DBUG_RETURN(ren_table); // This can't be skiped } sprintf(name,"%s/%s/%s%s",mysql_data_home, ren_table->db,ren_table->real_name, @@ -131,7 +128,7 @@ rename_tables(THD *thd, TABLE_LIST *table_list, bool skip_error) { my_error(ER_FILE_NOT_FOUND, MYF(0), name, my_errno); if (!skip_error) - return ren_table; + DBUG_RETURN(ren_table); } else if (mysql_rename_table(table_type, ren_table->db, ren_table->real_name, diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index 1940ff360c2..5e90bbf1b0f 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB & Sasha This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -15,102 +15,118 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ // Sasha Pachev <sasha@mysql.com> is currently in charge of this file -// Do not mess with it without his permission! #include "mysql_priv.h" #include "sql_repl.h" #include "sql_acl.h" #include "log_event.h" -#include <thr_alarm.h> +#include "mini_client.h" #include <my_dir.h> +#include <assert.h> extern const char* any_db; -extern pthread_handler_decl(handle_slave,arg); -#ifndef DBUG_OFF int max_binlog_dump_events = 0; // unlimited -bool opt_sporadic_binlog_dump_fail = 0; +my_bool opt_sporadic_binlog_dump_fail = 0; static int binlog_dump_count = 0; -#endif + +int check_binlog_magic(IO_CACHE* log, const char** errmsg) +{ + char magic[4]; + DBUG_ASSERT(my_b_tell(log) == 0); + + if (my_b_read(log, (byte*) magic, sizeof(magic))) + { + *errmsg = "I/O error reading the header from the binary log"; + sql_print_error("%s, errno=%d, io cache code=%d", *errmsg, my_errno, + log->error); + return 1; + } + if (memcmp(magic, BINLOG_MAGIC, sizeof(magic))) + { + *errmsg = "Binlog has bad magic number; It's not a binary log file that can be used by this version of MySQL"; + return 1; + } + return 0; +} static int fake_rotate_event(NET* net, String* packet, char* log_file_name, const char**errmsg) { - char header[LOG_EVENT_HEADER_LEN]; + char header[LOG_EVENT_HEADER_LEN], buf[ROTATE_HEADER_LEN]; memset(header, 0, 4); // when does not matter header[EVENT_TYPE_OFFSET] = ROTATE_EVENT; - char* p = strrchr(log_file_name, FN_LIBCHAR); - // find the last slash - if(p) - p++; - else - p = log_file_name; + char* p = log_file_name+dirname_length(log_file_name); uint ident_len = (uint) strlen(p); - ulong event_len = ident_len + sizeof(header); - int4store(header + EVENT_TYPE_OFFSET + 1, server_id); + ulong event_len = ident_len + ROTATE_EVENT_OVERHEAD; + int4store(header + SERVER_ID_OFFSET, server_id); int4store(header + EVENT_LEN_OFFSET, event_len); + int2store(header + FLAGS_OFFSET, 0); + + // TODO: check what problems this may cause and fix them + int4store(header + LOG_POS_OFFSET, 0); + packet->append(header, sizeof(header)); + /* We need to split the next statement because of problem with cxx */ + int4store(buf,4); // tell slave to skip magic number + int4store(buf+4,0); + packet->append(buf, ROTATE_HEADER_LEN); packet->append(p,ident_len); - if(my_net_write(net, (char*)packet->ptr(), packet->length())) - { - *errmsg = "failed on my_net_write()"; - return -1; - } + if (my_net_write(net, (char*)packet->ptr(), packet->length())) + { + *errmsg = "failed on my_net_write()"; + return -1; + } return 0; } - static int send_file(THD *thd) { NET* net = &thd->net; int fd = -1,bytes, error = 1; char fname[FN_REFLEN+1]; - char *buf; const char *errmsg = 0; int old_timeout; uint packet_len; + char buf[IO_SIZE]; // It's safe to alloc this DBUG_ENTER("send_file"); - // the client might be slow loading the data, give him wait_timeout to do - // the job - old_timeout = thd->net.timeout; - thd->net.timeout = thd->inactive_timeout; - - // spare the stack - if(!(buf = alloc_root(&thd->mem_root,IO_SIZE))) - { - errmsg = "Out of memory"; - goto err; - } - - // we need net_flush here because the client will not know it needs to send - // us the file name until it has processed the load event entry + /* + The client might be slow loading the data, give him wait_timeout to do + the job + */ + old_timeout = thd->net.read_timeout; + thd->net.read_timeout = thd->variables.net_wait_timeout; + + /* + We need net_flush here because the client will not know it needs to send + us the file name until it has processed the load event entry + */ if (net_flush(net) || (packet_len = my_net_read(net)) == packet_error) { - errmsg = "Failed reading file name"; + errmsg = "while reading file name"; goto err; } - *((char*)net->read_pos + packet_len) = 0; // terminate with \0 - //for fn_format - fn_format(fname, (char*)net->read_pos + 1, "", "", 4); + // terminate with \0 for fn_format + *((char*)net->read_pos + packet_len) = 0; + fn_format(fname, (char*) net->read_pos + 1, "", "", 4); // this is needed to make replicate-ignore-db if (!strcmp(fname,"/dev/null")) goto end; - if ((fd = my_open(fname, O_RDONLY, MYF(MY_WME))) < 0) + if ((fd = my_open(fname, O_RDONLY, MYF(0))) < 0) { - errmsg = "Failed on my_open()"; + errmsg = "on open of file"; goto err; } - while ((bytes = (int) my_read(fd, (byte*) buf, IO_SIZE, - MYF(MY_WME))) > 0) + while ((bytes = (int) my_read(fd, (byte*) buf, IO_SIZE, MYF(0))) > 0) { if (my_net_write(net, buf, bytes)) { - errmsg = "Failed on my_net_write()"; + errmsg = "while writing data to client"; goto err; } } @@ -119,18 +135,18 @@ static int send_file(THD *thd) if (my_net_write(net, "", 0) || net_flush(net) || (my_net_read(net) == packet_error)) { - errmsg = "failed negotiating file transfer close"; + errmsg = "while negotiating file transfer close"; goto err; } error = 0; err: - thd->net.timeout = old_timeout; - if(fd >= 0) - (void) my_close(fd, MYF(MY_WME)); + thd->net.read_timeout = old_timeout; + if (fd >= 0) + (void) my_close(fd, MYF(0)); if (errmsg) { - sql_print_error("failed in send_file() : %s", errmsg); + sql_print_error("Failed in send_file() %s", errmsg); DBUG_PRINT("error", (errmsg)); } DBUG_RETURN(error); @@ -138,61 +154,78 @@ static int send_file(THD *thd) File open_binlog(IO_CACHE *log, const char *log_file_name, - const char **errmsg) + const char **errmsg) { File file; - char magic[4]; + DBUG_ENTER("open_binlog"); + if ((file = my_open(log_file_name, O_RDONLY | O_BINARY, MYF(MY_WME))) < 0 || init_io_cache(log, file, IO_SIZE*2, READ_CACHE, 0, 0, - MYF(MY_WME))) - { - *errmsg = "Could not open log file"; // This will not be sent - goto err; - } - - if (my_b_read(log, (byte*) magic, sizeof(magic))) + MYF(MY_WME | MY_DONT_CHECK_FILESIZE))) { - *errmsg = "I/O error reading binlog magic number"; + *errmsg = "Could not open log file"; // This will not be sent goto err; } - if (memcmp(magic, BINLOG_MAGIC, 4)) - { - *errmsg = "Binlog has bad magic number, fire your magician"; + if (check_binlog_magic(log,errmsg)) goto err; - } - return file; + DBUG_RETURN(file); err: - if (file > 0) + if (file >= 0) + { my_close(file,MYF(0)); - end_io_cache(log); - return -1; + end_io_cache(log); + } + DBUG_RETURN(-1); } +/* + Adjust the position pointer in the binary log file for all running slaves + + SYNOPSIS + adjust_linfo_offsets() + purge_offset Number of bytes removed from start of log index file + + NOTES + - This is called when doing a PURGE when we delete lines from the + index log file + + REQUIREMENTS + - Before calling this function, we have to ensure that no threads are + using any binary log file before purge_offset.a + + TODO + - Inform the slave threads that they should sync the position + in the binary log file with flush_relay_log_info. + Now they sync is done for next read. +*/ + void adjust_linfo_offsets(my_off_t purge_offset) { THD *tmp; - + pthread_mutex_lock(&LOCK_thread_count); I_List_iterator<THD> it(threads); - - while((tmp=it++)) - { - LOG_INFO* linfo; - if((linfo = tmp->current_linfo)) - { - pthread_mutex_lock(&linfo->lock); - // no big deal if we just started reading the log - // nothing to adjust - if(linfo->index_file_offset < purge_offset) - linfo->fatal = (linfo->index_file_offset != 0); - else - linfo->index_file_offset -= purge_offset; - pthread_mutex_unlock(&linfo->lock); - } - } + while ((tmp=it++)) + { + LOG_INFO* linfo; + if ((linfo = tmp->current_linfo)) + { + pthread_mutex_lock(&linfo->lock); + /* + Index file offset can be less that purge offset only if + we just started reading the index file. In that case + we have nothing to adjust + */ + if (linfo->index_file_offset < purge_offset) + linfo->fatal = (linfo->index_file_offset != 0); + else + linfo->index_file_offset -= purge_offset; + pthread_mutex_unlock(&linfo->lock); + } + } pthread_mutex_unlock(&LOCK_thread_count); } @@ -202,21 +235,22 @@ bool log_in_use(const char* log_name) int log_name_len = strlen(log_name) + 1; THD *tmp; bool result = 0; - + pthread_mutex_lock(&LOCK_thread_count); I_List_iterator<THD> it(threads); - - while((tmp=it++)) + + while ((tmp=it++)) + { + LOG_INFO* linfo; + if ((linfo = tmp->current_linfo)) { - LOG_INFO* linfo; - if((linfo = tmp->current_linfo)) - { - pthread_mutex_lock(&linfo->lock); - result = !memcmp(log_name, linfo->log_file_name, log_name_len); - pthread_mutex_unlock(&linfo->lock); - if(result) break; - } - } + pthread_mutex_lock(&linfo->lock); + result = !memcmp(log_name, linfo->log_file_name, log_name_len); + pthread_mutex_unlock(&linfo->lock); + if (result) + break; + } + } pthread_mutex_unlock(&LOCK_thread_count); return result; @@ -226,44 +260,48 @@ bool log_in_use(const char* log_name) int purge_master_logs(THD* thd, const char* to_log) { char search_file_name[FN_REFLEN]; + const char* errmsg = 0; + mysql_bin_log.make_log_name(search_file_name, to_log); int res = mysql_bin_log.purge_logs(thd, search_file_name); - const char* errmsg = 0; - switch(res) - { - case 0: break; - case LOG_INFO_EOF: errmsg = "Target log not found in binlog index"; break; - case LOG_INFO_IO: errmsg = "I/O error reading log index file"; break; - case LOG_INFO_INVALID: errmsg = "Server configuration does not permit \ + + switch(res) { + case 0: break; + case LOG_INFO_EOF: errmsg = "Target log not found in binlog index"; break; + case LOG_INFO_IO: errmsg = "I/O error reading log index file"; break; + case LOG_INFO_INVALID: errmsg = "Server configuration does not permit \ binlog purge"; break; - case LOG_INFO_SEEK: errmsg = "Failed on fseek()"; break; - case LOG_INFO_PURGE_NO_ROTATE: errmsg = "Cannot purge unrotatable log"; - break; - case LOG_INFO_MEM: errmsg = "Out of memory"; break; - case LOG_INFO_FATAL: errmsg = "Fatal error during purge"; break; - case LOG_INFO_IN_USE: errmsg = "A purgeable log is in use, will not purge"; - break; - default: - errmsg = "Unknown error during purge"; break; - } - - if(errmsg) - { - send_error(&thd->net, 0, errmsg); - return 1; - } + case LOG_INFO_SEEK: errmsg = "Failed on fseek()"; break; + case LOG_INFO_PURGE_NO_ROTATE: errmsg = "Cannot purge unrotatable log"; + break; + case LOG_INFO_MEM: errmsg = "Out of memory"; break; + case LOG_INFO_FATAL: errmsg = "Fatal error during purge"; break; + case LOG_INFO_IN_USE: errmsg = "A purgeable log is in use, will not purge"; + break; + default: errmsg = "Unknown error during purge"; break; + } + + if (errmsg) + { + send_error(&thd->net, 0, errmsg); + return 1; + } else send_ok(&thd->net); - + return 0; } +/* + TODO: Clean up loop to only have one call to send_file() +*/ -void mysql_binlog_send(THD* thd, char* log_ident, ulong pos, ushort flags) +void mysql_binlog_send(THD* thd, char* log_ident, my_off_t pos, + ushort flags) { LOG_INFO linfo; char *log_file_name = linfo.log_file_name; - char search_file_name[FN_REFLEN]; + char search_file_name[FN_REFLEN], *name; IO_CACHE log; File file = -1; String* packet = &thd->packet; @@ -272,112 +310,130 @@ void mysql_binlog_send(THD* thd, char* log_ident, ulong pos, ushort flags) NET* net = &thd->net; #ifndef DBUG_OFF int left_events = max_binlog_dump_events; -#endif +#endif DBUG_ENTER("mysql_binlog_send"); + DBUG_PRINT("enter",("log_ident: '%s' pos: %ld", log_ident, (long) pos)); + bzero((char*) &log,sizeof(log)); #ifndef DBUG_OFF if (opt_sporadic_binlog_dump_fail && (binlog_dump_count++ % 2)) { errmsg = "Master failed COM_BINLOG_DUMP to test if slave can recover"; + my_errno= ER_UNKNOWN_ERROR; goto err; } -#endif - +#endif - if(!mysql_bin_log.is_open()) + if (!mysql_bin_log.is_open()) { errmsg = "Binary log is not open"; + my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG; goto err; } - if(!server_id_supplied) - { - errmsg = "Misconfigured master - server id was not set"; - goto err; - } - + if (!server_id_supplied) + { + errmsg = "Misconfigured master - server id was not set"; + my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG; + goto err; + } + + name=search_file_name; if (log_ident[0]) mysql_bin_log.make_log_name(search_file_name, log_ident); else - search_file_name[0] = 0; - + name=0; // Find first log + linfo.index_file_offset = 0; thd->current_linfo = &linfo; - if (mysql_bin_log.find_first_log(&linfo, search_file_name)) + if (mysql_bin_log.find_log_pos(&linfo, name, 1)) { - errmsg = "Could not find first log"; + errmsg = "Could not find first log file name in binary log index file"; + my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG; goto err; } if ((file=open_binlog(&log, log_file_name, &errmsg)) < 0) + { + my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG; goto err; - - if (pos < 4) + } + if (pos < BIN_LOG_HEADER_SIZE || pos > my_b_filelength(&log)) { - errmsg = "Client requested master to start repliction from impossible position.\n"; + errmsg= "Client requested master to start replication from \ +impossible position"; + my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG; goto err; } - + my_b_seek(&log, pos); // Seek will done on next read - packet->length(0); - packet->append("\0", 1); - // we need to start a packet with something other than 255 - // to distiquish it from error + /* + We need to start a packet with something other than 255 + to distiquish it from error + */ + packet->set("\0", 1); - // tell the client log name with a fake rotate_event // if we are at the start of the log - if(pos == 4) + if (pos == BIN_LOG_HEADER_SIZE) { + // tell the client log name with a fake rotate_event if (fake_rotate_event(net, packet, log_file_name, &errmsg)) + { + my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG; goto err; - packet->length(0); - packet->append("\0", 1); + } + packet->set("\0", 1); } while (!net->error && net->vio != 0 && !thd->killed) { pthread_mutex_t *log_lock = mysql_bin_log.get_log_lock(); - + while (!(error = Log_event::read_log_event(&log, packet, log_lock))) { #ifndef DBUG_OFF - if(max_binlog_dump_events && !left_events--) + if (max_binlog_dump_events && !left_events--) { net_flush(net); errmsg = "Debugging binlog dump abort"; + my_errno= ER_UNKNOWN_ERROR; goto err; } -#endif +#endif if (my_net_write(net, (char*)packet->ptr(), packet->length()) ) { errmsg = "Failed on my_net_write()"; + my_errno= ER_UNKNOWN_ERROR; goto err; } DBUG_PRINT("info", ("log event code %d", (*packet)[LOG_EVENT_OFFSET+1] )); if ((*packet)[LOG_EVENT_OFFSET+1] == LOAD_EVENT) { - if(send_file(thd)) + if (send_file(thd)) { errmsg = "failed in send_file()"; + my_errno= ER_UNKNOWN_ERROR; goto err; } } - packet->length(0); - packet->append("\0",1); + packet->set("\0", 1); } - + /* + TODO: now that we are logging the offset, check to make sure + the recorded offset and the actual match + */ if (error != LOG_READ_EOF) { - switch(error) - { - case LOG_READ_BOGUS: + my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG; + switch (error) { + case LOG_READ_BOGUS: errmsg = "bogus data in log event"; break; - case LOG_READ_TOO_LARGE: - errmsg = "log event entry exceeded max_allowed_packet -\ - increase max_allowed_packet on master"; + case LOG_READ_TOO_LARGE: + errmsg = "log event entry exceeded max_allowed_packet; \ +Increase max_allowed_packet on master"; break; case LOG_READ_IO: errmsg = "I/O error reading log event"; @@ -395,95 +451,104 @@ void mysql_binlog_send(THD* thd, char* log_ident, ulong pos, ushort flags) goto err; } - if(!(flags & BINLOG_DUMP_NON_BLOCK) && + if (!(flags & BINLOG_DUMP_NON_BLOCK) && mysql_bin_log.is_active(log_file_name)) - // block until there is more data in the log - // unless non-blocking mode requested { - if(net_flush(net)) + /* + Block until there is more data in the log + */ + if (net_flush(net)) { errmsg = "failed on net_flush()"; + my_errno= ER_UNKNOWN_ERROR; goto err; } - // we may have missed the update broadcast from the log - // that has just happened, let's try to catch it if it did - // if we did not miss anything, we just wait for other threads - // to signal us + /* + We may have missed the update broadcast from the log + that has just happened, let's try to catch it if it did. + If we did not miss anything, we just wait for other threads + to signal us. + */ { log.error=0; - - // tell the kill thread how to wake us up - thd->mysys_var->current_mutex = log_lock; - thd->mysys_var->current_cond = &COND_binlog_update; - const char* proc_info = thd->proc_info; - thd->proc_info = "Slave connection: waiting for binlog update"; - bool read_packet = 0, fatal_error = 0; #ifndef DBUG_OFF - if(max_binlog_dump_events && !left_events--) + if (max_binlog_dump_events && !left_events--) { - net_flush(net); errmsg = "Debugging binlog dump abort"; + my_errno= ER_UNKNOWN_ERROR; goto err; } -#endif +#endif + + /* + No one will update the log while we are reading + now, but we'll be quick and just read one record + + TODO: + Add an counter that is incremented for each time we update + the binary log. We can avoid the following read if the counter + has not been updated since last read. + */ - // no one will update the log while we are reading - // now, but we'll be quick and just read one record pthread_mutex_lock(log_lock); - switch (Log_event::read_log_event(&log, packet, (pthread_mutex_t*) 0)) - { + switch (Log_event::read_log_event(&log, packet, (pthread_mutex_t*)0)) { case 0: + /* we read successfully, so we'll need to send it to the slave */ + pthread_mutex_unlock(log_lock); read_packet = 1; - // we read successfully, so we'll need to send it to the - // slave break; + case LOG_READ_EOF: - DBUG_PRINT("wait",("waiting for data on binary log")); + DBUG_PRINT("wait",("waiting for data in binary log")); if (!thd->killed) - pthread_cond_wait(&COND_binlog_update, log_lock); + { + /* Note that the following call unlocks lock_log */ + mysql_bin_log.wait_for_update(thd); + } + else + pthread_mutex_unlock(log_lock); + DBUG_PRINT("wait",("binary log received update")); break; default: + pthread_mutex_unlock(log_lock); fatal_error = 1; break; } - pthread_mutex_unlock(log_lock); - - pthread_mutex_lock(&thd->mysys_var->mutex); - thd->mysys_var->current_mutex= 0; - thd->mysys_var->current_cond= 0; - thd->proc_info= proc_info; - pthread_mutex_unlock(&thd->mysys_var->mutex); - if(read_packet) + if (read_packet) { thd->proc_info = "sending update to slave"; - if(my_net_write(net, (char*)packet->ptr(), packet->length()) ) + if (my_net_write(net, (char*)packet->ptr(), packet->length()) ) { errmsg = "Failed on my_net_write()"; + my_errno= ER_UNKNOWN_ERROR; goto err; } - if((*packet)[LOG_EVENT_OFFSET+1] == LOAD_EVENT) + if ((*packet)[LOG_EVENT_OFFSET+1] == LOAD_EVENT) { - if(send_file(thd)) + if (send_file(thd)) { errmsg = "failed in send_file()"; + my_errno= ER_UNKNOWN_ERROR; goto err; } } - packet->length(0); - packet->append("\0",1); - // no need to net_flush because we will get to flush later when - // we hit EOF pretty quick + packet->set("\0", 1); + /* + No need to net_flush because we will get to flush later when + we hit EOF pretty quick + */ } - if(fatal_error) + if (fatal_error) { errmsg = "error reading log entry"; + my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG; goto err; } log.error=0; @@ -494,8 +559,7 @@ void mysql_binlog_send(THD* thd, char* log_ident, ulong pos, ushort flags) bool loop_breaker = 0; // need this to break out of the for loop from switch thd->proc_info = "switching to next log"; - switch(mysql_bin_log.find_next_log(&linfo)) - { + switch (mysql_bin_log.find_next_log(&linfo, 1)) { case LOG_INFO_EOF: loop_breaker = (flags & BINLOG_DUMP_NON_BLOCK); break; @@ -503,21 +567,24 @@ void mysql_binlog_send(THD* thd, char* log_ident, ulong pos, ushort flags) break; default: errmsg = "could not find next log"; + my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG; goto err; } - if(loop_breaker) + if (loop_breaker) break; end_io_cache(&log); (void) my_close(file, MYF(MY_WME)); - + // fake Rotate_log event just in case it did not make it to the log // otherwise the slave make get confused about the offset if ((file=open_binlog(&log, log_file_name, &errmsg)) < 0 || fake_rotate_event(net, packet, log_file_name, &errmsg)) + { + my_errno= ER_MASTER_FATAL_ERROR_READING_BINLOG; goto err; - + } packet->length(0); packet->append("\0",1); } @@ -525,22 +592,25 @@ void mysql_binlog_send(THD* thd, char* log_ident, ulong pos, ushort flags) end_io_cache(&log); (void)my_close(file, MYF(MY_WME)); - + send_eof(&thd->net); thd->proc_info = "waiting to finalize termination"; pthread_mutex_lock(&LOCK_thread_count); thd->current_linfo = 0; pthread_mutex_unlock(&LOCK_thread_count); DBUG_VOID_RETURN; + err: thd->proc_info = "waiting to finalize termination"; end_io_cache(&log); + /* + Exclude iteration through thread list + this is needed for purge_logs() - it will iterate through + thread list and update thd->current_linfo->index_file_offset + this mutex will make sure that it never tried to update our linfo + after we return from this stack frame + */ pthread_mutex_lock(&LOCK_thread_count); - // exclude iteration through thread list - // this is needed for purge_logs() - it will iterate through - // thread list and update thd->current_linfo->index_file_offset - // this mutex will make sure that it never tried to update our linfo - // after we return from this stack frame thd->current_linfo = 0; pthread_mutex_unlock(&LOCK_thread_count); if (file >= 0) @@ -549,251 +619,433 @@ void mysql_binlog_send(THD* thd, char* log_ident, ulong pos, ushort flags) DBUG_VOID_RETURN; } -int start_slave(THD* thd , bool net_report) +int start_slave(THD* thd , MASTER_INFO* mi, bool net_report) { - if(!thd) thd = current_thd; - NET* net = &thd->net; int slave_errno = 0; - if (check_access(thd, PROCESS_ACL, any_db)) - return 1; - pthread_mutex_lock(&LOCK_slave); - if(!slave_running) - { - if(init_master_info(&glob_mi)) - slave_errno = ER_MASTER_INFO; - else if(server_id_supplied && *glob_mi.host) - { - pthread_t hThread; - if(pthread_create(&hThread, &connection_attrib, handle_slave, 0)) - { - slave_errno = ER_SLAVE_THREAD; - } - while(!slave_running) // slave might already be running by now - pthread_cond_wait(&COND_slave_start, &LOCK_slave); - } - else - slave_errno = ER_BAD_SLAVE; - } + if (!thd) thd = current_thd; + NET* net = &thd->net; + int thread_mask; + DBUG_ENTER("start_slave"); + + if (check_access(thd, SUPER_ACL, any_db)) + DBUG_RETURN(1); + lock_slave_threads(mi); // this allows us to cleanly read slave_running + init_thread_mask(&thread_mask,mi,1 /* inverse */); + if (thd->lex.slave_thd_opt) + thread_mask &= thd->lex.slave_thd_opt; + if (thread_mask) + { + if (init_master_info(mi,master_info_file,relay_log_info_file, 0)) + slave_errno=ER_MASTER_INFO; + else if (server_id_supplied && *mi->host) + slave_errno = start_slave_threads(0 /*no mutex */, + 1 /* wait for start */, + mi, + master_info_file,relay_log_info_file, + thread_mask); + else + slave_errno = ER_BAD_SLAVE; + } else slave_errno = ER_SLAVE_MUST_STOP; - - pthread_mutex_unlock(&LOCK_slave); - if(slave_errno) - { - if(net_report) send_error(net, slave_errno); - return 1; - } - else if(net_report) + + unlock_slave_threads(mi); + + if (slave_errno) + { + if (net_report) + send_error(net, slave_errno); + DBUG_RETURN(1); + } + else if (net_report) send_ok(net); - return 0; + DBUG_RETURN(0); } -int stop_slave(THD* thd, bool net_report ) +int stop_slave(THD* thd, MASTER_INFO* mi, bool net_report ) { - if(!thd) thd = current_thd; - NET* net = &thd->net; int slave_errno = 0; - - if (check_access(thd, PROCESS_ACL, any_db)) + if (!thd) thd = current_thd; + NET* net = &thd->net; + + if (check_access(thd, SUPER_ACL, any_db)) return 1; + thd->proc_info = "Killing slave"; + int thread_mask; + lock_slave_threads(mi); + init_thread_mask(&thread_mask,mi,0 /* not inverse*/); + if (thd->lex.slave_thd_opt) + thread_mask &= thd->lex.slave_thd_opt; + slave_errno = (thread_mask) ? + terminate_slave_threads(mi,thread_mask, + 1 /*skip lock */) : ER_SLAVE_NOT_RUNNING; + unlock_slave_threads(mi); + thd->proc_info = 0; - pthread_mutex_lock(&LOCK_slave); - if (slave_running) + if (slave_errno) { - abort_slave = 1; - KICK_SLAVE; - // do not abort the slave in the middle of a query, so we do not set - // thd->killed for the slave thread - thd->proc_info = "waiting for slave to die"; - while(slave_running) - { - /* there is a small chance that slave thread might miss the first - alarm. To protect againts it, resend the signal until it reacts - */ - - struct timespec abstime; -#ifdef HAVE_TIMESPEC_TS_SEC - abstime.ts_sec=time(NULL)+2; - abstime.ts_nsec=0; -#elif defined(__WIN__) - abstime.tv_sec=time((time_t*) 0)+2; - abstime.tv_nsec=0; -#else - struct timeval tv; - gettimeofday(&tv,0); - abstime.tv_sec=tv.tv_sec+2; - abstime.tv_nsec=tv.tv_usec*1000; -#endif - pthread_cond_timedwait(&COND_slave_stopped, &LOCK_slave, &abstime); - if (slave_running) - KICK_SLAVE; - } + if (net_report) + send_error(net, slave_errno); + return 1; } - else - slave_errno = ER_SLAVE_NOT_RUNNING; - - pthread_mutex_unlock(&LOCK_slave); - thd->proc_info = 0; - - if(slave_errno) - { - if(net_report) send_error(net, slave_errno); - return 1; - } - else if(net_report) + else if (net_report) send_ok(net); return 0; } -void reset_slave() + +/* + Remove all relay logs and start replication from the start + + SYNOPSIS + reset_slave() + thd Thread handler + mi Master info for the slave + + + NOTES + We don't send ok in this functions as this is called from + reload_acl_and_cache() which may have done other tasks, which may + have failed for which we want to send and error. + + RETURN + 0 ok + 1 error + In this case error is sent to the client with send_error() +*/ + + +int reset_slave(THD *thd, MASTER_INFO* mi) { MY_STAT stat_area; char fname[FN_REFLEN]; - bool slave_was_running ; - - pthread_mutex_lock(&LOCK_slave); - if((slave_was_running = slave_running)) - { - pthread_mutex_unlock(&LOCK_slave); - stop_slave(0,0); - } - else - pthread_mutex_unlock(&LOCK_slave); + int thread_mask= 0, error= 0; + uint sql_errno=0; + const char* errmsg=0; + DBUG_ENTER("reset_slave"); + + lock_slave_threads(mi); + init_thread_mask(&thread_mask,mi,0 /* not inverse */); + if (thread_mask) // We refuse if any slave thread is running + { + sql_errno= ER_SLAVE_MUST_STOP; + error=1; + goto err; + } + //delete relay logs, clear relay log coordinates + if ((error= purge_relay_logs(&mi->rli, thd, + 1 /* just reset */, + &errmsg))) + goto err; - end_master_info(&glob_mi); - fn_format(fname, master_info_file, mysql_data_home, "", 4+16+32); - if(my_stat(fname, &stat_area, MYF(0))) - if(my_delete(fname, MYF(MY_WME))) - return; - if(slave_was_running) - start_slave(0,0); + //Clear master's log coordinates (only for good display of SHOW SLAVE STATUS) + mi->master_log_name[0]= 0; + mi->master_log_pos= BIN_LOG_HEADER_SIZE; + //close master_info_file, relay_log_info_file, set mi->inited=rli->inited=0 + end_master_info(mi); + //and delete these two files + fn_format(fname, master_info_file, mysql_data_home, "", 4+32); + if (my_stat(fname, &stat_area, MYF(0)) && my_delete(fname, MYF(MY_WME))) + { + error=1; + goto err; + } + fn_format(fname, relay_log_info_file, mysql_data_home, "", 4+32); + if (my_stat(fname, &stat_area, MYF(0)) && my_delete(fname, MYF(MY_WME))) + { + error=1; + goto err; + } + +err: + unlock_slave_threads(mi); + if (thd && error) + send_error(&thd->net, sql_errno, errmsg); + DBUG_RETURN(error); } + void kill_zombie_dump_threads(uint32 slave_server_id) { pthread_mutex_lock(&LOCK_thread_count); I_List_iterator<THD> it(threads); THD *tmp; - while((tmp=it++)) + while ((tmp=it++)) + { + if (tmp->command == COM_BINLOG_DUMP && + tmp->server_id == slave_server_id) { - if(tmp->command == COM_BINLOG_DUMP && - tmp->server_id == slave_server_id) - { - // here we do not call kill_one_thread() - // it will be slow because it will iterate through the list - // again. Plus it double-locks LOCK_thread_count, which - // make safe_mutex complain and abort - // so we just to our own thread murder - - thr_alarm_kill(tmp->real_id); - tmp->killed = 1; - tmp->mysys_var->abort = 1; - pthread_mutex_lock(&tmp->mysys_var->mutex); - if(tmp->mysys_var->current_cond) - { - pthread_mutex_lock(tmp->mysys_var->current_mutex); - pthread_cond_broadcast(tmp->mysys_var->current_cond); - pthread_mutex_unlock(tmp->mysys_var->current_mutex); - } - pthread_mutex_unlock(&tmp->mysys_var->mutex); - } - } - + pthread_mutex_lock(&tmp->LOCK_delete); // Lock from delete + break; + } + } pthread_mutex_unlock(&LOCK_thread_count); + if (tmp) + { + /* + Here we do not call kill_one_thread() as + it will be slow because it will iterate through the list + again. We just to do kill the thread ourselves. + */ + tmp->awake(1/*prepare to die*/); + pthread_mutex_unlock(&tmp->LOCK_delete); + } } -int change_master(THD* thd) + +int change_master(THD* thd, MASTER_INFO* mi) { - bool slave_was_running; - // kill slave thread - pthread_mutex_lock(&LOCK_slave); - if((slave_was_running = slave_running)) - { - abort_slave = 1; - KICK_SLAVE; - thd->proc_info = "waiting for slave to die"; - while(slave_running) - pthread_cond_wait(&COND_slave_stopped, &LOCK_slave); // wait until done - } - pthread_mutex_unlock(&LOCK_slave); + int thread_mask; + const char* errmsg=0; + bool need_relay_log_purge=1; + DBUG_ENTER("change_master"); + + lock_slave_threads(mi); + init_thread_mask(&thread_mask,mi,0 /*not inverse*/); + if (thread_mask) // We refuse if any slave thread is running + { + net_printf(&thd->net,ER_SLAVE_MUST_STOP); + unlock_slave_threads(mi); + DBUG_RETURN(1); + } + thd->proc_info = "changing master"; LEX_MASTER_INFO* lex_mi = &thd->lex.mi; + // TODO: see if needs re-write + if (init_master_info(mi, master_info_file, relay_log_info_file, 0)) + { + send_error(&thd->net, 0, "Could not initialize master info"); + unlock_slave_threads(mi); + DBUG_RETURN(1); + } - if(init_master_info(&glob_mi)) - { - send_error(&thd->net, 0, "Could not initialize master info"); - return 1; - } - - pthread_mutex_lock(&glob_mi.lock); - if((lex_mi->host || lex_mi->port) && !lex_mi->log_file_name && !lex_mi->pos) - { - // if we change host or port, we must reset the postion - glob_mi.log_file_name[0] = 0; - glob_mi.pos = 4; // skip magic number - glob_mi.pending = 0; - } + /* + Data lock not needed since we have already stopped the running threads, + and we have the hold on the run locks which will keep all threads that + could possibly modify the data structures from running + */ + if ((lex_mi->host || lex_mi->port) && !lex_mi->log_file_name && !lex_mi->pos) + { + // if we change host or port, we must reset the postion + mi->master_log_name[0] = 0; + mi->master_log_pos= BIN_LOG_HEADER_SIZE; + mi->rli.pending = 0; + } - if(lex_mi->log_file_name) - strmake(glob_mi.log_file_name, lex_mi->log_file_name, - sizeof(glob_mi.log_file_name)); - if(lex_mi->pos) + if (lex_mi->log_file_name) + strmake(mi->master_log_name, lex_mi->log_file_name, + sizeof(mi->master_log_name)); + if (lex_mi->pos) { - glob_mi.pos = lex_mi->pos; - glob_mi.pending = 0; + mi->master_log_pos= lex_mi->pos; + mi->rli.pending = 0; } - - if(lex_mi->host) + DBUG_PRINT("info", ("master_log_pos: %d", (ulong) mi->master_log_pos)); + + if (lex_mi->host) + strmake(mi->host, lex_mi->host, sizeof(mi->host)); + if (lex_mi->user) + strmake(mi->user, lex_mi->user, sizeof(mi->user)); + if (lex_mi->password) + strmake(mi->password, lex_mi->password, sizeof(mi->password)); + if (lex_mi->port) + mi->port = lex_mi->port; + if (lex_mi->connect_retry) + mi->connect_retry = lex_mi->connect_retry; + + if (lex_mi->relay_log_name) + { + need_relay_log_purge = 0; + mi->rli.skip_log_purge=1; + strmake(mi->rli.relay_log_name,lex_mi->relay_log_name, + sizeof(mi->rli.relay_log_name)-1); + } + + if (lex_mi->relay_log_pos) + { + need_relay_log_purge=0; + mi->rli.relay_log_pos=lex_mi->relay_log_pos; + } + + flush_master_info(mi); + if (need_relay_log_purge) + { + mi->rli.skip_log_purge=0; + thd->proc_info="purging old relay logs"; + if (purge_relay_logs(&mi->rli, thd, + 0 /* not only reset, but also reinit */, + &errmsg)) { - strmake(glob_mi.host, lex_mi->host, sizeof(glob_mi.host)); + net_printf(&thd->net, 0, "Failed purging old relay logs: %s",errmsg); + unlock_slave_threads(mi); + DBUG_RETURN(1); } - if(lex_mi->user) - strmake(glob_mi.user, lex_mi->user, sizeof(glob_mi.user)); - if(lex_mi->password) - strmake(glob_mi.password, lex_mi->password, sizeof(glob_mi.password)); - if(lex_mi->port) - glob_mi.port = lex_mi->port; - if(lex_mi->connect_retry) - glob_mi.connect_retry = lex_mi->connect_retry; - - flush_master_info(&glob_mi); - pthread_mutex_unlock(&glob_mi.lock); - thd->proc_info = "starting slave"; - if(slave_was_running) - start_slave(0,0); + } + else + { + const char* msg; + /* Relay log is already initialized */ + if (init_relay_log_pos(&mi->rli, + mi->rli.relay_log_name, + mi->rli.relay_log_pos, + 0 /*no data lock*/, + &msg)) + { + net_printf(&thd->net,0,"Failed initializing relay log position: %s",msg); + unlock_slave_threads(mi); + DBUG_RETURN(1); + } + } + mi->rli.master_log_pos = mi->master_log_pos; + DBUG_PRINT("info", ("master_log_pos: %d", (ulong) mi->master_log_pos)); + strmake(mi->rli.master_log_name,mi->master_log_name, + sizeof(mi->rli.master_log_name)-1); + if (!mi->rli.master_log_name[0]) // uninitialized case + mi->rli.master_log_pos=0; + + pthread_mutex_lock(&mi->rli.data_lock); + mi->rli.abort_pos_wait++; + pthread_cond_broadcast(&mi->data_cond); + pthread_mutex_unlock(&mi->rli.data_lock); + + unlock_slave_threads(mi); thd->proc_info = 0; - send_ok(&thd->net); - return 0; + DBUG_RETURN(0); } -void reset_master() +int reset_master(THD* thd) { - if(!mysql_bin_log.is_open()) + if (!mysql_bin_log.is_open()) { my_error(ER_FLUSH_MASTER_BINLOG_CLOSED, MYF(ME_BELL+ME_WAITTANG)); - return; + return 1; } + return mysql_bin_log.reset_logs(thd); +} - LOG_INFO linfo; - if (mysql_bin_log.find_first_log(&linfo, "")) - return; +int cmp_master_pos(const char* log_file_name1, ulonglong log_pos1, + const char* log_file_name2, ulonglong log_pos2) +{ + int res; + uint log_file_name1_len= strlen(log_file_name1); + uint log_file_name2_len= strlen(log_file_name2); - for(;;) + // We assume that both log names match up to '.' + if (log_file_name1_len == log_file_name2_len) { - my_delete(linfo.log_file_name, MYF(MY_WME)); - if (mysql_bin_log.find_next_log(&linfo)) - break; + if ((res= strcmp(log_file_name1, log_file_name2))) + return res; + return (log_pos1 < log_pos2) ? -1 : (log_pos1 == log_pos2) ? 0 : 1; + } + return ((log_file_name1_len < log_file_name2_len) ? -1 : 1); +} + + +int show_binlog_events(THD* thd) +{ + DBUG_ENTER("show_binlog_events"); + List<Item> field_list; + const char* errmsg = 0; + IO_CACHE log; + File file = -1; + + Log_event::init_show_field_list(&field_list); + if (send_fields(thd, field_list, 1)) + DBUG_RETURN(-1); + + if (mysql_bin_log.is_open()) + { + LEX_MASTER_INFO *lex_mi = &thd->lex.mi; + ha_rows event_count, limit_start, limit_end; + my_off_t pos = lex_mi->pos; + char search_file_name[FN_REFLEN], *name; + const char *log_file_name = lex_mi->log_file_name; + pthread_mutex_t *log_lock = mysql_bin_log.get_log_lock(); + LOG_INFO linfo; + Log_event* ev; + + limit_start = thd->lex.select->offset_limit; + limit_end = thd->lex.select->select_limit + limit_start; + + name= search_file_name; + if (log_file_name) + mysql_bin_log.make_log_name(search_file_name, log_file_name); + else + name=0; // Find first log + + linfo.index_file_offset = 0; + thd->current_linfo = &linfo; + + if (mysql_bin_log.find_log_pos(&linfo, name, 1)) + { + errmsg = "Could not find target log"; + goto err; + } + + if ((file=open_binlog(&log, linfo.log_file_name, &errmsg)) < 0) + goto err; + + if (pos < 4) + { + errmsg = "Invalid log position"; + goto err; + } + + pthread_mutex_lock(log_lock); + my_b_seek(&log, pos); + + for (event_count = 0; + (ev = Log_event::read_log_event(&log,(pthread_mutex_t*)0,0)); ) + { + if (event_count >= limit_start && + ev->net_send(thd, linfo.log_file_name, pos)) + { + errmsg = "Net error"; + delete ev; + pthread_mutex_unlock(log_lock); + goto err; + } + + pos = my_b_tell(&log); + delete ev; + + if (++event_count >= limit_end) + break; + } + + if (event_count < limit_end && log.error) + { + errmsg = "Wrong offset or I/O error"; + pthread_mutex_unlock(log_lock); + goto err; + } + + pthread_mutex_unlock(log_lock); + } + +err: + if (file >= 0) + { + end_io_cache(&log); + (void) my_close(file, MYF(MY_WME)); } - mysql_bin_log.close(1); // exiting close - my_delete(mysql_bin_log.get_index_fname(), MYF(MY_WME)); - mysql_bin_log.open(opt_bin_logname,LOG_BIN); + if (errmsg) + { + my_error(ER_ERROR_WHEN_EXECUTING_COMMAND, MYF(0), + "SHOW BINLOG EVENTS", errmsg); + DBUG_RETURN(-1); + } + + send_eof(&thd->net); + DBUG_RETURN(0); } + int show_binlog_info(THD* thd) { DBUG_ENTER("show_binlog_info"); @@ -803,100 +1055,116 @@ int show_binlog_info(THD* thd) field_list.push_back(new Item_empty_string("Binlog_do_db",20)); field_list.push_back(new Item_empty_string("Binlog_ignore_db",20)); - if(send_fields(thd, field_list, 1)) + if (send_fields(thd, field_list, 1)) DBUG_RETURN(-1); String* packet = &thd->packet; packet->length(0); - if(mysql_bin_log.is_open()) - { - LOG_INFO li; - mysql_bin_log.get_current_log(&li); - int dir_len = dirname_length(li.log_file_name); - net_store_data(packet, li.log_file_name + dir_len); - net_store_data(packet, (longlong)li.pos); - net_store_data(packet, &binlog_do_db); - net_store_data(packet, &binlog_ignore_db); - } - else - { - net_store_null(packet); - net_store_null(packet); - net_store_null(packet); - net_store_null(packet); - } - - if(my_net_write(&thd->net, (char*)thd->packet.ptr(), packet->length())) - DBUG_RETURN(-1); - + if (mysql_bin_log.is_open()) + { + LOG_INFO li; + mysql_bin_log.get_current_log(&li); + int dir_len = dirname_length(li.log_file_name); + net_store_data(packet, li.log_file_name + dir_len); + net_store_data(packet, (longlong)li.pos); + net_store_data(packet, &binlog_do_db); + net_store_data(packet, &binlog_ignore_db); + if (my_net_write(&thd->net, (char*)thd->packet.ptr(), packet->length())) + DBUG_RETURN(-1); + } send_eof(&thd->net); DBUG_RETURN(0); } + +/* + Send a lost of all binary logs to client + + SYNOPSIS + show_binlogs() + thd Thread specific variable + + RETURN VALUES + 0 ok + 1 error (Error message sent to client) +*/ + int show_binlogs(THD* thd) { - const char* errmsg = 0; - File index_file; + const char *errmsg; + IO_CACHE *index_file; char fname[FN_REFLEN]; NET* net = &thd->net; List<Item> field_list; - String* packet = &thd->packet; - IO_CACHE io_cache; + String *packet = &thd->packet; uint length; - - if(!mysql_bin_log.is_open()) + + if (!mysql_bin_log.is_open()) { - errmsg = "binlog is not open"; - goto err; + //TODO: Replace with ER() error message + errmsg= "You are not using binary logging"; + goto err_with_msg; } - field_list.push_back(new Item_empty_string("Log_name", 128)); - if(send_fields(thd, field_list, 1)) - { - sql_print_error("Failed in send_fields"); + field_list.push_back(new Item_empty_string("Log_name", 255)); + if (send_fields(thd, field_list, 1)) return 1; - } - mysql_bin_log.lock_index(); - index_file = mysql_bin_log.get_index_file(); - if (index_file < 0) - { - errmsg = "Uninitialized index file pointer"; - goto err2; - } - if (init_io_cache(&io_cache, index_file, IO_SIZE, READ_CACHE, 0, 0, - MYF(MY_WME))) - { - errmsg = "Failed on init_io_cache()"; - goto err2; - } - while ((length=my_b_gets(&io_cache, fname, sizeof(fname)))) + index_file=mysql_bin_log.get_index_file(); + + reinit_io_cache(index_file, READ_CACHE, (my_off_t) 0, 0, 0); + + /* The file ends with EOF or empty line */ + while ((length=my_b_gets(index_file, fname, sizeof(fname))) > 1) { - fname[--length]=0; int dir_len = dirname_length(fname); packet->length(0); - net_store_data(packet, fname + dir_len, length-dir_len); - if(my_net_write(net, (char*) packet->ptr(), packet->length())) - { - sql_print_error("Failed in my_net_write"); - end_io_cache(&io_cache); - mysql_bin_log.unlock_index(); - return 1; - } + /* The -1 is for removing newline from fname */ + net_store_data(packet, fname + dir_len, length-1-dir_len); + if (my_net_write(net, (char*) packet->ptr(), packet->length())) + goto err; } - mysql_bin_log.unlock_index(); - end_io_cache(&io_cache); - send_eof(net); + send_eof(net); return 0; -err2: - mysql_bin_log.unlock_index(); - end_io_cache(&io_cache); -err: +err_with_msg: send_error(net, 0, errmsg); +err: + mysql_bin_log.unlock_index(); return 1; } - +int log_loaded_block(IO_CACHE* file) +{ + LOAD_FILE_INFO* lf_info; + uint block_len ; + + /* file->request_pos contains position where we started last read */ + char* buffer = (char*) file->request_pos; + if (!(block_len = (char*) file->read_end - (char*) buffer)) + return 0; + lf_info = (LOAD_FILE_INFO*) file->arg; + if (lf_info->last_pos_in_file != HA_POS_ERROR && + lf_info->last_pos_in_file >= file->pos_in_file) + return 0; + lf_info->last_pos_in_file = file->pos_in_file; + if (lf_info->wrote_create_file) + { + Append_block_log_event a(lf_info->thd, buffer, block_len, + lf_info->log_delayed); + mysql_bin_log.write(&a); + } + else + { + Create_file_log_event c(lf_info->thd,lf_info->ex,lf_info->db, + lf_info->table_name, *lf_info->fields, + lf_info->handle_dup, buffer, + block_len, lf_info->log_delayed); + mysql_bin_log.write(&c); + lf_info->wrote_create_file = 1; + DBUG_SYNC_POINT("debug_lock.created_file_event",10); + } + return 0; +} diff --git a/sql/sql_repl.h b/sql/sql_repl.h index 3b8f161dcd0..15435382b08 100644 --- a/sql/sql_repl.h +++ b/sql/sql_repl.h @@ -1,39 +1,55 @@ -#ifndef SQL_REPL_H -#define SQL_REPL_H - #include "slave.h" -extern char* master_host; -extern my_string opt_bin_logname, master_info_file; -extern ulong server_id; +typedef struct st_slave_info +{ + uint32 server_id; + uint32 rpl_recovery_rank, master_id; + char host[HOSTNAME_LENGTH+1]; + char user[USERNAME_LENGTH+1]; + char password[HASH_PASSWORD_LENGTH+1]; + uint16 port; + THD* thd; +} SLAVE_INFO; + +extern my_bool opt_show_slave_auth_info, opt_old_rpl_compat; +extern char *master_host, *master_info_file; extern bool server_id_supplied; extern I_List<i_string> binlog_do_db, binlog_ignore_db; -#ifndef DBUG_OFF extern int max_binlog_dump_events; -extern bool opt_sporadic_binlog_dump_fail; -#endif +extern my_bool opt_sporadic_binlog_dump_fail; -#ifdef SIGNAL_WITH_VIO_CLOSE -#define KICK_SLAVE { slave_thd->close_active_vio(); \ - thr_alarm_kill(slave_real_id); } -#else -#define KICK_SLAVE thr_alarm_kill(slave_real_id); -#endif +#define KICK_SLAVE(thd) { pthread_mutex_lock(&(thd)->LOCK_delete); (thd)->awake(0 /* do not prepare to die*/); pthread_mutex_unlock(&(thd)->LOCK_delete); } File open_binlog(IO_CACHE *log, const char *log_file_name, - const char **errmsg); + const char **errmsg); -int start_slave(THD* thd = 0, bool net_report = 1); -int stop_slave(THD* thd = 0, bool net_report = 1); -int change_master(THD* thd); -void reset_slave(); -void reset_master(); +int start_slave(THD* thd, MASTER_INFO* mi, bool net_report); +int stop_slave(THD* thd, MASTER_INFO* mi, bool net_report); +int change_master(THD* thd, MASTER_INFO* mi); +int show_binlog_events(THD* thd); +int cmp_master_pos(const char* log_file_name1, ulonglong log_pos1, + const char* log_file_name2, ulonglong log_pos2); +int reset_slave(THD *thd, MASTER_INFO* mi); +int reset_master(THD* thd); int purge_master_logs(THD* thd, const char* to_log); bool log_in_use(const char* log_name); void adjust_linfo_offsets(my_off_t purge_offset); int show_binlogs(THD* thd); extern int init_master_info(MASTER_INFO* mi); void kill_zombie_dump_threads(uint32 slave_server_id); +int check_binlog_magic(IO_CACHE* log, const char** errmsg); + +typedef struct st_load_file_info +{ + THD* thd; + my_off_t last_pos_in_file; + sql_exchange* ex; + List <Item> *fields; + enum enum_duplicates handle_dup; + char* db; + char* table_name; + bool wrote_create_file, log_delayed; +} LOAD_FILE_INFO; -#endif +int log_loaded_block(IO_CACHE* file); diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 8444a451965..d3934fbd620 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2003 MySQL AB & MySQL Finland AB & TCX DataKonsult AB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -41,13 +41,16 @@ static bool update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse, uint tables,COND *conds,table_map table_map); static int sort_keyuse(KEYUSE *a,KEYUSE *b); static void set_position(JOIN *join,uint index,JOIN_TAB *table,KEYUSE *key); +static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse, + table_map used_tables); static void find_best_combination(JOIN *join,table_map rest_tables); static void find_best(JOIN *join,table_map rest_tables,uint index, double record_count,double read_time); static uint cache_record_length(JOIN *join,uint index); static double prev_record_reads(JOIN *join,table_map found_ref); static bool get_best_combination(JOIN *join); -static store_key *get_store_key(KEYUSE *keyuse, table_map used_tables, +static store_key *get_store_key(THD *thd, + KEYUSE *keyuse, table_map used_tables, KEY_PART_INFO *key_part, char *key_buff, uint maybe_null); static bool make_simple_join(JOIN *join,TABLE *tmp_table); @@ -59,7 +62,7 @@ static void update_depend_map(JOIN *join); static void update_depend_map(JOIN *join, ORDER *order); static ORDER *remove_const(JOIN *join,ORDER *first_order,COND *cond, bool *simple_order); -static int return_zero_rows(select_result *res,TABLE_LIST *tables, +static int return_zero_rows(JOIN *join, select_result *res,TABLE_LIST *tables, List<Item> &fields, bool send_row, uint select_options, const char *info, Item *having, Procedure *proc); @@ -68,7 +71,7 @@ static COND *remove_eq_conds(COND *cond,Item::cond_result *cond_value); static bool const_expression_in_where(COND *conds,Item *item, Item **comp_item); static bool open_tmp_table(TABLE *table); static bool create_myisam_tmp_table(TABLE *table,TMP_TABLE_PARAM *param, - uint options); + ulong options); static int do_select(JOIN *join,List<Item> *fields,TABLE *tmp_table, Procedure *proc); static int sub_select_cache(JOIN *join,JOIN_TAB *join_tab,bool end_of_records); @@ -83,20 +86,23 @@ static int end_unique_update(JOIN *join,JOIN_TAB *join_tab, static int end_write_group(JOIN *join, JOIN_TAB *join_tab, bool end_of_records); static int test_if_group_changed(List<Item_buff> &list); -static int join_read_const_tables(JOIN *join); +static int join_read_const_table(JOIN_TAB *tab, POSITION *pos); static int join_read_system(JOIN_TAB *tab); static int join_read_const(JOIN_TAB *tab); static int join_read_key(JOIN_TAB *tab); static int join_read_always_key(JOIN_TAB *tab); +static int join_read_last_key(JOIN_TAB *tab); static int join_no_more_records(READ_RECORD *info); static int join_read_next(READ_RECORD *info); static int join_init_quick_read_record(JOIN_TAB *tab); static int test_if_quick_select(JOIN_TAB *tab); static int join_init_read_record(JOIN_TAB *tab); -static int join_init_read_first_with_key(JOIN_TAB *tab); -static int join_init_read_next_with_key(READ_RECORD *info); -static int join_init_read_last_with_key(JOIN_TAB *tab); -static int join_init_read_prev_with_key(READ_RECORD *info); +static int join_read_first(JOIN_TAB *tab); +static int join_read_next(READ_RECORD *info); +static int join_read_next_same(READ_RECORD *info); +static int join_read_last(JOIN_TAB *tab); +static int join_read_prev_same(READ_RECORD *info); +static int join_read_prev(READ_RECORD *info); static int join_ft_read_first(JOIN_TAB *tab); static int join_ft_read_next(READ_RECORD *info); static COND *make_cond_for_table(COND *cond,table_map table, @@ -104,9 +110,9 @@ static COND *make_cond_for_table(COND *cond,table_map table, static Item* part_of_refkey(TABLE *form,Field *field); static uint find_shortest_key(TABLE *table, key_map usable_keys); static bool test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order, - ha_rows select_limit); -static int create_sort_index(JOIN_TAB *tab,ORDER *order,ha_rows select_limit); -static bool fix_having(JOIN *join, Item **having); + ha_rows select_limit, bool no_changes); +static int create_sort_index(JOIN_TAB *tab,ORDER *order,ha_rows filesort_limit, + ha_rows select_limit); static int remove_duplicates(JOIN *join,TABLE *entry,List<Item> &fields, Item *having); static int remove_dup_with_compare(THD *thd, TABLE *entry, Field **field, @@ -124,7 +130,9 @@ static int setup_group(THD *thd,TABLE_LIST *tables,List<Item> &fields, List<Item> &all_fields, ORDER *order, bool *hidden); static bool setup_new_fields(THD *thd,TABLE_LIST *tables,List<Item> &fields, List<Item> &all_fields,ORDER *new_order); -static ORDER *create_distinct_group(ORDER *order, List<Item> &fields); +static ORDER *create_distinct_group(THD *thd, ORDER *order, + List<Item> &fields, + bool *all_order_by_fields_used); static bool test_if_subpart(ORDER *a,ORDER *b); static TABLE *get_sort_by_table(ORDER *a,ORDER *b,TABLE_LIST *tables); static void calc_group_buffer(JOIN *join,ORDER *group); @@ -139,23 +147,70 @@ static bool add_ref_to_table_cond(THD *thd, JOIN_TAB *join_tab); static void init_sum_functions(Item_sum **func); static bool update_sum_func(Item_sum **func); static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, - bool distinct); -static void describe_info(THD *thd, const char *info); + bool distinct, const char *message=NullS); +static void describe_info(JOIN *join, const char *info); + +/* + This handles SELECT with and without UNION +*/ + +int handle_select(THD *thd, LEX *lex, select_result *result) +{ + int res; + register SELECT_LEX *select_lex = &lex->select_lex; + +#ifdef DISABLED_UNTIL_REWRITTEN_IN_4_1 + if (lex->olap) + { + SELECT_LEX *sl, *sl_next; + int error; + for (sl= &select_lex; sl; sl=sl_next) + { + sl_next=sl->next; // Save if sl->next changes + if (sl->olap != UNSPECIFIED_OLAP_TYPE) + { + if ((error=handle_olaps(lex,sl))) + return error; + lex->last_selects->next=sl_next; + } + } + lex->select = select_lex; + } +#endif /* DISABLED_UNTIL_REWRITTEN_IN_4_1 */ + if (select_lex->next) + res=mysql_union(thd,lex,result); + else + res=mysql_select(thd,(TABLE_LIST*) select_lex->table_list.first, + select_lex->item_list, + select_lex->where, + (ORDER*) select_lex->order_list.first, + (ORDER*) select_lex->group_list.first, + select_lex->having, + (ORDER*) lex->proc_list.first, + select_lex->options | thd->options, + result); + if (res && result) + result->abort(); + delete result; + return res; +} + /***************************************************************************** -** check fields, find best join, do the select and output fields. -** mysql_select assumes that all tables are allready opened + Check fields, find best join, do the select and output fields. + mysql_select assumes that all tables are already opened *****************************************************************************/ int mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, ORDER *order, ORDER *group,Item *having,ORDER *proc_param, - uint select_options,select_result *result) + ulong select_options,select_result *result) { TABLE *tmp_table; int error, tmp_error; bool need_tmp,hidden_group_fields; - bool simple_order,simple_group,no_order; + bool simple_order,simple_group,no_order, skip_sort_order; + ha_rows select_limit; Item::cond_result cond_value; SQL_SELECT *select; DYNAMIC_ARRAY keyuse; @@ -163,6 +218,7 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, Procedure *procedure; List<Item> all_fields(fields); bool select_distinct; + SELECT_LEX *cur_sel = thd->lex.select; DBUG_ENTER("mysql_select"); /* Check that all tables, fields, conds and order are ok */ @@ -170,13 +226,17 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, select_distinct=test(select_options & SELECT_DISTINCT); tmp_table=0; select=0; - no_order=0; + no_order=skip_sort_order=0; bzero((char*) &keyuse,sizeof(keyuse)); thd->proc_info="init"; thd->used_tables=0; // Updated by setup_fields + /* select_limit is used to decide if we are likely to scan the whole table */ + select_limit= thd->select_limit; + if (having || (select_options & OPTION_FOUND_ROWS)) + select_limit= HA_POS_ERROR; if (setup_tables(tables) || - setup_fields(thd,tables,fields,1,&all_fields) || + setup_fields(thd,tables,fields,1,&all_fields,1) || setup_conds(thd,tables,&conds) || setup_order(thd,tables,fields,all_fields,order) || setup_group(thd,tables,fields,all_fields,group,&hidden_group_fields)) @@ -198,14 +258,14 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, and no GROUP BY. TODO: Add check of calculation of GROUP functions and fields: SELECT COUNT(*)+table.col1 from table1; - */ + */ join.table=0; join.tables=0; { if (!group) { uint flag=0; - List_iterator<Item> it(fields); + List_iterator_fast<Item> it(fields); Item *item; while ((item= it++)) { @@ -274,12 +334,15 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, count_field_types(&join.tmp_table_param,all_fields,0); join.const_tables=0; join.having=0; + join.do_send_rows = 1; join.group= group != 0; + join.row_limit= ((select_distinct || order || group) ? HA_POS_ERROR : + thd->select_limit); #ifdef RESTRICTED_GROUP if (join.sum_func_count && !group && (join.func_count || join.field_count)) { - my_message(ER_WRONG_SUM_SELECT,ER(ER_WRONG_SUM_SELECT)); + my_message(ER_WRONG_SUM_SELECT,ER(ER_WRONG_SUM_SELECT),MYF(0)); delete procedure; DBUG_RETURN(-1); } @@ -302,6 +365,7 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, { conds->fix_fields(thd,tables); conds->change_ref_to_fields(thd,tables); + conds->top_level_item(); having=0; } } @@ -315,7 +379,7 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, } if (cond_value == Item::COND_FALSE || !thd->select_limit) { /* Impossible cond */ - error=return_zero_rows(result, tables, fields, + error=return_zero_rows(&join, result, tables, fields, join.tmp_table_param.sum_func_count != 0 && !group, select_options,"Impossible WHERE",having, procedure); @@ -331,7 +395,7 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, { if (res < 0) { - error=return_zero_rows(result, tables, fields, !group, + error=return_zero_rows(&join, result, tables, fields, !group, select_options,"No matching min/max row", having,procedure); delete procedure; @@ -339,9 +403,9 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, } if (select_options & SELECT_DESCRIBE) { - describe_info(thd,"Select tables optimized away"); + describe_info(&join, "Select tables optimized away"); delete procedure; - DBUG_RETURN(0); + DBUG_RETURN(error); } tables=0; // All tables resolved } @@ -350,13 +414,13 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, { // Only test of functions error=0; if (select_options & SELECT_DESCRIBE) - describe_info(thd,"No tables used"); + describe_info(&join, "No tables used"); else { result->send_fields(fields,1); if (!having || having->val_int()) { - if (result->send_data(fields)) + if (join.do_send_rows && result->send_data(fields)) { result->send_error(0,NullS); /* purecov: inspected */ error=1; @@ -368,7 +432,7 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, error=(int) result->send_eof(); } delete procedure; - DBUG_RETURN(0); + DBUG_RETURN(error); } error = -1; @@ -376,28 +440,31 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, /* Calculate how to do the join */ thd->proc_info="statistics"; - if (make_join_statistics(&join,tables,conds,&keyuse) || - thd->fatal_error) + if (make_join_statistics(&join,tables,conds,&keyuse) || thd->fatal_error) goto err; + thd->proc_info="preparing"; - if ((tmp_error=join_read_const_tables(&join)) > 0) + if (result->initialize_tables(&join)) goto err; - if (tmp_error && !(select_options & SELECT_DESCRIBE)) + if (join.const_table_map != join.found_const_table_map && + !(select_options & SELECT_DESCRIBE)) { - error=return_zero_rows(result,tables,fields, + error=return_zero_rows(&join,result,tables,fields, join.tmp_table_param.sum_func_count != 0 && - !group,0,"",having,procedure); + !group,0,"no matching row in const table",having, + procedure); goto err; } if (!(thd->options & OPTION_BIG_SELECTS) && - join.best_read > (double) thd->max_join_size && + join.best_read > (double) thd->variables.max_join_size && !(select_options & SELECT_DESCRIBE)) { /* purecov: inspected */ result->send_error(ER_TOO_BIG_SELECT,ER(ER_TOO_BIG_SELECT)); /* purecov: inspected */ error= 1; /* purecov: inspected */ goto err; /* purecov: inspected */ } - if (join.const_tables && !thd->locked_tables) + if (join.const_tables && !thd->locked_tables && + !(select_options & SELECT_NO_UNLOCK)) { TABLE **table, **end; for (table=join.table, end=table + join.const_tables ; @@ -428,7 +495,7 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, } if (make_join_select(&join,select,conds)) { - error=return_zero_rows(result,tables,fields, + error=return_zero_rows(&join, result, tables, fields, join.tmp_table_param.sum_func_count != 0 && !group, select_options, "Impossible WHERE noticed after reading const tables", @@ -439,22 +506,62 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, error= -1; /* if goto err */ /* Optimize distinct away if possible */ - order=remove_const(&join,order,conds,&simple_order); + { + ORDER *org_order= order; + order=remove_const(&join,order,conds,&simple_order); + /* + If we are using ORDER BY NULL or ORDER BY const_expression, + return result in any order (even if we are using a GROUP BY) + */ + if (!order && org_order) + skip_sort_order= 1; + } if (group || join.tmp_table_param.sum_func_count) { if (! hidden_group_fields) select_distinct=0; } - else if (select_distinct && join.tables - join.const_tables == 1 && - (order || thd->select_limit == HA_POS_ERROR)) + else if (select_distinct && join.tables - join.const_tables == 1) { - if ((group=create_distinct_group(order,fields))) - { - select_distinct=0; - no_order= !order; - join.group=1; // For end_write_group - } - else if (thd->fatal_error) // End of memory + /* + We are only using one table. In this case we change DISTINCT to a + GROUP BY query if: + - The GROUP BY can be done through indexes (no sort) and the ORDER + BY only uses selected fields. + (In this case we can later optimize away GROUP BY and ORDER BY) + - We are scanning the whole table without LIMIT + This can happen if: + - We are using CALC_FOUND_ROWS + - We are using an ORDER BY that can't be optimized away. + + We don't want to use this optimization when we are using LIMIT + because in this case we can just create a temporary table that + holds LIMIT rows and stop when this table is full. + */ + JOIN_TAB *tab= &join.join_tab[join.const_tables]; + bool all_order_fields_used; + if (order) + skip_sort_order= test_if_skip_sort_order(tab, order, select_limit, 1); + if ((group=create_distinct_group(thd, order, fields, + &all_order_fields_used))) + { + bool skip_group= (skip_sort_order && + test_if_skip_sort_order(tab, group, select_limit, + 1) != 0); + if ((skip_group && all_order_fields_used) || + select_limit == HA_POS_ERROR || + (order && !skip_sort_order)) + { + /* Change DISTINCT to GROUP BY */ + select_distinct= 0; + no_order= !order; + if (all_order_fields_used) + order=0; + join.group=1; // For end_write_group + } + else + group= 0; + } else if (thd->fatal_error) // End of memory goto err; } group=remove_const(&join,group,conds,&simple_group); @@ -477,7 +584,7 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, (!group && join.tmp_table_param.sum_func_count)) order=0; - // Can't use sort on head table if using cache + // Can't use sort on head table if using row cache if (join.full_join) { if (group) @@ -486,19 +593,33 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, simple_order=0; } + /* + Check if we need to create a temporary table. + This has to be done if all tables are not already read (const tables) + and one of the following conditions holds: + - We are using DISTINCT (simple distinct's are already optimized away) + - We are using an ORDER BY or GROUP BY on fields not in the first table + - We are using different ORDER BY and GROUP BY orders + - The user wants us to buffer the result. + */ need_tmp= (join.const_tables != join.tables && ((select_distinct || !simple_order || !simple_group) || - (group && order) || + (group && order) || test(select_options & OPTION_BUFFER_RESULT))); - make_join_readinfo(&join, (select_options & SELECT_DESCRIBE) | - (thd->lex.ftfunc_list.elements ? 0 : SELECT_USE_CACHE)); // No cache for MATCH - - /* Need to tell Innobase that to play it safe, it should fetch all - columns of the tables: this is because MySQL - may build row pointers for the rows, and for all columns of the primary - key the field->query_id has not necessarily been set to thd->query_id - by MySQL. */ + // No cache for MATCH + make_join_readinfo(&join, + (select_options & (SELECT_DESCRIBE | + SELECT_NO_JOIN_CACHE)) | + (cur_sel->ftfunc_list.elements ? SELECT_NO_JOIN_CACHE : + 0)); + /* + Need to tell Innobase that to play it safe, it should fetch all + columns of the tables: this is because MySQL may build row + pointers for the rows, and for all columns of the primary key the + field->query_id has not necessarily been set to thd->query_id by + MySQL. + */ #ifdef HAVE_INNOBASE_DB if (need_tmp || select_distinct || group || order) @@ -506,7 +627,7 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, for (uint i_h = join.const_tables; i_h < join.tables; i_h++) { TABLE* table_h = join.join_tab[i_h].table; - if (table_h->db_type == DB_TYPE_INNOBASE) + if (table_h->db_type == DB_TYPE_INNODB) table_h->file->extra(HA_EXTRA_DONT_USE_CURSOR_TO_UPDATE); } } @@ -531,7 +652,7 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, ((group && join.const_tables != join.tables && (!simple_group || !test_if_skip_sort_order(&join.join_tab[join.const_tables], group, - HA_POS_ERROR))) || + thd->select_limit,0))) || select_distinct) && join.tmp_table_param.quick_group && !procedure) { @@ -544,21 +665,19 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, order=group; if (order && (join.const_tables == join.tables || - (simple_order && + ((simple_order || skip_sort_order) && test_if_skip_sort_order(&join.join_tab[join.const_tables], order, - (group ? HA_POS_ERROR : - thd->select_limit))))) + select_limit, 0)))) order=0; select_describe(&join,need_tmp, - (order != 0 && - (!need_tmp || order != group || simple_group)), + order != 0 && !skip_sort_order, select_distinct); error=0; goto err; } /* Perform FULLTEXT search before all regular searches */ - init_ftfuncs(thd, test(order)); + init_ftfuncs(thd,test(order)); /* Create a tmp table if distinct or if the sort is too complicated */ if (need_tmp) @@ -566,6 +685,8 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, DBUG_PRINT("info",("Creating tmp table")); thd->proc_info="Creating tmp table"; + join.tmp_table_param.hidden_field_count= (all_fields.elements - + fields.elements); if (!(tmp_table = create_tmp_table(thd,&join.tmp_table_param,all_fields, ((!simple_group && !procedure && @@ -573,7 +694,8 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, group : (ORDER*) 0), group ? 0 : select_distinct, group && simple_group, - order == 0, + (order == 0 || skip_sort_order) && + select_limit != HA_POS_ERROR, join.select_options))) goto err; /* purecov: inspected */ @@ -586,7 +708,7 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, DBUG_PRINT("info",("Sorting for group")); thd->proc_info="Sorting for group"; if (create_sort_index(&join.join_tab[join.const_tables],group, - HA_POS_ERROR) || + HA_POS_ERROR, HA_POS_ERROR) || make_sum_func_list(&join,all_fields) || alloc_group_fields(&join,group)) goto err; @@ -601,7 +723,7 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, DBUG_PRINT("info",("Sorting for order")); thd->proc_info="Sorting for order"; if (create_sort_index(&join.join_tab[join.const_tables],order, - HA_POS_ERROR)) + HA_POS_ERROR, HA_POS_ERROR)) goto err; /* purecov: inspected */ order=0; } @@ -623,12 +745,23 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, break; join_tab->not_used_in_distinct=1; } while (join_tab-- != join.join_tab); + /* Optimize "select distinct b from t1 order by key_part_1 limit #" */ + if (order && skip_sort_order) + { + /* Should always succeed */ + if (test_if_skip_sort_order(&join.join_tab[join.const_tables], + order, thd->select_limit,0)) + order=0; + } } /* Copy data to the temporary table */ thd->proc_info="Copying to tmp table"; - if (do_select(&join,(List<Item> *) 0,tmp_table,0)) + if ((tmp_error=do_select(&join,(List<Item> *) 0,tmp_table,0))) + { + error=tmp_error; goto err; /* purecov: inspected */ + } if (join.having) join.having=having=0; // Allready done @@ -652,17 +785,17 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, procedure->update_refs(); if (tmp_table->group) { // Already grouped - if (!order && !no_order) + if (!order && !no_order && !skip_sort_order) order=group; /* order by group */ group=0; } /* - ** If we have different sort & group then we must sort the data by group - ** and copy it to another tmp table - ** This code is also used if we are using distinct something - ** we haven't been able to store in the temporary table yet - ** like SEC_TO_TIME(SUM(...)). + If we have different sort & group then we must sort the data by group + and copy it to another tmp table + This code is also used if we are using distinct something + we haven't been able to store in the temporary table yet + like SEC_TO_TIME(SUM(...)). */ if (group && (!test_if_subpart(group,order) || select_distinct) || @@ -692,7 +825,7 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, if (group) { thd->proc_info="Creating sort index"; - if (create_sort_index(join.join_tab,group,HA_POS_ERROR) || + if (create_sort_index(join.join_tab,group,HA_POS_ERROR, HA_POS_ERROR) || alloc_group_fields(&join,group)) { free_tmp_table(thd,tmp_table2); /* purecov: inspected */ @@ -752,7 +885,7 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, (procedure && (procedure->flags & PROC_GROUP))) { alloc_group_fields(&join,group); - setup_copy_fields(&join.tmp_table_param,all_fields); + setup_copy_fields(thd, &join.tmp_table_param,all_fields); if (make_sum_func_list(&join,all_fields) || thd->fatal_error) goto err; /* purecov: inspected */ } @@ -763,14 +896,60 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, /* If we have already done the group, add HAVING to sorted table */ if (having && ! group && ! join.sort_and_group) { - if (fix_having(&join,&having)) - goto err; + having->update_used_tables(); // Some tables may have been const + JOIN_TAB *table=&join.join_tab[join.const_tables]; + table_map used_tables= join.const_table_map | table->table->map; + + Item* sort_table_cond=make_cond_for_table(having,used_tables,used_tables); + if (sort_table_cond) + { + if (!table->select) + if (!(table->select=new SQL_SELECT)) + goto err; + if (!table->select->cond) + table->select->cond=sort_table_cond; + else // This should never happen + if (!(table->select->cond=new Item_cond_and(table->select->cond, + sort_table_cond))) + goto err; + table->select_cond=table->select->cond; + table->select_cond->top_level_item(); + DBUG_EXECUTE("where",print_where(table->select->cond, + "select and having");); + having=make_cond_for_table(having,~ (table_map) 0,~used_tables); + DBUG_EXECUTE("where",print_where(conds,"having after sort");); + } + } + if (group) + select_limit= HA_POS_ERROR; + else + { + /* + We can abort sorting after thd->select_limit rows if we there is no + WHERE clause for any tables after the sorted one. + */ + JOIN_TAB *table= &join.join_tab[join.const_tables+1]; + JOIN_TAB *end_table= &join.join_tab[join.tables]; + for (; table < end_table ; table++) + { + /* + table->keyuse is set in the case there was an original WHERE clause + on the table that was optimized away. + table->on_expr tells us that it was a LEFT JOIN and there will be + at least one row generated from the table. + */ + if (table->select_cond || (table->keyuse && !table->on_expr)) + { + /* We have to sort all rows */ + select_limit= HA_POS_ERROR; + break; + } + } } if (create_sort_index(&join.join_tab[join.const_tables], group ? group : order, - (having || group || - join.const_tables != join.tables - 1) ? - HA_POS_ERROR : thd->select_limit)) + select_limit, + thd->select_limit)) goto err; /* purecov: inspected */ } join.having=having; // Actually a parameter @@ -778,7 +957,8 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, error=do_select(&join,&fields,NULL,procedure); err: - thd->examined_row_count=join.examined_rows; + thd->limit_found_rows = join.send_records; + thd->examined_row_count = join.examined_rows; thd->proc_info="end"; join.lock=0; // It's faster to unlock later join_free(&join); @@ -794,12 +974,12 @@ err: } /***************************************************************************** -** Create JOIN_TABS, make a guess about the table types, -** Approximate how many records will be used in each table + Create JOIN_TABS, make a guess about the table types, + Approximate how many records will be used in each table *****************************************************************************/ static ha_rows get_quick_record_count(SQL_SELECT *select,TABLE *table, - key_map keys) + key_map keys,ha_rows limit) { int error; DBUG_ENTER("get_quick_record_count"); @@ -807,7 +987,7 @@ static ha_rows get_quick_record_count(SQL_SELECT *select,TABLE *table, { select->head=table; table->reginfo.impossible_range=0; - if ((error=select->test_quick_select(keys,(table_map) 0,HA_POS_ERROR)) + if ((error=select->test_quick_select(keys,(table_map) 0,limit)) == 1) DBUG_RETURN(select->quick->records); if (error == -1) @@ -821,16 +1001,23 @@ static ha_rows get_quick_record_count(SQL_SELECT *select,TABLE *table, } +/* + Calculate the best possible join and initialize the join structure + + RETURN VALUES + 0 ok + 1 Fatal error +*/ + static bool make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds, DYNAMIC_ARRAY *keyuse_array) { int error; uint i,table_count,const_count,found_ref,refs,key,const_ref,eq_part; - table_map const_table_map,all_table_map; + table_map found_const_table_map,all_table_map; TABLE **table_vector; JOIN_TAB *stat,*stat_end,*s,**stat_ref; - SQL_SELECT *select; KEYUSE *keyuse,*start_keyuse; table_map outer_join=0; JOIN_TAB *stat_vector[MAX_TABLES+1]; @@ -842,12 +1029,11 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds, table_vector=(TABLE**) join->thd->alloc(sizeof(TABLE*)*(table_count*2)); if (!stat || !stat_ref || !table_vector) DBUG_RETURN(1); // Eom /* purecov: inspected */ - select=0; join->best_ref=stat_vector; stat_end=stat+table_count; - const_table_map=all_table_map=0; + found_const_table_map=all_table_map=0; const_count=0; for (s=stat,i=0 ; tables ; s++,tables=tables->next,i++) @@ -862,13 +1048,13 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds, bzero((char*) table->const_key_parts, sizeof(key_part_map)*table->keys); all_table_map|= table->map; s->join=join; + s->info=0; // For describe if ((s->on_expr=tables->on_expr)) { + /* Left join */ if (!table->file->records) { // Empty table - s->key_dependent=s->dependent=0; - s->type=JT_SYSTEM; - const_table_map|=table->map; + s->key_dependent=s->dependent=0; // Ignore LEFT JOIN depend. set_position(join,const_count++,s,(KEYUSE*) 0); continue; } @@ -887,11 +1073,9 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds, s->dependent=(table_map) 0; s->key_dependent=(table_map) 0; if ((table->system || table->file->records <= 1) && ! s->dependent && - !(table->file->option_flag() & HA_NOT_EXACT_COUNT) && + !(table->file->table_flags() & HA_NOT_EXACT_COUNT) && !table->fulltext_searched) { - s->type=JT_SYSTEM; - const_table_map|=table->map; set_position(join,const_count++,s,(KEYUSE*) 0); } } @@ -899,10 +1083,10 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds, join->outer_join=outer_join; /* - ** If outer join: Re-arrange tables in stat_vector so that outer join - ** tables are after all tables it is dependent of. - ** For example: SELECT * from A LEFT JOIN B ON B.c=C.c, C WHERE A.C=C.C - ** Will shift table B after table C. + If outer join: Re-arrange tables in stat_vector so that outer join + tables are after all tables it is dependent of. + For example: SELECT * from A LEFT JOIN B ON B.c=C.c, C WHERE A.C=C.C + Will shift table B after table C. */ if (outer_join) { @@ -939,31 +1123,66 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds, conds,~outer_join)) DBUG_RETURN(1); + /* Read tables with 0 or 1 rows (system tables) */ + join->const_table_map= 0; + + for (POSITION *p_pos=join->positions, *p_end=p_pos+const_count; + p_pos < p_end ; + p_pos++) + { + int tmp; + s= p_pos->table; + s->type=JT_SYSTEM; + join->const_table_map|=s->table->map; + if ((tmp=join_read_const_table(s, p_pos))) + { + if (tmp > 0) + DBUG_RETURN(1); // Fatal error + } + else + found_const_table_map|= s->table->map; + } + /* loop until no more const tables are found */ int ref_changed; do { ref_changed = 0; found_ref=0; - for (JOIN_TAB **pos=stat_vector+const_count; (s= *pos) ; pos++) + + /* + We only have to loop from stat_vector + const_count as + set_position() will move all const_tables first in stat_vector + */ + + for (JOIN_TAB **pos=stat_vector+const_count ; (s= *pos) ; pos++) { + TABLE *table=s->table; if (s->dependent) // If dependent on some table { - if (s->dependent & ~(const_table_map)) // All dep. must be constants + // All dep. must be constants + if (s->dependent & ~(found_const_table_map)) continue; - if (s->table->file->records <= 1L && - !(s->table->file->option_flag() & HA_NOT_EXACT_COUNT)) + if (table->file->records <= 1L && + !(table->file->table_flags() & HA_NOT_EXACT_COUNT)) { // system table + int tmp= 0; s->type=JT_SYSTEM; - const_table_map|=s->table->map; + join->const_table_map|=table->map; set_position(join,const_count++,s,(KEYUSE*) 0); + if ((tmp= join_read_const_table(s,join->positions+const_count-1))) + { + if (tmp > 0) + DBUG_RETURN(1); // Fatal error + } + else + found_const_table_map|= table->map; continue; } } /* check if table can be read by key or table only uses const refs */ if ((keyuse=s->keyuse)) { - TABLE *table=s->table; s->type= JT_REF; while (keyuse->table == table) { @@ -976,7 +1195,7 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds, { if (keyuse->val->type() != Item::NULL_ITEM) { - if (!((~const_table_map) & keyuse->used_tables)) + if (!((~found_const_table_map) & keyuse->used_tables)) const_ref|= (key_map) 1 << keyuse->keypart; else refs|=keyuse->used_tables; @@ -991,10 +1210,22 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds, { if (const_ref == eq_part) { // Found everything for ref. - s->type=JT_CONST; - const_table_map|=table->map; - set_position(join,const_count++,s,start_keyuse); + int tmp; ref_changed = 1; + s->type= JT_CONST; + join->const_table_map|=table->map; + set_position(join,const_count++,s,start_keyuse); + if (create_ref_for_key(join, s, start_keyuse, + found_const_table_map)) + DBUG_RETURN(1); + if ((tmp=join_read_const_table(s, + join->positions+const_count-1))) + { + if (tmp > 0) + DBUG_RETURN(1); // Fatal error + } + else + found_const_table_map|= table->map; break; } else @@ -1003,7 +1234,7 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds, } } } - } while (const_table_map & found_ref && ref_changed); + } while (join->const_table_map & found_ref && ref_changed); /* Calc how many (possible) matched records in each table */ @@ -1019,8 +1250,11 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds, s->found_records=s->records=s->table->file->records; s->read_time=(ha_rows) s->table->file->scan_time(); - /* Set a max range of how many seeks we can expect when using keys */ - s->worst_seeks= (double) (s->read_time*2); + /* + Set a max range of how many seeks we can expect when using keys + This was (s->read_time*5), but this was too low with small rows + */ + s->worst_seeks= (double) s->found_records / 5; if (s->worst_seeks < 2.0) // Fix for small tables s->worst_seeks=2.0; @@ -1029,32 +1263,54 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds, if (s->const_keys) { ha_rows records; - if (!select) - select=make_select(s->table,0, - 0, - and_conds(conds,s->on_expr),&error); - records=get_quick_record_count(select,s->table, s->const_keys); + SQL_SELECT *select; + select= make_select(s->table, found_const_table_map, + found_const_table_map, + s->on_expr ? s->on_expr : conds, + &error); + records= get_quick_record_count(select,s->table, s->const_keys, + join->row_limit); s->quick=select->quick; s->needed_reg=select->needed_reg; select->quick=0; + if (records == 0 && s->table->reginfo.impossible_range) + { + /* + Impossible WHERE or ON expression + In case of ON, we mark that the we match one empty NULL row. + In case of WHERE, don't set found_const_table_map to get the + caller to abort with a zero row result. + */ + join->const_table_map|= s->table->map; + set_position(join,const_count++,s,(KEYUSE*) 0); + s->type= JT_CONST; + if (s->on_expr) + { + /* Generate empty row */ + s->info= "Impossible ON condition"; + found_const_table_map|= s->table->map; + s->type= JT_CONST; + mark_as_null_row(s->table); // All fields are NULL + } + } if (records != HA_POS_ERROR) { s->found_records=records; s->read_time= (ha_rows) (s->quick ? s->quick->read_time : 0.0); } + delete select; } } - delete select; /* Find best combination and return it */ join->join_tab=stat; join->map2table=stat_ref; join->table= join->all_tables=table_vector; join->const_tables=const_count; - join->const_table_map=const_table_map; + join->found_const_table_map=found_const_table_map; if (join->const_tables != join->tables) - find_best_combination(join,all_table_map & ~const_table_map); + find_best_combination(join,all_table_map & ~join->const_table_map); else { memcpy((gptr) join->best_positions,(gptr) join->positions, @@ -1066,11 +1322,11 @@ make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds, /***************************************************************************** -** check with keys are used and with tables references with tables -** updates in stat: -** keys Bitmap of all used keys -** const_keys Bitmap of all keys with may be used with quick_select -** keyuse Pointer to possible keys + Check with keys are used and with tables references with tables + Updates in stat: + keys Bitmap of all used keys + const_keys Bitmap of all keys with may be used with quick_select + keyuse Pointer to possible keys *****************************************************************************/ typedef struct key_field_t { // Used when finding key fields @@ -1091,7 +1347,7 @@ merge_key_fields(KEY_FIELD *start,KEY_FIELD *new_fields,KEY_FIELD *end, if (start == new_fields) return start; // Impossible or if (new_fields == end) - return start; // No new fields, skipp all + return start; // No new fields, skip all KEY_FIELD *first_free=new_fields; @@ -1153,7 +1409,7 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, // Don't remove column IS NULL on a LEFT JOIN table if (!eq_func || !value || value->type() != Item::NULL_ITEM || !field->table->maybe_null || field->null_ptr) - return; // Not a key. Skipp it + return; // Not a key. Skip it exists_optimize=1; } else @@ -1172,11 +1428,13 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, else { JOIN_TAB *stat=field->table->reginfo.join_tab; - stat[0].keys|=field->key_start; // Add possible keys + key_map possible_keys= (field->key_start & + field->table->keys_in_use_for_query); + stat[0].keys|= possible_keys; // Add possible keys if (!value) { // Probably BETWEEN or IN - stat[0].const_keys |= field->key_start; + stat[0].const_keys |= possible_keys; return; // Can't be used as eq key } @@ -1190,7 +1448,7 @@ add_key_field(KEY_FIELD **key_fields,uint and_level, */ stat[0].key_dependent|=used_tables; if (value->const_item()) - stat[0].const_keys |= field->key_start; + stat[0].const_keys |= possible_keys; /* We can't always use indexes when comparing a string index to a number. cmp_type() is checked to allow compare of dates to numbers */ @@ -1217,7 +1475,7 @@ add_key_fields(JOIN_TAB *stat,KEY_FIELD **key_fields,uint *and_level, { if (cond->type() == Item_func::COND_ITEM) { - List_iterator<Item> li(*((Item_cond*) cond)->argument_list()); + List_iterator_fast<Item> li(*((Item_cond*) cond)->argument_list()); KEY_FIELD *org_key_fields= *key_fields; if (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC) @@ -1300,8 +1558,8 @@ add_key_fields(JOIN_TAB *stat,KEY_FIELD **key_fields,uint *and_level, } /* -** Add all keys with uses 'field' for some keypart -** If field->and_level != and_level then only mark key_part as const_part + Add all keys with uses 'field' for some keypart + If field->and_level != and_level then only mark key_part as const_part */ static uint @@ -1371,7 +1629,7 @@ add_ft_keys(DYNAMIC_ARRAY *keyuse_array, *arg1=(Item_func *)(func->arguments()[1]); if ((functype == Item_func::GE_FUNC || functype == Item_func::GT_FUNC) && - arg0->type() == Item::FUNC_ITEM && + arg0->type() == Item::FUNC_ITEM && arg0->functype() == Item_func::FT_FUNC && arg1->const_item() && arg1->val()>0) cond_func=(Item_func_match *) arg0; @@ -1385,13 +1643,13 @@ add_ft_keys(DYNAMIC_ARRAY *keyuse_array, } else if (cond->type() == Item::COND_ITEM) { - List_iterator<Item> li(*((Item_cond*) cond)->argument_list()); + List_iterator_fast<Item> li(*((Item_cond*) cond)->argument_list()); if (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC) { Item *item; /* - I', (Sergei) too lazy to implement proper recursive descent here, + I'm (Sergei) too lazy to implement proper recursive descent here, and anyway, nobody will use such a stupid queries that will require it :-) May be later... @@ -1408,7 +1666,7 @@ add_ft_keys(DYNAMIC_ARRAY *keyuse_array, } } - if(!cond_func) + if (!cond_func || cond_func->key == NO_SUCH_KEY) return; KEYUSE keyuse; @@ -1436,9 +1694,9 @@ sort_keyuse(KEYUSE *a,KEYUSE *b) /* -** Update keyuse array with all possible keys we can use to fetch rows -** join_tab is a array in tablenr_order -** stat is a reference array in 'prefered' order. + Update keyuse array with all possible keys we can use to fetch rows + join_tab is a array in tablenr_order + stat is a reference array in 'prefered' order. */ static bool @@ -1471,15 +1729,15 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab, add_key_part(keyuse,field); } - if (thd->lex.ftfunc_list.elements) + if (thd->lex.select->ftfunc_list.elements) { add_ft_keys(keyuse,join_tab,cond,normal_tables); } /* - ** remove ref if there is a keypart which is a ref and a const. - ** remove keyparts without previous keyparts. - ** Special treatment for ft-keys. SerG. + Remove ref if there is a keypart which is a ref and a const. + Remove keyparts without previous keyparts. + Special treatment for ft-keys. */ if (keyuse->elements) { @@ -1529,8 +1787,8 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab, /***************************************************************************** -** Go through all combinations of not marked tables and find the one -** which uses least records + Go through all combinations of not marked tables and find the one + which uses least records *****************************************************************************/ /* Save const tables first as used tables */ @@ -1545,7 +1803,7 @@ set_position(JOIN *join,uint idx,JOIN_TAB *table,KEYUSE *key) /* Move the const table as down as possible in best_ref */ JOIN_TAB **pos=join->best_ref+idx+1; JOIN_TAB *next=join->best_ref[idx]; - for ( ;next != table ; pos++) + for (;next != table ; pos++) { JOIN_TAB *tmp=pos[0]; pos[0]=next; @@ -1569,8 +1827,9 @@ static void find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, double read_time) { - ulong rec; + ha_rows rec; double tmp; + THD *thd= current_thd; if (!rest_tables) { @@ -1636,12 +1895,12 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, found_ref|= keyuse->used_tables; } /* - ** If we find a ref, assume this table matches a proportional - ** part of this table. - ** For example 100 records matching a table with 5000 records - ** gives 5000/100 = 50 records per key - ** Constant tables are ignored and to avoid bad matches, - ** we don't make rec less than 100. + If we find a ref, assume this table matches a proportional + part of this table. + For example 100 records matching a table with 5000 records + gives 5000/100 = 50 records per key + Constant tables are ignored and to avoid bad matches, + we don't make rec less than 100. */ if (keyuse->used_tables & (map=(keyuse->used_tables & ~join->const_table_map))) @@ -1662,7 +1921,7 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, } while (keyuse->table == table && keyuse->key == key); /* - ** Assume that that each key matches a proportional part of table. + Assume that that each key matches a proportional part of table. */ if (!found_part && !ft_key) continue; // Nothing usable found @@ -1670,13 +1929,13 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, rec=1L; // Fix for small tables /* - ** ft-keys require special treatment + ft-keys require special treatment */ if (ft_key) { /* - ** Really, there should be records=0.0 (yes!) - ** but 1.0 would be probably safer + Really, there should be records=0.0 (yes!) + but 1.0 would be probably safer */ tmp=prev_record_reads(join,found_ref); records=1.0; @@ -1684,7 +1943,7 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, else { /* - ** Check if we found full key + Check if we found full key */ if (found_part == PREV_BITS(uint,keyinfo->key_parts)) { /* use eq key */ @@ -1720,7 +1979,7 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, { /* we can use only index tree */ uint keys_per_block= table->file->block_size/2/ - keyinfo->key_length+1; + (keyinfo->key_length+table->file->ref_length)+1; tmp=(record_count*(records+keys_per_block-1)/ keys_per_block); } @@ -1731,17 +1990,18 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, else { /* - ** Use as much key-parts as possible and a uniq key is better - ** than a not unique key - ** Set tmp to (previous record count) * (records / combination) + Use as much key-parts as possible and a uniq key is better + than a not unique key + Set tmp to (previous record count) * (records / combination) */ if ((found_part & 1) && - !(table->file->option_flag() & HA_ONLY_WHOLE_INDEX)) + !(table->file->index_flags(key) & HA_ONLY_WHOLE_INDEX)) { max_key_part=max_part_bit(found_part); - /* Check if quick_range could determinate how many rows we - will match */ - + /* + Check if quick_range could determinate how many rows we + will match + */ if (table->quick_keys & ((key_map) 1 << key) && table->quick_key_parts[key] <= max_key_part) tmp=records= (double) table->quick_rows[key]; @@ -1753,18 +2013,18 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, else { /* - ** Assume that the first key part matches 1% of the file - ** and that the hole key matches 10 (dupplicates) or 1 - ** (unique) records. - ** Assume also that more key matches proportionally more - ** records - ** This gives the formula: - ** records= (x * (b-a) + a*c-b)/(c-1) - ** - ** b = records matched by whole key - ** a = records matched by first key part (10% of all records?) - ** c = number of key parts in key - ** x = used key parts (1 <= x <= c) + Assume that the first key part matches 1% of the file + and that the hole key matches 10 (dupplicates) or 1 + (unique) records. + Assume also that more key matches proportionally more + records + This gives the formula: + records= (x * (b-a) + a*c-b)/(c-1) + + b = records matched by whole key + a = records matched by first key part (10% of all records?) + c = number of key parts in key + x = used key parts (1 <= x <= c) */ double rec_per_key; if (!(rec_per_key=(double) @@ -1790,7 +2050,7 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, { /* we can use only index tree */ uint keys_per_block= table->file->block_size/2/ - keyinfo->key_length+1; + (keyinfo->key_length+table->file->ref_length)+1; tmp=record_count*(tmp+keys_per_block-1)/keys_per_block; } else @@ -1815,21 +2075,30 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, /* Don't test table scan if it can't be better. Prefer key lookup if we would use the same key for scanning. + + Don't do a table scan on InnoDB tables, if we can read the used + parts of the row from any of the used index. + This is because table scans uses index and we would not win + anything by using a table scan. */ if ((records >= s->found_records || best > s->read_time) && !(s->quick && best_key && s->quick->index == best_key->key && - best_max_key_part >= s->table->quick_key_parts[best_key->key])) + best_max_key_part >= s->table->quick_key_parts[best_key->key]) && + !((s->table->file->table_flags() & HA_TABLE_SCAN_ON_INDEX) && + s->table->used_keys && best_key) && + !(s->table->force_index && best_key)) { // Check full join if (s->on_expr) { - tmp=s->found_records; // Can't use read cache + tmp=rows2double(s->found_records); // Can't use read cache } else { tmp=(double) s->read_time; /* Calculate time to read through cache */ tmp*=(1.0+floor((double) cache_record_length(join,idx)* - record_count/(double) join_buff_size)); + record_count / + (double) thd->variables.join_buff_size)); } if (best == DBL_MAX || (tmp + record_count/(double) TIME_FOR_COMPARE*s->found_records < @@ -1840,15 +2109,16 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, will ensure that this will be used */ best=tmp; - records=s->found_records; + records= rows2double(s->found_records); best_key=0; } } - join->positions[idx].records_read=(double) records; + join->positions[idx].records_read= records; join->positions[idx].key=best_key; join->positions[idx].table= s; if (!best_key && idx == join->const_tables && - s->table == join->sort_by_table) + s->table == join->sort_by_table && + join->thd->select_limit >= records) join->sort_by_table= (TABLE*) 1; // Must use temporary table /* @@ -1881,7 +2151,7 @@ find_best(JOIN *join,table_map rest_tables,uint idx,double record_count, /* -** Find how much space the prevous read not const tables takes in cache + Find how much space the prevous read not const tables takes in cache */ static void calc_used_field_length(THD *thd, JOIN_TAB *join_tab) @@ -1957,27 +2227,24 @@ prev_record_reads(JOIN *join,table_map found_ref) /***************************************************************************** -** Set up join struct according to best position. + Set up join struct according to best position. *****************************************************************************/ static bool get_best_combination(JOIN *join) { - uint i,key,tablenr; + uint i,tablenr; table_map used_tables; - TABLE *table; JOIN_TAB *join_tab,*j; KEYUSE *keyuse; - KEY *keyinfo; uint table_count; + THD *thd=join->thd; table_count=join->tables; if (!(join->join_tab=join_tab= - (JOIN_TAB*) join->thd->alloc(sizeof(JOIN_TAB)*table_count))) + (JOIN_TAB*) thd->alloc(sizeof(JOIN_TAB)*table_count))) return TRUE; - join->const_tables=0; /* for checking */ - join->const_table_map=0; join->full_join=0; used_tables=0; @@ -1986,188 +2253,188 @@ get_best_combination(JOIN *join) TABLE *form; *j= *join->best_positions[tablenr].table; form=join->table[tablenr]=j->table; - j->ref.key = -1; - j->ref.key_parts=0; - j->info=0; // For describe used_tables|= form->map; form->reginfo.join_tab=j; if (!j->on_expr) form->reginfo.not_exists_optimize=0; // Only with LEFT JOIN + if (j->type == JT_CONST) + continue; // Handled in make_join_stat.. + + j->ref.key = -1; + j->ref.key_parts=0; if (j->type == JT_SYSTEM) - { - j->table->const_table=1; - if (join->const_tables == tablenr) - { - join->const_tables++; - join->const_table_map|=form->map; - } continue; - } if (!j->keys || !(keyuse= join->best_positions[tablenr].key)) { j->type=JT_ALL; if (tablenr != join->const_tables) join->full_join=1; } - else - { - uint keyparts,length; - bool ftkey=(keyuse->keypart == FT_KEYPART); - /* - ** Use best key from find_best - */ - table=j->table; - key=keyuse->key; + else if (create_ref_for_key(join, j, keyuse, used_tables)) + return TRUE; // Something went wrong + } - keyinfo=table->key_info+key; - if (ftkey) - { - Item_func_match *ifm=(Item_func_match *)keyuse->val; + for (i=0 ; i < table_count ; i++) + join->map2table[join->join_tab[i].table->tablenr]=join->join_tab+i; + update_depend_map(join); + return 0; +} - length=0; - keyparts=1; - ifm->join_key=1; - } - else - { - keyparts=length=0; - do - { - if (!((~used_tables) & keyuse->used_tables)) - { - if (keyparts == keyuse->keypart) - { - keyparts++; - length+=keyinfo->key_part[keyuse->keypart].store_length; - } - } - keyuse++; - } while (keyuse->table == table && keyuse->key == key); - } /* not ftkey */ - - /* set up fieldref */ - keyinfo=table->key_info+key; - j->ref.key_parts=keyparts; - j->ref.key_length=length; - j->ref.key=(int) key; - if (!(j->ref.key_buff= (byte*) sql_calloc(ALIGN_SIZE(length)*2)) || - !(j->ref.key_copy= (store_key**) sql_alloc((sizeof(store_key*) * - (keyparts+1)))) || - !(j->ref.items= (Item**) sql_alloc(sizeof(Item*)*keyparts))) - { - return TRUE; - } - j->ref.key_buff2=j->ref.key_buff+ALIGN_SIZE(length); - j->ref.key_err=1; - keyuse=join->best_positions[tablenr].key; - store_key **ref_key=j->ref.key_copy; - byte *key_buff=j->ref.key_buff; - if (ftkey) - { - j->ref.items[0]=((Item_func*)(keyuse->val))->key_item(); - if (keyuse->used_tables) - return TRUE; // not supported yet. SerG +static bool create_ref_for_key(JOIN *join, JOIN_TAB *j, KEYUSE *org_keyuse, + table_map used_tables) +{ + KEYUSE *keyuse=org_keyuse; + bool ftkey=(keyuse->keypart == FT_KEYPART); + THD *thd= join->thd; + uint keyparts,length,key; + TABLE *table; + KEY *keyinfo; - j->type=JT_FT; - } - else - { - THD *thd=join->thd; - for (i=0 ; i < keyparts ; keyuse++,i++) - { - while (keyuse->keypart != i || - ((~used_tables) & keyuse->used_tables)) - keyuse++; /* Skipp other parts */ - - uint maybe_null= test(keyinfo->key_part[i].null_bit); - j->ref.items[i]=keyuse->val; // Save for cond removal - if (!keyuse->used_tables && - !(join->select_options & SELECT_DESCRIBE)) - { // Compare against constant - store_key_item *tmp=new store_key_item(keyinfo->key_part[i].field, - (char*)key_buff + - maybe_null, - maybe_null ? - (char*) key_buff : 0, - keyinfo->key_part[i].length, - keyuse->val); - if (thd->fatal_error) - { - return TRUE; - } - tmp->copy(); - } - else - *ref_key++= get_store_key(keyuse,join->const_table_map, - &keyinfo->key_part[i], - (char*) key_buff,maybe_null); - key_buff+=keyinfo->key_part[i].store_length; - } - } /* not ftkey */ - *ref_key=0; // end_marker - if (j->type == JT_FT) /* no-op */; - else if (j->type == JT_CONST) + /* + Use best key from find_best + */ + table=j->table; + key=keyuse->key; + keyinfo=table->key_info+key; + + if (ftkey) + { + Item_func_match *ifm=(Item_func_match *)keyuse->val; + + length=0; + keyparts=1; + ifm->join_key=1; + } + else + { + keyparts=length=0; + do + { + if (!((~used_tables) & keyuse->used_tables)) { - j->table->const_table=1; - if (join->const_tables == tablenr) + if (keyparts == keyuse->keypart) { - join->const_tables++; - join->const_table_map|=form->map; + keyparts++; + length+=keyinfo->key_part[keyuse->keypart].store_length; } } - else if (((keyinfo->flags & (HA_NOSAME | HA_NULL_PART_KEY)) != HA_NOSAME) || - keyparts != keyinfo->key_parts) - j->type=JT_REF; /* Must read with repeat */ - else if (ref_key == j->ref.key_copy) - { /* Should never be reached */ - /* - This happen if we are using a constant expression in the ON part - of an LEFT JOIN. - SELECT * FROM a LEFT JOIN b ON b.key=30 - Here we should not mark the table as a 'const' as a field may - have a 'normal' value or a NULL value. - */ - j->type=JT_CONST; - if (join->const_tables == tablenr && !form->fulltext_searched) + keyuse++; + } while (keyuse->table == table && keyuse->key == key); + } /* not ftkey */ + + /* set up fieldref */ + keyinfo=table->key_info+key; + j->ref.key_parts=keyparts; + j->ref.key_length=length; + j->ref.key=(int) key; + if (!(j->ref.key_buff= (byte*) thd->calloc(ALIGN_SIZE(length)*2)) || + !(j->ref.key_copy= (store_key**) thd->alloc((sizeof(store_key*) * + (keyparts+1)))) || + !(j->ref.items= (Item**) thd->alloc(sizeof(Item*)*keyparts))) + { + return TRUE; + } + j->ref.key_buff2=j->ref.key_buff+ALIGN_SIZE(length); + j->ref.key_err=1; + keyuse=org_keyuse; + + store_key **ref_key=j->ref.key_copy; + byte *key_buff=j->ref.key_buff; + if (ftkey) + { + j->ref.items[0]=((Item_func*)(keyuse->val))->key_item(); + if (keyuse->used_tables) + return TRUE; // not supported yet. SerG + + j->type=JT_FT; + } + else + { + uint i; + for (i=0 ; i < keyparts ; keyuse++,i++) + { + while (keyuse->keypart != i || + ((~used_tables) & keyuse->used_tables)) + keyuse++; /* Skip other parts */ + + uint maybe_null= test(keyinfo->key_part[i].null_bit); + j->ref.items[i]=keyuse->val; // Save for cond removal + if (!keyuse->used_tables && + !(join->select_options & SELECT_DESCRIBE)) + { // Compare against constant + store_key_item *tmp=new store_key_item(thd, + keyinfo->key_part[i].field, + (char*)key_buff + + maybe_null, + maybe_null ? + (char*) key_buff : 0, + keyinfo->key_part[i].length, + keyuse->val); + if (thd->fatal_error) { - join->const_tables++; - join->const_table_map|=form->map; + return TRUE; } + tmp->copy(); } else - j->type=JT_EQ_REF; - } + *ref_key++= get_store_key(thd, + keyuse,join->const_table_map, + &keyinfo->key_part[i], + (char*) key_buff,maybe_null); + key_buff+=keyinfo->key_part[i].store_length; + } + } /* not ftkey */ + *ref_key=0; // end_marker + if (j->type == JT_FT) /* no-op */; + else if (j->type == JT_CONST) + j->table->const_table=1; + else if (((keyinfo->flags & (HA_NOSAME | HA_NULL_PART_KEY)) + != HA_NOSAME) || + keyparts != keyinfo->key_parts) + j->type=JT_REF; /* Must read with repeat */ + else if (ref_key == j->ref.key_copy) + { + /* + This happen if we are using a constant expression in the ON part + of an LEFT JOIN. + SELECT * FROM a LEFT JOIN b ON b.key=30 + Here we should not mark the table as a 'const' as a field may + have a 'normal' value or a NULL value. + */ + j->type=JT_CONST; } - - for (i=0 ; i < table_count ; i++) - join->map2table[join->join_tab[i].table->tablenr]=join->join_tab+i; - update_depend_map(join); + else + j->type=JT_EQ_REF; return 0; } + static store_key * -get_store_key(KEYUSE *keyuse, table_map used_tables, KEY_PART_INFO *key_part, - char *key_buff, uint maybe_null) +get_store_key(THD *thd, KEYUSE *keyuse, table_map used_tables, + KEY_PART_INFO *key_part, char *key_buff, uint maybe_null) { if (!((~used_tables) & keyuse->used_tables)) // if const item { - return new store_key_const_item(key_part->field, + return new store_key_const_item(thd, + key_part->field, key_buff + maybe_null, maybe_null ? key_buff : 0, key_part->length, keyuse->val); } else if (keyuse->val->type() == Item::FIELD_ITEM) - return new store_key_field(key_part->field, + return new store_key_field(thd, + key_part->field, key_buff + maybe_null, maybe_null ? key_buff : 0, key_part->length, ((Item_field*) keyuse->val)->field, keyuse->val->full_name()); - return new store_key_item(key_part->field, + return new store_key_item(thd, + key_part->field, key_buff + maybe_null, maybe_null ? key_buff : 0, key_part->length, @@ -2175,19 +2442,20 @@ get_store_key(KEYUSE *keyuse, table_map used_tables, KEY_PART_INFO *key_part, } /* -** This function is only called for const items on fields which are keys -** returns 1 if there was some conversion made when the field was stored. + This function is only called for const items on fields which are keys + returns 1 if there was some conversion made when the field was stored. */ bool store_val_in_field(Field *field,Item *item) { + bool error; THD *thd=current_thd; - ulong cuted_fields=thd->cuted_fields; + ha_rows cuted_fields=thd->cuted_fields; thd->count_cuted_fields=1; - item->save_in_field(field); + error= item->save_in_field(field, 1); thd->count_cuted_fields=0; - return cuted_fields != thd->cuted_fields; + return error || cuted_fields != thd->cuted_fields; } @@ -2205,13 +2473,15 @@ make_simple_join(JOIN *join,TABLE *tmp_table) join->tables=1; join->const_tables=0; join->const_table_map=0; - join->tmp_table_param.copy_field_count=join->tmp_table_param.field_count= - join->tmp_table_param.sum_func_count= join->tmp_table_param.func_count=0; - join->tmp_table_param.copy_field=0; + join->tmp_table_param.field_count= join->tmp_table_param.sum_func_count= + join->tmp_table_param.func_count=0; + join->tmp_table_param.copy_field=join->tmp_table_param.copy_field_end=0; join->first_record=join->sort_and_group=0; join->sum_funcs=0; join->send_records=(ha_rows) 0; join->group=0; + join->do_send_rows = 1; + join->row_limit=join->thd->select_limit; join_tab->cache.buff=0; /* No cacheing */ join_tab->table=tmp_table; @@ -2269,7 +2539,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) use_quick_range=1; tab->use_quick=1; tab->ref.key_parts=0; // Don't use ref key. - join->best_positions[i].records_read=tab->quick->records; + join->best_positions[i].records_read= rows2double(tab->quick->records); } COND *tmp=make_cond_for_table(cond,used_tables,current_map); @@ -2285,7 +2555,7 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) { DBUG_EXECUTE("where",print_where(tmp,tab->table->table_name);); SQL_SELECT *sel=tab->select=(SQL_SELECT*) - sql_memdup((gptr) select, sizeof(SQL_SELECT)); + join->thd->memdup((gptr) select, sizeof(SQL_SELECT)); if (!sel) DBUG_RETURN(1); // End of memory tab->select_cond=sel->cond=tmp; @@ -2327,15 +2597,19 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) */ if ((tab->keys & ~ tab->const_keys && i > 0) || - tab->const_keys && i == join->const_tables && - join->thd->select_limit < join->best_positions[i].records_read) + (tab->const_keys && i == join->const_tables && + join->thd->select_limit < join->best_positions[i].records_read && + !(join->select_options & OPTION_FOUND_ROWS))) { /* Join with outer join condition */ COND *orig_cond=sel->cond; sel->cond=and_conds(sel->cond,tab->on_expr); if (sel->test_quick_select(tab->keys, used_tables & ~ current_map, - join->thd->select_limit) < 0) + (join->select_options & + OPTION_FOUND_ROWS ? + HA_POS_ERROR : + join->thd->select_limit)) < 0) DBUG_RETURN(1); // Impossible range sel->cond=orig_cond; } @@ -2362,8 +2636,8 @@ make_join_select(JOIN *join,SQL_SELECT *select,COND *cond) current_map))) { DBUG_EXECUTE("where",print_where(tmp,"cache");); - tab->cache.select=(SQL_SELECT*) sql_memdup((gptr) sel, - sizeof(SQL_SELECT)); + tab->cache.select=(SQL_SELECT*) + join->thd->memdup((gptr) sel, sizeof(SQL_SELECT)); tab->cache.select->cond=tmp; tab->cache.select->read_tables=join->const_table_map; } @@ -2380,6 +2654,7 @@ static void make_join_readinfo(JOIN *join,uint options) { uint i; + SELECT_LEX *select_lex = &(join->thd->lex.select_lex); DBUG_ENTER("make_join_readinfo"); for (i=join->const_tables ; i < join->tables ; i++) @@ -2412,7 +2687,8 @@ make_join_readinfo(JOIN *join,uint options) table->file->index_init(tab->ref.key); tab->read_first_record= join_read_key; tab->read_record.read_record= join_no_more_records; - if (table->used_keys & ((key_map) 1 << tab->ref.key)) + if (table->used_keys & ((key_map) 1 << tab->ref.key) && + !table->no_keyread) { table->key_read=1; table->file->extra(HA_EXTRA_KEYREAD); @@ -2429,8 +2705,9 @@ make_join_readinfo(JOIN *join,uint options) tab->quick=0; table->file->index_init(tab->ref.key); tab->read_first_record= join_read_always_key; - tab->read_record.read_record= join_read_next; - if (table->used_keys & ((key_map) 1 << tab->ref.key)) + tab->read_record.read_record= join_read_next_same; + if (table->used_keys & ((key_map) 1 << tab->ref.key) && + !table->no_keyread) { table->key_read=1; table->file->extra(HA_EXTRA_KEYREAD); @@ -2444,10 +2721,10 @@ make_join_readinfo(JOIN *join,uint options) break; case JT_ALL: /* - ** if previous table use cache + If previous table use cache */ table->status=STATUS_NO_RECORD; - if (i != join->const_tables && (options & SELECT_USE_CACHE) && + if (i != join->const_tables && !(options & SELECT_NO_JOIN_CACHE) && tab->use_quick != 2 && !tab->on_expr) { if ((options & SELECT_DESCRIBE) || @@ -2460,7 +2737,7 @@ make_join_readinfo(JOIN *join,uint options) /* These init changes read_record */ if (tab->use_quick == 2) { - join->thd->lex.options|=QUERY_NO_GOOD_INDEX_USED; + select_lex->options|=QUERY_NO_GOOD_INDEX_USED; tab->read_first_record= join_init_quick_read_record; statistic_increment(select_range_check_count, &LOCK_status); } @@ -2475,7 +2752,7 @@ make_join_readinfo(JOIN *join,uint options) } else { - join->thd->lex.options|=QUERY_NO_INDEX_USED; + select_lex->options|=QUERY_NO_INDEX_USED; statistic_increment(select_scan_count, &LOCK_status); } } @@ -2487,22 +2764,25 @@ make_join_readinfo(JOIN *join,uint options) } else { - join->thd->lex.options|=QUERY_NO_INDEX_USED; + select_lex->options|=QUERY_NO_INDEX_USED; statistic_increment(select_full_join_count, &LOCK_status); } } - if (tab->select && tab->select->quick && - table->used_keys & ((key_map) 1 << tab->select->quick->index)) + if (!table->no_keyread) { - table->key_read=1; - table->file->extra(HA_EXTRA_KEYREAD); - } - else if (table->used_keys && ! (tab->select && tab->select->quick)) - { // Only read index tree - tab->index=find_shortest_key(table, table->used_keys); - tab->table->file->index_init(tab->index); - tab->read_first_record= join_init_read_first_with_key; - tab->type=JT_NEXT; // Read with index_first / index_next + if (tab->select && tab->select->quick && + table->used_keys & ((key_map) 1 << tab->select->quick->index)) + { + table->key_read=1; + table->file->extra(HA_EXTRA_KEYREAD); + } + else if (table->used_keys && ! (tab->select && tab->select->quick)) + { // Only read index tree + tab->index=find_shortest_key(table, table->used_keys); + tab->table->file->index_init(tab->index); + tab->read_first_record= join_read_first; + tab->type=JT_NEXT; // Read with index_first / index_next + } } } break; @@ -2519,6 +2799,38 @@ make_join_readinfo(JOIN *join,uint options) } +/* + Give error if we some tables are done with a full join + + SYNOPSIS + error_if_full_join() + join Join condition + + USAGE + This is used by multi_table_update and multi_table_delete when running + in safe mode + + RETURN VALUES + 0 ok + 1 Error (full join used) +*/ + +bool error_if_full_join(JOIN *join) +{ + for (JOIN_TAB *tab=join->join_tab, *end=join->join_tab+join->tables; + tab < end; + tab++) + { + if (tab->type == JT_ALL && (!tab->select || !tab->select->quick)) + { + my_error(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE,MYF(0)); + return(1); + } + } + return(0); +} + + static void join_free(JOIN *join) { @@ -2553,35 +2865,37 @@ join_free(JOIN *join) } join->table=0; } - // We are not using tables anymore - // Unlock all tables. We may be in an INSERT .... SELECT statement. - if (join->lock && join->thd->lock) + /* + We are not using tables anymore + Unlock all tables. We may be in an INSERT .... SELECT statement. + */ + if (join->lock && join->thd->lock && + !(join->select_options & SELECT_NO_UNLOCK)) { mysql_unlock_read_tables(join->thd, join->lock);// Don't free join->lock join->lock=0; } join->group_fields.delete_elements(); join->tmp_table_param.copy_funcs.delete_elements(); - delete [] join->tmp_table_param.copy_field; - join->tmp_table_param.copy_field=0; + join->tmp_table_param.cleanup(); DBUG_VOID_RETURN; } /***************************************************************************** -** Remove the following expressions from ORDER BY and GROUP BY: -** Constant expressions -** Expression that only uses tables that are of type EQ_REF and the reference -** is in the ORDER list or if all refereed tables are of the above type. -** -** In the following, the X field can be removed: -** SELECT * FROM t1,t2 WHERE t1.a=t2.a ORDER BY t1.a,t2.X -** SELECT * FROM t1,t2,t3 WHERE t1.a=t2.a AND t2.b=t3.b ORDER BY t1.a,t3.X -** -** These can't be optimized: -** SELECT * FROM t1,t2 WHERE t1.a=t2.a ORDER BY t2.X,t1.a -** SELECT * FROM t1,t2 WHERE t1.a=t2.a AND t1.b=t2.b ORDER BY t1.a,t2.c -** SELECT * FROM t1,t2 WHERE t1.a=t2.a ORDER BY t2.b,t1.a + Remove the following expressions from ORDER BY and GROUP BY: + Constant expressions + Expression that only uses tables that are of type EQ_REF and the reference + is in the ORDER list or if all refereed tables are of the above type. + + In the following, the X field can be removed: + SELECT * FROM t1,t2 WHERE t1.a=t2.a ORDER BY t1.a,t2.X + SELECT * FROM t1,t2,t3 WHERE t1.a=t2.a AND t2.b=t3.b ORDER BY t1.a,t3.X + + These can't be optimized: + SELECT * FROM t1,t2 WHERE t1.a=t2.a ORDER BY t2.X,t1.a + SELECT * FROM t1,t2 WHERE t1.a=t2.a AND t1.b=t2.b ORDER BY t1.a,t2.c + SELECT * FROM t1,t2 WHERE t1.a=t2.a ORDER BY t2.b,t1.a *****************************************************************************/ static bool @@ -2612,7 +2926,7 @@ eq_ref_table(JOIN *join, ORDER *start_order, JOIN_TAB *tab) if (order) { found++; - dbug_assert(!(order->used & map)); + DBUG_ASSERT(!(order->used & map)); order->used|=map; continue; // Used in ORDER BY } @@ -2621,7 +2935,7 @@ eq_ref_table(JOIN *join, ORDER *start_order, JOIN_TAB *tab) } } /* Check that there was no reference to table before sort order */ - for ( ; found && start_order ; start_order=start_order->next) + for (; found && start_order ; start_order=start_order->next) { if (start_order->used & map) { @@ -2639,7 +2953,7 @@ static bool only_eq_ref_tables(JOIN *join,ORDER *order,table_map tables) { if (specialflag & SPECIAL_SAFE_MODE) - return 0; // skip this optimize /* purecov: inspected */ + return 0; // skip this optimize /* purecov: inspected */ for (JOIN_TAB **tab=join->map2table ; tables ; tab++, tables>>=1) { if (tables & 1 && !eq_ref_table(join, order, *tab)) @@ -2655,7 +2969,7 @@ static void update_depend_map(JOIN *join) { JOIN_TAB *join_tab=join->join_tab, *end=join_tab+join->tables; - for ( ; join_tab != end ; join_tab++) + for (; join_tab != end ; join_tab++) { TABLE_REF *ref= &join_tab->ref; table_map depend_map=0; @@ -2679,7 +2993,7 @@ static void update_depend_map(JOIN *join) static void update_depend_map(JOIN *join, ORDER *order) { - for ( ; order ; order=order->next) + for (; order ; order=order->next) { table_map depend_map; order->item[0]->update_used_tables(); @@ -2727,7 +3041,7 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond, bool *simple_order) else if (!(order_tables & not_const_tables)) { DBUG_PRINT("info",("removing: %s", order->item[0]->full_name())); - continue; // skipp const item + continue; // skip const item } else { @@ -2762,16 +3076,17 @@ remove_const(JOIN *join,ORDER *first_order, COND *cond, bool *simple_order) DBUG_RETURN(first_order); } + static int -return_zero_rows(select_result *result,TABLE_LIST *tables,List<Item> &fields, - bool send_row, uint select_options,const char *info, - Item *having, Procedure *procedure) +return_zero_rows(JOIN *join, select_result *result,TABLE_LIST *tables, + List<Item> &fields, bool send_row, uint select_options, + const char *info, Item *having, Procedure *procedure) { DBUG_ENTER("return_zero_rows"); if (select_options & SELECT_DESCRIBE) { - describe_info(current_thd, info); + describe_info(join, info); DBUG_RETURN(0); } if (procedure) @@ -2786,17 +3101,23 @@ return_zero_rows(select_result *result,TABLE_LIST *tables,List<Item> &fields, if (having && having->val_int() == 0) send_row=0; } - if (!tables || !(result->send_fields(fields,1))) + if (!(result->send_fields(fields,1))) { if (send_row) + { + List_iterator_fast<Item> it(fields); + Item *item; + while ((item= it++)) + item->no_rows_in_result(); result->send_data(fields); - if (tables) // Not from do_select() + } + if (tables) // Not from do_select() { /* Close open cursors */ for (TABLE_LIST *table=tables; table ; table=table->next) table->table->file->index_end(); - result->send_eof(); // Should be safe } + result->send_eof(); // Should be safe } DBUG_RETURN(0); } @@ -2809,12 +3130,12 @@ static void clear_tables(JOIN *join) } /***************************************************************************** -** Make som simple condition optimization: -** If there is a test 'field = const' change all refs to 'field' to 'const' -** Remove all dummy tests 'item = item', 'const op const'. -** Remove all 'item is NULL', when item can never be null! -** item->marker should be 0 for all items on entry -** Return in cond_value FALSE if condition is impossible (1 = 2) + Make som simple condition optimization: + If there is a test 'field = const' change all refs to 'field' to 'const' + Remove all dummy tests 'item = item', 'const op const'. + Remove all 'item is NULL', when item can never be null! + item->marker should be 0 for all items on entry + Return in cond_value FALSE if condition is impossible (1 = 2) *****************************************************************************/ class COND_CMP :public ilink { @@ -2836,8 +3157,8 @@ template class List_iterator<Item_func_match>; #endif /* -** change field = field to field = const for each found field = const in the -** and_level + change field = field to field = const for each found field = const in the + and_level */ static void @@ -2914,7 +3235,7 @@ propagate_cond_constants(I_List<COND_CMP> *save_list,COND *and_level, { bool and_level= ((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC; - List_iterator<Item> li(*((Item_cond*) cond)->argument_list()); + List_iterator_fast<Item> li(*((Item_cond*) cond)->argument_list()); Item *item; I_List<COND_CMP> save; while ((item=li++)) @@ -2982,8 +3303,8 @@ optimize_cond(COND *conds,Item::cond_result *cond_value) DBUG_EXECUTE("where",print_where(conds,"original");); propagate_cond_constants((I_List<COND_CMP> *) 0,conds,conds); /* - ** Remove all instances of item == item - ** Remove all and-levels where CONST item != CONST item + Remove all instances of item == item + Remove all and-levels where CONST item != CONST item */ DBUG_EXECUTE("where",print_where(conds,"after const change");); conds=remove_eq_conds(conds,cond_value) ; @@ -2993,11 +3314,11 @@ optimize_cond(COND *conds,Item::cond_result *cond_value) /* -** remove const and eq items. Return new item, or NULL if no condition -** cond_value is set to according: -** COND_OK query is possible (field = constant) -** COND_TRUE always true ( 1 = 1 ) -** COND_FALSE always false ( 1 = 2 ) + Remove const and eq items. Return new item, or NULL if no condition + cond_value is set to according: + COND_OK query is possible (field = constant) + COND_TRUE always true ( 1 = 1 ) + COND_FALSE always false ( 1 = 2 ) */ static COND * @@ -3073,13 +3394,13 @@ remove_eq_conds(COND *cond,Item::cond_result *cond_value) ((Item_func*) cond)->functype() == Item_func::ISNULL_FUNC) { /* - ** Handles this special case for some ODBC applications: - ** The are requesting the row that was just updated with a auto_increment - ** value with this construct: - ** - ** SELECT * from table_name where auto_increment_column IS NULL - ** This will be changed to: - ** SELECT * from table_name where auto_increment_column = LAST_INSERT_ID + Handles this special case for some ODBC applications: + The are requesting the row that was just updated with a auto_increment + value with this construct: + + SELECT * from table_name where auto_increment_column IS NULL + This will be changed to: + SELECT * from table_name where auto_increment_column = LAST_INSERT_ID */ Item_func_isnull *func=(Item_func_isnull*) cond; @@ -3092,6 +3413,7 @@ remove_eq_conds(COND *cond,Item::cond_result *cond_value) (thd->options & OPTION_AUTO_IS_NULL) && thd->insert_id()) { + query_cache_abort(&thd->net); COND *new_cond; if ((new_cond= new Item_func_eq(args[0], new Item_int("last_insert_id()", @@ -3135,11 +3457,11 @@ remove_eq_conds(COND *cond,Item::cond_result *cond_value) } } *cond_value=Item::COND_OK; - return cond; /* Point at next and level */ + return cond; // Point at next and level } /* -** Return 1 if the item is a const value in all the WHERE clause + Return 1 if the item is a const value in all the WHERE clause */ static bool @@ -3149,7 +3471,7 @@ const_expression_in_where(COND *cond, Item *comp_item, Item **const_item) { bool and_level= (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC); - List_iterator<Item> li(*((Item_cond*) cond)->argument_list()); + List_iterator_fast<Item> li(*((Item_cond*) cond)->argument_list()); Item *item; while ((item=li++)) { @@ -3198,14 +3520,36 @@ const_expression_in_where(COND *cond, Item *comp_item, Item **const_item) /**************************************************************************** -** Create a temp table according to a field list. -** Set distinct if duplicates could be removed -** Given fields field pointers are changed to point at tmp_table -** for send_fields + Create internal temporary table ****************************************************************************/ -Field *create_tmp_field(TABLE *table,Item *item, Item::Type type, - Item_result_field ***copy_func, Field **from_field, +/* + Create field for temporary table + + SYNOPSIS + create_tmp_field() + thd Thread handler + table Temporary table + item Item to create a field for + type Type of item (normally item->type) + copy_func If set and item is a function, store copy of item + in this array + group 1 if we are going to do a relative group by on result + modify_item 1 if item->result_field should point to new item. + This is relevent for how fill_record() is going to + work: + If modify_item is 1 then fill_record() will update + the record in the original table. + If modify_item is 0 then fill_record() will update + the temporary table + + RETURN + 0 on error + new_created field +*/ + +Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, + Item ***copy_func, Field **from_field, bool group, bool modify_item) { switch (type) { @@ -3237,7 +3581,7 @@ Field *create_tmp_field(TABLE *table,Item *item, Item::Type type, item->name,table,item_sum->decimals); case INT_RESULT: return new Field_longlong(item_sum->max_length,maybe_null, - item->name,table); + item->name,table,item->unsigned_flag); case STRING_RESULT: if (item_sum->max_length > 255) return new Field_blob(item_sum->max_length,maybe_null, @@ -3246,7 +3590,7 @@ Field *create_tmp_field(TABLE *table,Item *item, Item::Type type, item->name,table,item->binary); } } - current_thd->fatal_error=1; + thd->fatal_error=1; return 0; // Error } case Item::FIELD_ITEM: @@ -3254,7 +3598,8 @@ Field *create_tmp_field(TABLE *table,Item *item, Item::Type type, Field *org_field=((Item_field*) item)->field,*new_field; *from_field=org_field; - if ((new_field= org_field->new_field(table))) // Should always be true + // The following should always be true + if ((new_field= org_field->new_field(&thd->mem_root,table))) { if (modify_item) ((Item_field*) item)->result_field= new_field; @@ -3265,12 +3610,12 @@ Field *create_tmp_field(TABLE *table,Item *item, Item::Type type, } return new_field; } - case Item::PROC_ITEM: case Item::FUNC_ITEM: case Item::COND_ITEM: case Item::FIELD_AVG_ITEM: case Item::FIELD_STD_ITEM: /* The following can only happen with 'CREATE TABLE ... SELECT' */ + case Item::PROC_ITEM: case Item::INT_ITEM: case Item::REAL_ITEM: case Item::STRING_ITEM: @@ -3288,7 +3633,7 @@ Field *create_tmp_field(TABLE *table,Item *item, Item::Type type, break; case INT_RESULT: new_field=new Field_longlong(item->max_length,maybe_null, - item->name,table); + item->name,table, item->unsigned_flag); break; case STRING_RESULT: if (item->max_length > 255) @@ -3299,10 +3644,10 @@ Field *create_tmp_field(TABLE *table,Item *item, Item::Type type, item->name,table,item->binary); break; } - if (copy_func) - *((*copy_func)++) = (Item_result_field*) item; // Save for copy_funcs + if (copy_func && item->is_result_field()) + *((*copy_func)++) = item; // Save for copy_funcs if (modify_item) - ((Item_result_field*) item)->result_field=new_field; + item->set_result_field(new_field); return new_field; } default: // Dosen't have to be stored @@ -3311,16 +3656,24 @@ Field *create_tmp_field(TABLE *table,Item *item, Item::Type type, } +/* + Create a temp table according to a field list. + Set distinct if duplicates could be removed + Given fields field pointers are changed to point at tmp_table + for send_fields +*/ + TABLE * create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, ORDER *group, bool distinct, bool save_sum_fields, - bool allow_distinct_limit, uint select_options) + bool allow_distinct_limit, ulong select_options) { TABLE *table; uint i,field_count,reclength,null_count,null_pack_length, hidden_null_count, hidden_null_pack_length, hidden_field_count, blob_count,group_null_items; bool using_unique_constraint=0; + bool not_all_columns= !(select_options & TMP_TABLE_ALL_COLUMNS); char *tmpname,path[FN_REFLEN]; byte *pos,*group_buff; uchar *null_flags; @@ -3328,7 +3681,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, Copy_field *copy=0; KEY *keyinfo; KEY_PART_INFO *key_part_info; - Item_result_field **copy_func; + Item **copy_func; MI_COLUMNDEF *recinfo; uint temp_pool_slot=MY_BIT_NONE; @@ -3343,7 +3696,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, temp_pool_slot = bitmap_set_next(&temp_pool); if (temp_pool_slot != MY_BIT_NONE) // we got a slot - sprintf(path, "%s%s_%lx_%i", mysql_tmpdir, tmp_file_prefix, + sprintf(path, "%s%s_%lx_%i", mysql_tmpdir, tmp_file_prefix, current_pid, temp_pool_slot); else // if we run out of slots or we are not using tempool sprintf(path,"%s%s%lx_%lx_%x",mysql_tmpdir,tmp_file_prefix,current_pid, @@ -3384,15 +3737,15 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, NullS)) { bitmap_clear_bit(&temp_pool, temp_pool_slot); - DBUG_RETURN(NULL); /* purecov: inspected */ + DBUG_RETURN(NULL); /* purecov: inspected */ } if (!(param->copy_field=copy=new Copy_field[field_count])) { bitmap_clear_bit(&temp_pool, temp_pool_slot); - my_free((gptr) table,MYF(0)); /* purecov: inspected */ - DBUG_RETURN(NULL); /* purecov: inspected */ + my_free((gptr) table,MYF(0)); /* purecov: inspected */ + DBUG_RETURN(NULL); /* purecov: inspected */ } - param->funcs=copy_func; + param->items_to_copy= copy_func; strmov(tmpname,path); /* make table according to fields */ @@ -3420,24 +3773,27 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, reclength=blob_count=null_count=hidden_null_count=group_null_items=0; param->using_indirect_summary_function=0; - List_iterator<Item> li(fields); + List_iterator_fast<Item> li(fields); Item *item; Field **tmp_from_field=from_field; while ((item=li++)) { Item::Type type=item->type(); - if (item->with_sum_func && type != Item::SUM_FUNC_ITEM) + if (not_all_columns) { - /* - Mark that the we have ignored an item that refers to a summary - function. We need to know this if someone is going to use - DISTINCT on the result. - */ - param->using_indirect_summary_function=1; - continue; + if (item->with_sum_func && type != Item::SUM_FUNC_ITEM) + { + /* + Mark that the we have ignored an item that refers to a summary + function. We need to know this if someone is going to use + DISTINCT on the result. + */ + param->using_indirect_summary_function=1; + continue; + } + if (item->const_item() && (int) hidden_field_count <= 0) + continue; // We don't have to store this } - if (item->const_item()) // We don't have to store this - continue; if (type == Item::SUM_FUNC_ITEM && !group && !save_sum_fields) { /* Can't calc group yet */ ((Item_sum*) item)->result_field=0; @@ -3447,8 +3803,8 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, if (!arg->const_item()) { Field *new_field= - create_tmp_field(table,arg,arg->type(),©_func,tmp_from_field, - group != 0,1); + create_tmp_field(thd, table,arg,arg->type(),©_func, + tmp_from_field, group != 0,not_all_columns); if (!new_field) goto err; // Should be OOM tmp_from_field++; @@ -3467,8 +3823,19 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, } else { - Field *new_field=create_tmp_field(table,item,type,©_func, - tmp_from_field, group != 0,1); + /* + The last parameter to create_tmp_field() is a bit tricky: + + We need to set it to 0 in union, to get fill_record() to modify the + temporary table. + We need to set it to 1 on multi-table-update and in select to + write rows to the temporary table. + We here distinguish between UNION and multi-table-updates by the fact + that in the later case group is set to the row pointer. + */ + Field *new_field=create_tmp_field(thd, table, item,type, ©_func, + tmp_from_field, group != 0, + not_all_columns || group !=0); if (!new_field) { if (thd->fatal_error) @@ -3496,11 +3863,12 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, if (!--hidden_field_count) hidden_null_count=null_count; } + DBUG_ASSERT(field_count >= (uint) (reg_field - table->field)); field_count= (uint) (reg_field - table->field); *blob_field= 0; // End marker /* If result table is small; use a heap */ - if (blob_count || using_unique_constraint || group_null_items || + if (blob_count || using_unique_constraint || (select_options & (OPTION_BIG_TABLES | SELECT_SMALL_RESULT)) == OPTION_BIG_TABLES) { @@ -3568,6 +3936,10 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, { if (field->flags & GROUP_FLAG && !using_unique_constraint) { + /* + We have to reserve one byte here for NULL bits, + as this is updated by 'end_update()' + */ *pos++=0; // Null is stored here recinfo->length=1; recinfo->type=FIELD_NORMAL; @@ -3602,23 +3974,24 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, (field->type() == FIELD_TYPE_STRING || field->type() == FIELD_TYPE_VAR_STRING) && length >= 10 && blob_count) - recinfo->type=FIELD_SKIPP_ENDSPACE; + recinfo->type=FIELD_SKIP_ENDSPACE; else recinfo->type=FIELD_NORMAL; if (!--hidden_field_count) null_count=(null_count+7) & ~7; // move to next byte } - param->copy_field_count=(uint) (copy - param->copy_field); + param->copy_field_end=copy; param->recinfo=recinfo; store_record(table,2); // Make empty default record - if (tmp_table_size == ~(ulong) 0) // No limit + if (thd->variables.tmp_table_size == ~(ulong) 0) // No limit table->max_rows= ~(ha_rows) 0; else table->max_rows=(((table->db_type == DB_TYPE_HEAP) ? - min(tmp_table_size, max_heap_table_size) : - tmp_table_size)/ table->reclength); + min(thd->variables.tmp_table_size, + thd->variables.max_heap_table_size) : + thd->variables.tmp_table_size)/ table->reclength); set_if_bigger(table->max_rows,1); // For dummy start options keyinfo=param->keyinfo; @@ -3651,7 +4024,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, if (!using_unique_constraint) { group->buff=(char*) group_buff; - if (!(group->field=field->new_field(table))) + if (!(group->field=field->new_field(&thd->mem_root,table))) goto err; /* purecov: inspected */ if (maybe_null) { @@ -3684,6 +4057,8 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, 'param->hidden_field_count' extra columns, whose null bits are stored in the first 'hidden_null_pack_length' bytes of the row. */ + DBUG_PRINT("info",("hidden_field_count: %d", param->hidden_field_count)); + null_pack_length-=hidden_null_pack_length; keyinfo->key_parts= ((field_count-param->hidden_field_count)+ test(null_pack_length)); @@ -3706,7 +4081,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, goto err; table->key_info=keyinfo; keyinfo->key_part=key_part_info; - keyinfo->flags=HA_NOSAME; + keyinfo->flags=HA_NOSAME | HA_NULL_ARE_EQUAL; keyinfo->key_length=(uint16) reclength; keyinfo->name=(char*) "tmp"; if (null_pack_length) @@ -3774,15 +4149,13 @@ static bool open_tmp_table(TABLE *table) table->db_stat=0; return(1); } - /* VOID(ha_lock(table,F_WRLCK)); */ /* Single thread table */ - (void) table->file->extra(HA_EXTRA_NO_READCHECK); /* Not needed */ (void) table->file->extra(HA_EXTRA_QUICK); /* Faster */ return(0); } static bool create_myisam_tmp_table(TABLE *table,TMP_TABLE_PARAM *param, - uint options) + ulong options) { int error; MI_KEYDEF keydef; @@ -3829,10 +4202,10 @@ static bool create_myisam_tmp_table(TABLE *table,TMP_TABLE_PARAM *param, for (uint i=0; i < keyinfo->key_parts ; i++,seg++) { Field *field=keyinfo->key_part[i].field; - seg->flag=0; - seg->language=MY_CHARSET_CURRENT; - seg->length=keyinfo->key_part[i].length; - seg->start=keyinfo->key_part[i].offset; + seg->flag= 0; + seg->language= MY_CHARSET_CURRENT; + seg->length= keyinfo->key_part[i].length; + seg->start= keyinfo->key_part[i].offset; if (field->flags & BLOB_FLAG) { seg->type= @@ -3950,12 +4323,18 @@ bool create_myisam_from_heap(TABLE *table, TMP_TABLE_PARAM *param, int error, thd->proc_info="converting HEAP to MyISAM"; if (create_myisam_tmp_table(&new_table,param, - thd->lex.options | thd->options)) + thd->lex.select_lex.options | thd->options)) goto err2; if (open_tmp_table(&new_table)) goto err1; table->file->index_end(); table->file->rnd_init(); + if (table->no_rows) + { + new_table.file->extra(HA_EXTRA_NO_ROWS); + new_table.no_rows=1; + } + /* copy all old rows */ while (!table->file->rnd_next(new_table.record[1])) { @@ -3996,9 +4375,12 @@ bool create_myisam_from_heap(TABLE *table, TMP_TABLE_PARAM *param, int error, } -/***************************************************************************** -** Make a join of all tables and write it on socket or to table -*****************************************************************************/ +/**************************************************************************** + Make a join of all tables and write it on socket or to table + Return: 0 if ok + 1 if error is sent + -1 if error should be sent +****************************************************************************/ static int do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure) @@ -4010,7 +4392,7 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure) join->procedure=procedure; /* - ** Tell the client how many fields there are in a row + Tell the client how many fields there are in a row */ if (!table) join->result->send_fields(*fields,1); @@ -4075,41 +4457,41 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure) if (error == -3) error=0; /* select_limit used */ } - if (!table) /* If sending data to client */ + + /* Return 1 if error is sent; -1 if error should be sent */ + if (error < 0) { - if (error < 0) - join->result->send_error(0,NullS); /* purecov: inspected */ - else + join->result->send_error(0,NullS); /* purecov: inspected */ + error=1; // Error sent + } + else + { + error=0; + if (!table) // If sending data to client { join_free(join); // Unlock all cursors if (join->result->send_eof()) - error= -1; + error= 1; // Don't send error } - } - else if (error < 0) - join->result->send_error(0,NullS); /* purecov: inspected */ - - if (error >= 0) - { DBUG_PRINT("info",("%ld records output",join->send_records)); } if (table) { - int old_error=error,tmp; + int tmp; if ((tmp=table->file->extra(HA_EXTRA_NO_CACHE))) { my_errno=tmp; error= -1; } - if (table->file->index_end()) + if ((tmp=table->file->index_end())) { my_errno=tmp; error= -1; } - if (error != old_error) + if (error == -1) table->file->print_error(my_errno,MYF(0)); } - DBUG_RETURN(error < 0); + DBUG_RETURN(error); } @@ -4178,6 +4560,11 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records) { if ((error=(*join_tab->next_select)(join,join_tab+1,0)) < 0) return error; + /* + Test if this was a SELECT DISTINCT query on a table that + was not in the field list; In this case we can abort if + we found a row, as no new rows can be added to the result. + */ if (not_used_in_distinct && found_records != join->found_records) return 0; } @@ -4185,10 +4572,8 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records) info->file->unlock_row(); } } while (!(error=info->read_record(info))); - if (error > 0) // Fatal error - return -1; } - else if (error > 0) + if (error > 0) // Fatal error return -1; if (!found && on_expr) @@ -4274,50 +4659,48 @@ flush_cached_records(JOIN *join,JOIN_TAB *join_tab,bool skipp_last) /***************************************************************************** -** The different ways to read a record -** Returns -1 if row was not found, 0 if row was found and 1 on errors + The different ways to read a record + Returns -1 if row was not found, 0 if row was found and 1 on errors *****************************************************************************/ - static int -join_read_const_tables(JOIN *join) +join_read_const_table(JOIN_TAB *tab, POSITION *pos) { - uint i; int error; - DBUG_ENTER("join_read_const_tables"); - for (i=0 ; i < join->const_tables ; i++) - { - TABLE *form=join->table[i]; - form->null_row=0; - form->status=STATUS_NO_RECORD; - - if (join->join_tab[i].type == JT_SYSTEM) - { - if ((error=join_read_system(join->join_tab+i))) - { // Info for DESCRIBE - join->join_tab[i].info="const row not found"; - join->best_positions[i].records_read=0.0; - if (!form->outer_join || error > 0) - DBUG_RETURN(error); - } - } - else - { - if ((error=join_read_const(join->join_tab+i))) - { - join->join_tab[i].info="unique row not found"; - join->best_positions[i].records_read=0.0; - if (!form->outer_join || error > 0) - DBUG_RETURN(error); - } + DBUG_ENTER("join_read_const_table"); + TABLE *table=tab->table; + table->const_table=1; + table->null_row=0; + table->status=STATUS_NO_RECORD; + + if (tab->type == JT_SYSTEM) + { + if ((error=join_read_system(tab))) + { // Info for DESCRIBE + tab->info="const row not found"; + /* Mark for EXPLAIN that the row was not found */ + pos->records_read=0.0; + if (!table->outer_join || error > 0) + DBUG_RETURN(error); } - if (join->join_tab[i].on_expr && !form->null_row) + } + else + { + if ((error=join_read_const(tab))) { - if ((form->null_row= test(join->join_tab[i].on_expr->val_int() == 0))) - empty_record(form); + tab->info="unique row not found"; + /* Mark for EXPLAIN that the row was not found */ + pos->records_read=0.0; + if (!table->outer_join || error > 0) + DBUG_RETURN(error); } - if (!form->null_row) - form->maybe_null=0; } + if (tab->on_expr && !table->null_row) + { + if ((table->null_row= test(tab->on_expr->val_int() == 0))) + empty_record(table); + } + if (!table->null_row) + table->maybe_null=0; DBUG_RETURN(0); } @@ -4329,7 +4712,8 @@ join_read_system(JOIN_TAB *tab) int error; if (table->status & STATUS_GARBAGE) // If first read { - if ((error=table->file->rnd_first(table->record[0]))) + if ((error=table->file->read_first_row(table->record[0], + table->primary_key))) { if (error != HA_ERR_END_OF_FILE) { @@ -4370,7 +4754,10 @@ join_read_const(JOIN_TAB *tab) empty_record(table); if (error != HA_ERR_KEY_NOT_FOUND) { - sql_print_error("read_const: Got error %d when reading table %s", + /* Locking reads can legally return also these errors, do not + print them to the .err log */ + if (error != HA_ERR_LOCK_DEADLOCK && error != HA_ERR_LOCK_WAIT_TIMEOUT) + sql_print_error("read_const: Got error %d when reading table %s", error, table->path); table->file->print_error(error,MYF(0)); return 1; @@ -4433,7 +4820,38 @@ join_read_always_key(JOIN_TAB *tab) { if (error != HA_ERR_KEY_NOT_FOUND) { - sql_print_error("read_const: Got error %d when reading table %s",error, + if (error != HA_ERR_LOCK_DEADLOCK && error != HA_ERR_LOCK_WAIT_TIMEOUT) + sql_print_error("read_const: Got error %d when reading table %s",error, + table->path); + table->file->print_error(error,MYF(0)); + return 1; + } + return -1; /* purecov: inspected */ + } + return 0; +} + +/* + This function is used when optimizing away ORDER BY in + SELECT * FROM t1 WHERE a=1 ORDER BY a DESC,b DESC +*/ + +static int +join_read_last_key(JOIN_TAB *tab) +{ + int error; + TABLE *table= tab->table; + + if (cp_buffer_from_ref(&tab->ref)) + return -1; + if ((error=table->file->index_read_last(table->record[0], + tab->ref.key_buff, + tab->ref.key_length))) + { + if (error != HA_ERR_KEY_NOT_FOUND) + { + if (error != HA_ERR_LOCK_DEADLOCK && error != HA_ERR_LOCK_WAIT_TIMEOUT) + sql_print_error("read_const: Got error %d when reading table %s",error, table->path); table->file->print_error(error,MYF(0)); return 1; @@ -4453,7 +4871,7 @@ join_no_more_records(READ_RECORD *info __attribute__((unused))) static int -join_read_next(READ_RECORD *info) +join_read_next_same(READ_RECORD *info) { int error; TABLE *table= info->table; @@ -4465,7 +4883,8 @@ join_read_next(READ_RECORD *info) { if (error != HA_ERR_END_OF_FILE) { - sql_print_error("read_next: Got error %d when reading table %s",error, + if (error != HA_ERR_LOCK_DEADLOCK && error != HA_ERR_LOCK_WAIT_TIMEOUT) + sql_print_error("read_next: Got error %d when reading table %s",error, table->path); table->file->print_error(error,MYF(0)); return 1; @@ -4476,6 +4895,38 @@ join_read_next(READ_RECORD *info) return 0; } +static int +join_read_prev_same(READ_RECORD *info) +{ + int error; + TABLE *table= info->table; + JOIN_TAB *tab=table->reginfo.join_tab; + + if ((error=table->file->index_prev(table->record[0]))) + { + if (error != HA_ERR_END_OF_FILE) + { + if (error != HA_ERR_LOCK_DEADLOCK && error != HA_ERR_LOCK_WAIT_TIMEOUT) + sql_print_error("read_next: Got error %d when reading table %s",error, + table->path); + table->file->print_error(error,MYF(0)); + error= 1; + } + else + { + table->status= STATUS_GARBAGE; + error= -1; + } + } + else if (key_cmp(table, tab->ref.key_buff, tab->ref.key, + tab->ref.key_length)) + { + table->status=STATUS_NOT_FOUND; + error= -1; + } + return error; +} + static int join_init_quick_read_record(JOIN_TAB *tab) @@ -4506,17 +4957,18 @@ join_init_read_record(JOIN_TAB *tab) } static int -join_init_read_first_with_key(JOIN_TAB *tab) +join_read_first(JOIN_TAB *tab) { int error; TABLE *table=tab->table; - if (!table->key_read && (table->used_keys & ((key_map) 1 << tab->index))) + if (!table->key_read && (table->used_keys & ((key_map) 1 << tab->index)) && + !table->no_keyread) { table->key_read=1; table->file->extra(HA_EXTRA_KEYREAD); } tab->table->status=0; - tab->read_record.read_record=join_init_read_next_with_key; + tab->read_record.read_record=join_read_next; tab->read_record.table=table; tab->read_record.file=table->file; tab->read_record.index=tab->index; @@ -4526,7 +4978,8 @@ join_init_read_first_with_key(JOIN_TAB *tab) { if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) { - sql_print_error("read_first_with_key: Got error %d when reading table", + if (error != HA_ERR_LOCK_DEADLOCK && error != HA_ERR_LOCK_WAIT_TIMEOUT) + sql_print_error("read_first_with_key: Got error %d when reading table", error); table->file->print_error(error,MYF(0)); return 1; @@ -4536,15 +4989,18 @@ join_init_read_first_with_key(JOIN_TAB *tab) return 0; } + static int -join_init_read_next_with_key(READ_RECORD *info) +join_read_next(READ_RECORD *info) { int error=info->file->index_next(info->record); if (error) { if (error != HA_ERR_END_OF_FILE) { - sql_print_error("read_next_with_key: Got error %d when reading table %s", + if (error != HA_ERR_LOCK_DEADLOCK && error != HA_ERR_LOCK_WAIT_TIMEOUT) + sql_print_error( + "read_next_with_key: Got error %d when reading table %s", error, info->table->path); info->file->print_error(error,MYF(0)); return 1; @@ -4554,19 +5010,19 @@ join_init_read_next_with_key(READ_RECORD *info) return 0; } - static int -join_init_read_last_with_key(JOIN_TAB *tab) +join_read_last(JOIN_TAB *tab) { TABLE *table=tab->table; int error; - if (!table->key_read && (table->used_keys & ((key_map) 1 << tab->index))) + if (!table->key_read && (table->used_keys & ((key_map) 1 << tab->index)) && + !table->no_keyread) { table->key_read=1; table->file->extra(HA_EXTRA_KEYREAD); } tab->table->status=0; - tab->read_record.read_record=join_init_read_prev_with_key; + tab->read_record.read_record=join_read_prev; tab->read_record.table=table; tab->read_record.file=table->file; tab->read_record.index=tab->index; @@ -4576,7 +5032,8 @@ join_init_read_last_with_key(JOIN_TAB *tab) { if (error != HA_ERR_END_OF_FILE) { - sql_print_error("read_last_with_key: Got error %d when reading table", + if (error != HA_ERR_LOCK_DEADLOCK && error != HA_ERR_LOCK_WAIT_TIMEOUT) + sql_print_error("read_last_with_key: Got error %d when reading table", error, table->path); table->file->print_error(error,MYF(0)); return 1; @@ -4586,15 +5043,18 @@ join_init_read_last_with_key(JOIN_TAB *tab) return 0; } + static int -join_init_read_prev_with_key(READ_RECORD *info) +join_read_prev(READ_RECORD *info) { int error=info->file->index_prev(info->record); if (error) { if (error != HA_ERR_END_OF_FILE) { - sql_print_error("read_prev_with_key: Got error %d when reading table: %s", + if (error != HA_ERR_LOCK_DEADLOCK && error != HA_ERR_LOCK_WAIT_TIMEOUT) + sql_print_error( + "read_prev_with_key: Got error %d when reading table: %s", error,info->table->path); info->file->print_error(error,MYF(0)); return 1; @@ -4604,13 +5064,14 @@ join_init_read_prev_with_key(READ_RECORD *info) return 0; } + static int join_ft_read_first(JOIN_TAB *tab) { int error; TABLE *table= tab->table; -#if 0 +#if NOT_USED_YET if (cp_buffer_from_ref(&tab->ref)) // as ft-key doesn't use store_key's return -1; // see also FT_SELECT::init() #endif @@ -4621,7 +5082,8 @@ join_ft_read_first(JOIN_TAB *tab) { if (error != HA_ERR_END_OF_FILE) { - sql_print_error("ft_read_first: Got error %d when reading table %s", + if (error != HA_ERR_LOCK_DEADLOCK && error != HA_ERR_LOCK_WAIT_TIMEOUT) + sql_print_error("ft_read_first: Got error %d when reading table %s", error, table->path); table->file->print_error(error,MYF(0)); return 1; @@ -4639,7 +5101,8 @@ join_ft_read_next(READ_RECORD *info) { if (error != HA_ERR_END_OF_FILE) { - sql_print_error("ft_read_next: Got error %d when reading table %s", + if (error != HA_ERR_LOCK_DEADLOCK && error != HA_ERR_LOCK_WAIT_TIMEOUT) + sql_print_error("ft_read_next: Got error %d when reading table %s", error, info->table->path); info->file->print_error(error,MYF(0)); return 1; @@ -4651,12 +5114,12 @@ join_ft_read_next(READ_RECORD *info) /***************************************************************************** -** The different end of select functions -** These functions returns < 0 when end is reached, 0 on ok and > 0 if a -** fatal error (like table corruption) was detected + The different end of select functions + These functions returns < 0 when end is reached, 0 on ok and > 0 if a + fatal error (like table corruption) was detected *****************************************************************************/ - /* ARGSUSED */ +/* ARGSUSED */ static int end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), bool end_of_records) @@ -4667,14 +5130,49 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), int error; if (join->having && join->having->val_int() == 0) DBUG_RETURN(0); // Didn't match having + error=0; if (join->procedure) error=join->procedure->send_row(*join->fields); - else + else if (join->do_send_rows) error=join->result->send_data(*join->fields); if (error) DBUG_RETURN(-1); /* purecov: inspected */ - if (++join->send_records >= join->thd->select_limit) + if (++join->send_records >= join->thd->select_limit && + join->do_send_rows) + { + if (join->select_options & OPTION_FOUND_ROWS) + { + JOIN_TAB *jt=join->join_tab; + if ((join->tables == 1) && !join->tmp_table && !join->sort_and_group + && !join->send_group_parts && !join->having && !jt->select_cond && + !(jt->select && jt->select->quick) && + !(jt->table->file->table_flags() & HA_NOT_EXACT_COUNT)) + { + /* Join over all rows in table; Return number of found rows */ + TABLE *table=jt->table; + + join->select_options ^= OPTION_FOUND_ROWS; + if (table->record_pointers || + (table->io_cache && my_b_inited(table->io_cache))) + { + /* Using filesort */ + join->send_records= table->found_records; + } + else + { + table->file->info(HA_STATUS_VARIABLE); + join->send_records = table->file->records; + } + } + else + { + join->do_send_rows=0; + join->thd->select_limit = HA_POS_ERROR; + DBUG_RETURN(0); + } + } DBUG_RETURN(-3); // Abort nicely + } } else { @@ -4702,12 +5200,12 @@ end_send_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), join->procedure->end_group(); if (idx < (int) join->send_group_parts) { - int error; + int error=0; if (join->procedure) { if (join->having && join->having->val_int() == 0) error= -1; // Didn't satisfy having - else + else if (join->do_send_rows) error=join->procedure->send_row(*join->fields) ? 1 : 0; if (end_of_records && join->procedure->end_of_records()) error= 1; // Fatal error @@ -4722,15 +5220,25 @@ end_send_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), } if (join->having && join->having->val_int() == 0) error= -1; // Didn't satisfy having - else + else if (join->do_send_rows) error=join->result->send_data(*join->fields) ? 1 : 0; } if (error > 0) DBUG_RETURN(-1); /* purecov: inspected */ if (end_of_records) + { + join->send_records++; DBUG_RETURN(0); - if (!error && ++join->send_records >= join->thd->select_limit) - DBUG_RETURN(-3); /* Abort nicely */ + } + if (!error && + ++join->send_records >= join->thd->select_limit && + join->do_send_rows) + { + if (!(join->select_options & OPTION_FOUND_ROWS)) + DBUG_RETURN(-3); // Abort nicely + join->do_send_rows=0; + join->thd->select_limit = HA_POS_ERROR; + } } } else @@ -4774,8 +5282,9 @@ end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), if (!end_of_records) { copy_fields(&join->tmp_table_param); - copy_funcs(join->tmp_table_param.funcs); + copy_funcs(join->tmp_table_param.items_to_copy); +#ifdef TO_BE_DELETED if (!table->uniques) // If not unique handling { /* Copy null values from group to row */ @@ -4786,10 +5295,11 @@ end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), if (item->maybe_null) { Field *field=item->tmp_table_field(); - field->ptr[-1]= (byte) (field->is_null() ? 0 : 1); + field->ptr[-1]= (byte) (field->is_null() ? 1 : 0); } } } +#endif if (!join->having || join->having->val_int()) { join->found_records++; @@ -4799,10 +5309,17 @@ end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), error == HA_ERR_FOUND_DUPP_UNIQUE) goto end; if (create_myisam_from_heap(table, &join->tmp_table_param, error,1)) - DBUG_RETURN(1); // Not a table_is_full error + DBUG_RETURN(-1); // Not a table_is_full error table->uniques=0; // To ensure rows are the same - if (++join->send_records >= join->tmp_table_param.end_write_records) + } + if (++join->send_records >= join->tmp_table_param.end_write_records && + join->do_send_rows) + { + if (!(join->select_options & OPTION_FOUND_ROWS)) DBUG_RETURN(-3); + join->do_send_rows=0; + join->thd->select_limit = HA_POS_ERROR; + DBUG_RETURN(0); } } } @@ -4865,7 +5382,7 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), memcpy(table->record[0]+key_part->offset, group->buff, key_part->length); init_tmptable_sum_functions(join->sum_funcs); - copy_funcs(join->tmp_table_param.funcs); + copy_funcs(join->tmp_table_param.items_to_copy); if ((error=table->file->write_row(table->record[0]))) { if (create_myisam_from_heap(table, &join->tmp_table_param, error, 0)) @@ -4898,7 +5415,7 @@ end_unique_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), init_tmptable_sum_functions(join->sum_funcs); copy_fields(&join->tmp_table_param); // Groups are copied twice. - copy_funcs(join->tmp_table_param.funcs); + copy_funcs(join->tmp_table_param.items_to_copy); if (!(error=table->file->write_row(table->record[0]))) join->send_records++; // New group @@ -4964,7 +5481,7 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), { if (create_myisam_from_heap(table, &join->tmp_table_param, error, 0)) - DBUG_RETURN(1); // Not a table_is_full error + DBUG_RETURN(-1); // Not a table_is_full error } else join->send_records++; @@ -4983,7 +5500,7 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), if (idx < (int) join->send_group_parts) { copy_fields(&join->tmp_table_param); - copy_funcs(join->tmp_table_param.funcs); + copy_funcs(join->tmp_table_param.items_to_copy); init_sum_functions(join->sum_funcs); if (join->procedure) join->procedure->add(); @@ -4999,11 +5516,11 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), /***************************************************************************** -** Remove calculation with tables that aren't yet read. Remove also tests -** against fields that are read through key where the table is not a -** outer join table. -** We can't remove tests that are made against columns which are stored -** in sorted order. + Remove calculation with tables that aren't yet read. Remove also tests + against fields that are read through key where the table is not a + outer join table. + We can't remove tests that are made against columns which are stored + in sorted order. *****************************************************************************/ /* Return 1 if right_item is used removable reference key on left_item */ @@ -5019,11 +5536,12 @@ static bool test_if_ref(Item_field *left_item,Item *right_item) { if (right_item->type() == Item::FIELD_ITEM) return (field->eq_def(((Item_field *) right_item)->field)); - if (right_item->const_item() && - (right_item->val_int() || !right_item->null_value)) + if (right_item->const_item() && !(right_item->is_null())) { - // We can remove binary fields and numerical fields except float, - // as float comparison isn't 100 % secure + /* + We can remove binary fields and numerical fields except float, + as float comparison isn't 100 % secure + */ if (field->binary() && (field->type() != FIELD_TYPE_FLOAT || field->decimals() == 0)) { @@ -5045,6 +5563,7 @@ make_cond_for_table(COND *cond,table_map tables,table_map used_table) { if (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC) { + /* Create new top level AND item */ Item_cond_and *new_cond=new Item_cond_and; if (!new_cond) return (COND*) 0; // OOM /* purecov: inspected */ @@ -5082,14 +5601,15 @@ make_cond_for_table(COND *cond,table_map tables,table_map used_table) new_cond->argument_list()->push_back(fix); } new_cond->used_tables_cache=((Item_cond_or*) cond)->used_tables_cache; + new_cond->top_level_item(); return new_cond; } } /* - ** Because the following test takes a while and it can be done - ** table_count times, we mark each item that we have examined with the result - ** of the test + Because the following test takes a while and it can be done + table_count times, we mark each item that we have examined with the result + of the test */ if (cond->marker == 3 || (cond->used_tables() & ~tables)) @@ -5137,13 +5657,15 @@ part_of_refkey(TABLE *table,Field *field) /***************************************************************************** -** Test if one can use the key to resolve ORDER BY -** Returns: 1 if key is ok. -** 0 if key can't be used -** -1 if reverse key can be used + Test if one can use the key to resolve ORDER BY + Returns: 1 if key is ok. + 0 if key can't be used + -1 if reverse key can be used + used_key_parts is set to key parts used if length != 0 *****************************************************************************/ -static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx) +static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx, + uint *used_key_parts) { KEY_PART_INFO *key_part,*key_part_end; key_part=table->key_info[idx].key_part; @@ -5175,6 +5697,7 @@ static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx) reverse=flag; // Remember if reverse key_part++; } + *used_key_parts= (uint) (key_part - table->key_info[idx].key_part); return reverse; } @@ -5197,17 +5720,20 @@ static uint find_shortest_key(TABLE *table, key_map usable_keys) } -/***************************************************************************** -** If not selecting by given key, create a index how records should be read -** return: 0 ok -** -1 some fatal error -** 1 no records -*****************************************************************************/ +/* + Test if we can skip the ORDER BY by using an index. + + If we can use an index, the JOIN_TAB / tab->select struct + is changed to use the index. -/* Return 1 if we don't have to do file sorting */ + Return: + 0 We have to use filesort to do the sorting + 1 We can use an index. +*/ static bool -test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit) +test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit, + bool no_changes) { int ref_key; TABLE *table=tab->table; @@ -5235,10 +5761,55 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit) if (ref_key >= 0) { + /* + We come here when there is a REF key. + */ + int order_direction; + uint used_key_parts; /* Check if we get the rows in requested sorted order by using the key */ if ((usable_keys & ((key_map) 1 << ref_key)) && - test_if_order_by_key(order,table,ref_key) == 1) + (order_direction = test_if_order_by_key(order,table,ref_key, + &used_key_parts))) + { + if (order_direction == -1) // If ORDER BY ... DESC + { + if (select && select->quick) + { + /* + Don't reverse the sort order, if it's already done. + (In some cases test_if_order_by_key() can be called multiple times + */ + if (!select->quick->reverse_sorted()) + { + // ORDER BY range_key DESC + QUICK_SELECT_DESC *tmp=new QUICK_SELECT_DESC(select->quick, + used_key_parts); + if (!tmp || tmp->error) + { + delete tmp; + DBUG_RETURN(0); // Reverse sort not supported + } + select->quick=tmp; + } + DBUG_RETURN(1); + } + if (tab->ref.key_parts < used_key_parts) + { + /* + SELECT * FROM t1 WHERE a=1 ORDER BY a DESC,b DESC + + Use a traversal function that starts by reading the last row + with key part (A) and then traverse the index backwards. + */ + if (table->file->index_flags(ref_key) & HA_NOT_READ_PREFIX_LAST) + DBUG_RETURN(0); // Use filesort + tab->read_first_record= join_read_last_key; + tab->read_record.read_record= join_read_prev_same; + /* fall through */ + } + } DBUG_RETURN(1); /* No need to sort */ + } } else { @@ -5257,20 +5828,24 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit) for (nr=0; keys ; keys>>=1, nr++) { + uint not_used; if (keys & 1) { int flag; - if ((flag=test_if_order_by_key(order,table,nr))) + if ((flag=test_if_order_by_key(order, table, nr, ¬_used))) { - tab->index=nr; - tab->read_first_record= (flag > 0 ? join_init_read_first_with_key: - join_init_read_last_with_key); - table->file->index_init(nr); - tab->type=JT_NEXT; // Read with index_first(), index_next() - if (table->used_keys & ((key_map) 1 << nr)) + if (!no_changes) { - table->key_read=1; - table->file->extra(HA_EXTRA_KEYREAD); + tab->index=nr; + tab->read_first_record= (flag > 0 ? join_read_first: + join_read_last); + table->file->index_init(nr); + tab->type=JT_NEXT; // Read with index_first(), index_next() + if (table->used_keys & ((key_map) 1 << nr)) + { + table->key_read=1; + table->file->extra(HA_EXTRA_KEYREAD); + } } DBUG_RETURN(1); } @@ -5280,8 +5855,17 @@ test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit) DBUG_RETURN(0); // Can't use index. } + +/***************************************************************************** + If not selecting by given key, create an index how records should be read + return: 0 ok + -1 some fatal error + 1 no records +*****************************************************************************/ + static int -create_sort_index(JOIN_TAB *tab,ORDER *order,ha_rows select_limit) +create_sort_index(JOIN_TAB *tab, ORDER *order, ha_rows filesort_limit, + ha_rows select_limit) { SORT_FIELD *sortorder; uint length; @@ -5290,7 +5874,7 @@ create_sort_index(JOIN_TAB *tab,ORDER *order,ha_rows select_limit) SQL_SELECT *select=tab->select; DBUG_ENTER("create_sort_index"); - if (test_if_skip_sort_order(tab,order,select_limit)) + if (test_if_skip_sort_order(tab,order,select_limit,0)) DBUG_RETURN(0); if (!(sortorder=make_unireg_sortorder(order,&length))) goto err; /* purecov: inspected */ @@ -5323,8 +5907,11 @@ create_sort_index(JOIN_TAB *tab,ORDER *order,ha_rows select_limit) goto err; } } - table->found_records=filesort(&table,sortorder,length, - select, 0L, select_limit, &examined_rows); + if (table->tmp_table) + table->file->info(HA_STATUS_VARIABLE); // Get record count + table->found_records=filesort(table,sortorder,length, + select, 0L, filesort_limit, &examined_rows); + tab->records=table->found_records; // For SQL_CALC_ROWS delete select; // filesort did select tab->select=0; tab->select_cond=0; @@ -5341,11 +5928,11 @@ err: DBUG_RETURN(-1); } - /* -** Add the HAVING criteria to table->select + Add the HAVING criteria to table->select */ +#ifdef NOT_YET static bool fix_having(JOIN *join, Item **having) { (*having)->update_used_tables(); // Some tables may have been const @@ -5366,6 +5953,7 @@ static bool fix_having(JOIN *join, Item **having) sort_table_cond))) return 1; table->select_cond=table->select->cond; + table->select_cond->top_level_item(); DBUG_EXECUTE("where",print_where(table->select_cond, "select and having");); *having=make_cond_for_table(*having,~ (table_map) 0,~used_tables); @@ -5373,14 +5961,15 @@ static bool fix_having(JOIN *join, Item **having) } return 0; } +#endif /***************************************************************************** -** Remove duplicates from tmp table -** This should be recoded to add a uniuqe index to the table and remove -** dupplicates -** Table is a locked single thread table -** fields is the number of fields to check (from the end) + Remove duplicates from tmp table + This should be recoded to add a unique index to the table and remove + duplicates + Table is a locked single thread table + fields is the number of fields to check (from the end) *****************************************************************************/ static bool compare_record(TABLE *table, Field **ptr) @@ -5420,10 +6009,10 @@ remove_duplicates(JOIN *join, TABLE *entry,List<Item> &fields, Item *having) int error; ulong reclength,offset; uint field_count; + THD *thd= current_thd; DBUG_ENTER("remove_duplicates"); entry->reginfo.lock_type=TL_WRITE; - entry->file->extra(HA_EXTRA_NO_READCHECK); /* Calculate how many saved fields there is in list */ field_count=0; @@ -5437,7 +6026,7 @@ remove_duplicates(JOIN *join, TABLE *entry,List<Item> &fields, Item *having) if (!field_count) { // only const items - join->thd->select_limit=1; // Only send first row + join->thd->select_limit=1; // Only send first row DBUG_RETURN(0); } Field **first_field=entry->field+entry->fields - field_count; @@ -5449,7 +6038,7 @@ remove_duplicates(JOIN *join, TABLE *entry,List<Item> &fields, Item *having) if (entry->db_type == DB_TYPE_HEAP || (!entry->blob_fields && ((ALIGN_SIZE(reclength) +sizeof(HASH_LINK)) * entry->file->records < - sortbuff_size))) + thd->variables.sortbuff_size))) error=remove_dup_with_hash_index(join->thd, entry, field_count, first_field, reclength, having); @@ -5609,7 +6198,7 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table, if ((error=file->delete_row(record))) goto err; continue; - } + } /* copy fields to key buffer */ field_length=field_lengths; @@ -5770,7 +6359,7 @@ join_init_cache(THD *thd,JOIN_TAB *tables,uint table_count) cache->length=length+blobs*sizeof(char*); cache->blobs=blobs; *blob_ptr=0; /* End sequentel */ - size=max(join_buff_size,cache->length); + size=max(thd->variables.join_buff_size, cache->length); if (!(cache->buff=(uchar*) my_malloc(size,MYF(0)))) DBUG_RETURN(1); /* Don't use cache */ /* purecov: inspected */ cache->end=cache->buff+size; @@ -5811,7 +6400,7 @@ store_record_in_cache(JOIN_CACHE *cache) cache->ptr_record=cache->records; /* - ** There is room in cache. Put record there + There is room in cache. Put record there */ cache->records++; for (copy=cache->field ; copy < end_field; copy++) @@ -5937,13 +6526,13 @@ cp_buffer_from_ref(TABLE_REF *ref) /***************************************************************************** -** Group and order functions + Group and order functions *****************************************************************************/ /* -** Find order/group item in requested columns and change the item to point at -** it. If item doesn't exists, add it first in the field list -** Return 0 if ok. + Find order/group item in requested columns and change the item to point at + it. If item doesn't exists, add it first in the field list + Return 0 if ok. */ static int @@ -5988,8 +6577,8 @@ find_order_in_list(THD *thd,TABLE_LIST *tables,ORDER *order,List<Item> &fields, /* -** Change order to point at item in select list. If item isn't a number -** and doesn't exits in the select list, add it the the field list. + Change order to point at item in select list. If item isn't a number + and doesn't exits in the select list, add it the the field list. */ int setup_order(THD *thd,TABLE_LIST *tables,List<Item> &fields, @@ -6023,7 +6612,7 @@ setup_group(THD *thd,TABLE_LIST *tables,List<Item> &fields, uint org_fields=all_fields.elements; thd->where="group statement"; - for ( ; order; order=order->next) + for (; order; order=order->next) { if (find_order_in_list(thd,tables,order,fields,all_fields)) return 1; @@ -6058,7 +6647,7 @@ setup_group(THD *thd,TABLE_LIST *tables,List<Item> &fields, } /* -** Add fields with aren't used at start of field list. Return FALSE if ok + Add fields with aren't used at start of field list. Return FALSE if ok */ static bool @@ -6070,7 +6659,7 @@ setup_new_fields(THD *thd,TABLE_LIST *tables,List<Item> &fields, thd->set_query_id=1; // Not really needed, but... thd->where=0; // Don't give error - for ( ; new_field ; new_field=new_field->next) + for (; new_field ; new_field=new_field->next) { if ((item=find_item_in_list(*new_field->item,fields))) new_field->item=item; /* Change to shared Item */ @@ -6088,18 +6677,20 @@ setup_new_fields(THD *thd,TABLE_LIST *tables,List<Item> &fields, } /* -** Create a group by that consist of all non const fields. Try to use -** the fields in the order given by 'order' to allow one to optimize -** away 'order by'. + Create a group by that consist of all non const fields. Try to use + the fields in the order given by 'order' to allow one to optimize + away 'order by'. */ static ORDER * -create_distinct_group(ORDER *order_list,List<Item> &fields) +create_distinct_group(THD *thd, ORDER *order_list, List<Item> &fields, + bool *all_order_by_fields_used) { List_iterator<Item> li(fields); Item *item; ORDER *order,*group,**prev; + *all_order_by_fields_used= 1; while ((item=li++)) item->marker=0; /* Marker that field is not used */ @@ -6108,13 +6699,15 @@ create_distinct_group(ORDER *order_list,List<Item> &fields) { if (order->in_field_list) { - ORDER *ord=(ORDER*) sql_memdup(order,sizeof(ORDER)); + ORDER *ord=(ORDER*) thd->memdup((char*) order,sizeof(ORDER)); if (!ord) return 0; *prev=ord; prev= &ord->next; (*ord->item)->marker=1; } + else + *all_order_by_fields_used= 0; } li.rewind(); @@ -6124,7 +6717,7 @@ create_distinct_group(ORDER *order_list,List<Item> &fields) continue; if (!item->marker) { - ORDER *ord=(ORDER*) sql_calloc(sizeof(ORDER)); + ORDER *ord=(ORDER*) thd->calloc(sizeof(ORDER)); if (!ord) return 0; ord->item=li.ref(); @@ -6139,7 +6732,7 @@ create_distinct_group(ORDER *order_list,List<Item> &fields) /***************************************************************************** -** Update join with count of the different type of fields + Update join with count of the different type of fields *****************************************************************************/ void @@ -6149,7 +6742,7 @@ count_field_types(TMP_TABLE_PARAM *param, List<Item> &fields, List_iterator<Item> li(fields); Item *field; - param->field_count=param->sum_func_count=param->func_count= + param->field_count=param->sum_func_count=param->func_count= param->hidden_field_count=0; param->quick_group=1; while ((field=li++)) @@ -6230,7 +6823,7 @@ get_sort_by_table(ORDER *a,ORDER *b,TABLE_LIST *tables) if (!map || (map & RAND_TABLE_BIT)) DBUG_RETURN(0); - for ( ; !(map & tables->table->map) ; tables=tables->next) ; + for (; !(map & tables->table->map) ; tables=tables->next) ; if (map != tables->table->map) DBUG_RETURN(0); // More than one table DBUG_PRINT("exit",("sort by table: %d",tables->table->tablenr)); @@ -6243,7 +6836,8 @@ get_sort_by_table(ORDER *a,ORDER *b,TABLE_LIST *tables) static void calc_group_buffer(JOIN *join,ORDER *group) { - uint key_length=0,parts=0; + uint key_length=0, parts=0, null_parts=0; + if (group) join->group= 1; for (; group ; group=group->next) @@ -6264,16 +6858,17 @@ calc_group_buffer(JOIN *join,ORDER *group) key_length+=(*group->item)->max_length; parts++; if ((*group->item)->maybe_null) - key_length++; + null_parts++; } - join->tmp_table_param.group_length=key_length; + join->tmp_table_param.group_length=key_length+null_parts; join->tmp_table_param.group_parts=parts; + join->tmp_table_param.group_null_parts=null_parts; } /* -** Get a list of buffers for saveing last group -** Groups are saved in reverse order for easyer check loop + Get a list of buffers for saveing last group + Groups are saved in reverse order for easyer check loop */ static bool @@ -6311,14 +6906,14 @@ test_if_group_changed(List<Item_buff> &list) /* -** Setup copy_fields to save fields at start of new group -** Only FIELD_ITEM:s and FUNC_ITEM:s needs to be saved between groups. -** Change old item_field to use a new field with points at saved fieldvalue -** This function is only called before use of send_fields + Setup copy_fields to save fields at start of new group + Only FIELD_ITEM:s and FUNC_ITEM:s needs to be saved between groups. + Change old item_field to use a new field with points at saved fieldvalue + This function is only called before use of send_fields */ bool -setup_copy_fields(TMP_TABLE_PARAM *param,List<Item> &fields) +setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields) { Item *pos; List_iterator<Item> li(fields); @@ -6326,7 +6921,7 @@ setup_copy_fields(TMP_TABLE_PARAM *param,List<Item> &fields) DBUG_ENTER("setup_copy_fields"); if (!(copy=param->copy_field= new Copy_field[param->field_count])) - goto err; + goto err2; param->copy_funcs.empty(); while ((pos=li++)) @@ -6346,7 +6941,7 @@ setup_copy_fields(TMP_TABLE_PARAM *param,List<Item> &fields) /* set up save buffer and change result_field to point at saved value */ Field *field= item->field; - item->result_field=field->new_field(field->table); + item->result_field=field->new_field(&thd->mem_root,field->table); char *tmp=(char*) sql_alloc(field->pack_length()+1); if (!tmp) goto err; @@ -6371,40 +6966,40 @@ setup_copy_fields(TMP_TABLE_PARAM *param,List<Item> &fields) goto err; } } - param->copy_field_count= (uint) (copy - param->copy_field); + param->copy_field_end= copy; DBUG_RETURN(0); err: - delete [] param->copy_field; + delete [] param->copy_field; // This is never 0 param->copy_field=0; +err2: DBUG_RETURN(TRUE); } /* -** Copy fields and null values between two tables + Copy fields and null values between two tables */ void copy_fields(TMP_TABLE_PARAM *param) { Copy_field *ptr=param->copy_field; - Copy_field *end=ptr+param->copy_field_count; + Copy_field *end=param->copy_field_end; - for ( ; ptr != end; ptr++) + for (; ptr != end; ptr++) (*ptr->do_copy)(ptr); - List_iterator<Item> it(param->copy_funcs); + List_iterator_fast<Item> &it=param->copy_funcs_it; + it.rewind(); Item_copy_string *item; while ((item = (Item_copy_string*) it++)) - { item->copy(); - } } /***************************************************************************** -** Make an array of pointer to sum_functions to speed up sum_func calculation + Make an array of pointer to sum_functions to speed up sum_func calculation *****************************************************************************/ static bool @@ -6436,7 +7031,7 @@ make_sum_func_list(JOIN *join,List<Item> &fields) /* -** Change all funcs and sum_funcs to fields in tmp table + Change all funcs and sum_funcs to fields in tmp table */ static bool @@ -6486,8 +7081,8 @@ change_to_use_tmp_fields(List<Item> &items) /* -** Change all sum_func refs to fields to point at fields in tmp table -** Change all funcs to be fields in tmp table + Change all sum_func refs to fields to point at fields in tmp table + Change all funcs to be fields in tmp table */ static bool @@ -6543,7 +7138,7 @@ change_refs_to_tmp_fields(THD *thd,List<Item> &items) /****************************************************************************** -** code for calculating functions + Code for calculating functions ******************************************************************************/ static void @@ -6574,7 +7169,7 @@ copy_sum_funcs(Item_sum **func_ptr) { Item_sum *func; for (; (func = *func_ptr) ; func_ptr++) - (void) func->save_in_field(func->result_field); + (void) func->save_in_result_field(1); return; } @@ -6601,18 +7196,17 @@ update_sum_func(Item_sum **func_ptr) /* Copy result of functions to record in tmp_table */ void -copy_funcs(Item_result_field **func_ptr) +copy_funcs(Item **func_ptr) { - Item_result_field *func; + Item *func; for (; (func = *func_ptr) ; func_ptr++) - (void) func->save_in_field(func->result_field); - return; + func->save_in_result_field(1); } /***************************************************************************** -** Create a condition for a const reference and add this to the -** currenct select for the table + Create a condition for a const reference and add this to the + currenct select for the table *****************************************************************************/ static bool add_ref_to_table_cond(THD *thd, JOIN_TAB *join_tab) @@ -6653,178 +7247,200 @@ static bool add_ref_to_table_cond(THD *thd, JOIN_TAB *join_tab) } /**************************************************************************** -** Send a description about what how the select will be done to stdout + Send a description about what how the select will be done to stdout ****************************************************************************/ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, - bool distinct) + bool distinct,const char *message) { List<Item> field_list; Item *item; + List<Item> item_list; THD *thd=join->thd; + MYSQL_LOCK *save_lock; + SELECT_LEX *select_lex = &(join->thd->lex.select_lex); + select_result *result=join->result; + Item *item_null= new Item_null(); DBUG_ENTER("select_describe"); /* Don't log this into the slow query log */ - join->thd->lex.options&= ~(QUERY_NO_INDEX_USED | QUERY_NO_GOOD_INDEX_USED); - field_list.push_back(new Item_empty_string("table",NAME_LEN)); - field_list.push_back(new Item_empty_string("type",10)); - field_list.push_back(item=new Item_empty_string("possible_keys", + select_lex->options&= ~(QUERY_NO_INDEX_USED | QUERY_NO_GOOD_INDEX_USED); + thd->offset_limit=0; + if (thd->lex.select == select_lex) + { + field_list.push_back(new Item_empty_string("table",NAME_LEN)); + field_list.push_back(new Item_empty_string("type",10)); + field_list.push_back(item=new Item_empty_string("possible_keys", NAME_LEN*MAX_KEY)); - item->maybe_null=1; - field_list.push_back(item=new Item_empty_string("key",NAME_LEN)); - item->maybe_null=1; - field_list.push_back(item=new Item_int("key_len",0,3)); - item->maybe_null=1; - field_list.push_back(item=new Item_empty_string("ref", - NAME_LEN*MAX_REF_PARTS)); - item->maybe_null=1; - field_list.push_back(new Item_real("rows",0.0,0,10)); - field_list.push_back(new Item_empty_string("Extra",255)); - if (send_fields(thd,field_list,1)) - return; /* purecov: inspected */ + item->maybe_null=1; + field_list.push_back(item=new Item_empty_string("key",NAME_LEN)); + item->maybe_null=1; + field_list.push_back(item=new Item_int("key_len",0,3)); + item->maybe_null=1; + field_list.push_back(item=new Item_empty_string("ref", + NAME_LEN*MAX_REF_PARTS)); + item->maybe_null=1; + field_list.push_back(new Item_real("rows",0.0,0,10)); + field_list.push_back(new Item_empty_string("Extra",255)); + if (result->send_fields(field_list,1)) + return; + } - char buff[512],*buff_ptr; - String tmp(buff,sizeof(buff)),*packet= &thd->packet; - table_map used_tables=0; - for (uint i=0 ; i < join->tables ; i++) + if (message) { - JOIN_TAB *tab=join->join_tab+i; - TABLE *table=tab->table; - - if (tab->type == JT_ALL && tab->select && tab->select->quick) - tab->type= JT_RANGE; - packet->length(0); - net_store_data(packet,table->table_name); - net_store_data(packet,join_type_str[tab->type]); - tmp.length(0); - key_map bits; - uint j; - for (j=0,bits=tab->keys ; bits ; j++,bits>>=1) - { - if (bits & 1) - { - if (tmp.length()) - tmp.append(','); - tmp.append(table->key_info[j].name); - } - } - if (tmp.length()) - net_store_data(packet,tmp.ptr(),tmp.length()); - else - net_store_null(packet); - if (tab->ref.key_parts) - { - net_store_data(packet,table->key_info[tab->ref.key].name); - net_store_data(packet,(uint32) tab->ref.key_length); - tmp.length(0); - for (store_key **ref=tab->ref.key_copy ; *ref ; ref++) - { - if (tmp.length()) - tmp.append(','); - tmp.append((*ref)->name()); - } - net_store_data(packet,tmp.ptr(),tmp.length()); - } - else if (tab->type == JT_NEXT) - { - net_store_data(packet,table->key_info[tab->index].name); - net_store_data(packet,(uint32) table->key_info[tab->index].key_length); - net_store_null(packet); - } - else if (tab->select && tab->select->quick) - { - net_store_data(packet,table->key_info[tab->select->quick->index].name);; - net_store_data(packet,(uint32) tab->select->quick->max_used_key_length); - net_store_null(packet); - } - else - { - net_store_null(packet); - net_store_null(packet); - net_store_null(packet); - } - sprintf(buff,"%.0f",join->best_positions[i].records_read); - net_store_data(packet,buff); - my_bool key_read=table->key_read; - if (tab->type == JT_NEXT && - ((table->used_keys & ((key_map) 1 << tab->index)))) - key_read=1; - - buff_ptr=buff; - if (tab->info) - net_store_data(packet,tab->info); - else if (tab->select) + Item *empty= new Item_empty_string("",0); + for (uint i=0 ; i < 7; i++) + item_list.push_back(empty); + item_list.push_back(new Item_string(message,strlen(message))); + if (result->send_data(item_list)) + result->send_error(0,NullS); + } + else + { + table_map used_tables=0; + for (uint i=0 ; i < join->tables ; i++) { - if (tab->use_quick == 2) + JOIN_TAB *tab=join->join_tab+i; + TABLE *table=tab->table; + char buff[512],*buff_ptr=buff; + char buff1[512], buff2[512], buff3[512]; + String tmp1(buff1,sizeof(buff1)); + String tmp2(buff2,sizeof(buff2)); + tmp1.length(0); + tmp2.length(0); + item_list.empty(); + + if (tab->type == JT_ALL && tab->select && tab->select->quick) + tab->type= JT_RANGE; + item_list.push_back(new Item_string(table->table_name, + strlen(table->table_name))); + item_list.push_back(new Item_string(join_type_str[tab->type], + strlen(join_type_str[tab->type]))); + key_map bits; + uint j; + for (j=0,bits=tab->keys ; bits ; j++,bits>>=1) { - sprintf(buff_ptr,"range checked for each record (index map: %u)", - tab->keys); - buff_ptr=strend(buff_ptr); + if (bits & 1) + { + if (tmp1.length()) + tmp1.append(','); + tmp1.append(table->key_info[j].name); + } } + if (tmp1.length()) + item_list.push_back(new Item_string(tmp1.ptr(),tmp1.length())); else - buff_ptr=strmov(buff_ptr,"where used"); - } - if (key_read) - { - if (buff != buff_ptr) + item_list.push_back(item_null); + if (tab->ref.key_parts) { - buff_ptr[0]=';' ; buff_ptr[1]=' '; buff_ptr+=2; + KEY *key_info=table->key_info+ tab->ref.key; + item_list.push_back(new Item_string(key_info->name, + strlen(key_info->name))); + item_list.push_back(new Item_int((int32) tab->ref.key_length)); + for (store_key **ref=tab->ref.key_copy ; *ref ; ref++) + { + if (tmp2.length()) + tmp2.append(','); + tmp2.append((*ref)->name()); + } + item_list.push_back(new Item_string(tmp2.ptr(),tmp2.length())); } - buff_ptr=strmov(buff_ptr,"Using index"); - } - if (table->reginfo.not_exists_optimize) - { - if (buff != buff_ptr) + else if (tab->type == JT_NEXT) { - buff_ptr[0]=';' ; buff_ptr[1]=' '; buff_ptr+=2; + KEY *key_info=table->key_info+ tab->index; + item_list.push_back(new Item_string(key_info->name, + strlen(key_info->name))); + item_list.push_back(new Item_int((int32) key_info->key_length)); + item_list.push_back(item_null); } - buff_ptr=strmov(buff_ptr,"Not exists"); - } - if (need_tmp_table) - { - need_tmp_table=0; - if (buff != buff_ptr) + else if (tab->select && tab->select->quick) { - buff_ptr[0]=';' ; buff_ptr[1]=' '; buff_ptr+=2; + KEY *key_info=table->key_info+ tab->select->quick->index; + item_list.push_back(new Item_string(key_info->name, + strlen(key_info->name))); + item_list.push_back(new Item_int((int32) tab->select->quick->max_used_key_length)); + item_list.push_back(item_null); } - buff_ptr=strmov(buff_ptr,"Using temporary"); - } - if (need_order) - { - need_order=0; - if (buff != buff_ptr) + else { - buff_ptr[0]=';' ; buff_ptr[1]=' '; buff_ptr+=2; + item_list.push_back(item_null); + item_list.push_back(item_null); + item_list.push_back(item_null); } - buff_ptr=strmov(buff_ptr,"Using filesort"); - } - if (distinct & test_all_bits(used_tables,thd->used_tables)) - { - if (buff != buff_ptr) + sprintf(buff3,"%.0f",join->best_positions[i].records_read); + item_list.push_back(new Item_string(buff3,strlen(buff3))); + my_bool key_read=table->key_read; + if (tab->type == JT_NEXT && + ((table->used_keys & ((key_map) 1 << tab->index)))) + key_read=1; + + if (tab->info) + item_list.push_back(new Item_string(tab->info,strlen(tab->info))); + else { - buff_ptr[0]=';' ; buff_ptr[1]=' '; buff_ptr+=2; + if (tab->select) + { + if (tab->use_quick == 2) + { + sprintf(buff_ptr,"; Range checked for each record (index map: %u)", + tab->keys); + buff_ptr=strend(buff_ptr); + } + else + buff_ptr=strmov(buff_ptr,"; Using where"); + } + if (key_read) + buff_ptr= strmov(buff_ptr,"; Using index"); + if (table->reginfo.not_exists_optimize) + buff_ptr= strmov(buff_ptr,"; Not exists"); + if (need_tmp_table) + { + need_tmp_table=0; + buff_ptr= strmov(buff_ptr,"; Using temporary"); + } + if (need_order) + { + need_order=0; + buff_ptr= strmov(buff_ptr,"; Using filesort"); + } + if (distinct && test_all_bits(used_tables,thd->used_tables)) + buff_ptr= strmov(buff_ptr,"; Distinct"); + if (buff_ptr == buff) + buff_ptr+= 2; + item_list.push_back(new Item_string(buff+2,(uint) (buff_ptr - buff)-2)); } - buff_ptr=strmov(buff_ptr,"Distinct"); + // For next iteration + used_tables|=table->map; + if (result->send_data(item_list)) + result->send_error(0,NullS); } - net_store_data(packet,buff,(uint) (buff_ptr - buff)); - if (my_net_write(&thd->net,(char*) packet->ptr(),packet->length())) - DBUG_VOID_RETURN; /* purecov: inspected */ - - // For next iteration - used_tables|=table->map; } - send_eof(&thd->net); + if (!thd->lex.select->next) // Not union + { + save_lock=thd->lock; + thd->lock=(MYSQL_LOCK *)0; + result->send_eof(); + thd->lock=save_lock; + } DBUG_VOID_RETURN; } -static void describe_info(THD *thd, const char *info) +static void describe_info(JOIN *join, const char *info) { + THD *thd= join->thd; + + if (thd->lex.select_lex.next) /* If in UNION */ + { + select_describe(join,FALSE,FALSE,FALSE,info); + return; + } List<Item> field_list; String *packet= &thd->packet; /* Don't log this into the slow query log */ - thd->lex.options&= ~(QUERY_NO_INDEX_USED | QUERY_NO_GOOD_INDEX_USED); + thd->lex.select_lex.options&= ~(QUERY_NO_INDEX_USED | + QUERY_NO_GOOD_INDEX_USED); field_list.push_back(new Item_empty_string("Comment",80)); if (send_fields(thd,field_list,1)) return; /* purecov: inspected */ diff --git a/sql/sql_select.h b/sql/sql_select.h index 1bf7d7863eb..332778aafe6 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -115,22 +115,26 @@ typedef struct st_position { /* Used in find_best */ /* Param to create temporary tables when doing SELECT:s */ -class TMP_TABLE_PARAM { +class TMP_TABLE_PARAM :public Sql_alloc +{ public: List<Item> copy_funcs; - Copy_field *copy_field; + List_iterator_fast<Item> copy_funcs_it; + Copy_field *copy_field, *copy_field_end; byte *group_buff; - Item_result_field **funcs; + Item **items_to_copy; /* Fields in tmp table */ MI_COLUMNDEF *recinfo,*start_recinfo; KEY *keyinfo; ha_rows end_write_records; - uint copy_field_count,field_count,sum_func_count,func_count; + uint field_count,sum_func_count,func_count; uint hidden_field_count; - uint group_parts,group_length; + uint group_parts,group_length,group_null_parts; uint quick_group; bool using_indirect_summary_function; - TMP_TABLE_PARAM() :copy_field(0), group_parts(0), group_length(0) + TMP_TABLE_PARAM() + :copy_funcs_it(copy_funcs), copy_field(0), group_parts(0), + group_length(0), group_null_parts(0) {} ~TMP_TABLE_PARAM() { @@ -154,7 +158,8 @@ class JOIN { uint tables,const_tables; uint send_group_parts; bool sort_and_group,first_record,full_join,group, no_field_update; - table_map const_table_map,outer_join; + bool do_send_rows; + table_map const_table_map,found_const_table_map,outer_join; ha_rows send_records,found_records,examined_rows,row_limit; POSITION positions[MAX_TABLES+1],best_positions[MAX_TABLES+1]; double best_read; @@ -183,13 +188,13 @@ void TEST_join(JOIN *join); bool store_val_in_field(Field *field,Item *val); TABLE *create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, ORDER *group, bool distinct, bool save_sum_fields, - bool allow_distinct_limit, uint select_options); + bool allow_distinct_limit, ulong select_options); void free_tmp_table(THD *thd, TABLE *entry); void count_field_types(TMP_TABLE_PARAM *param, List<Item> &fields, bool reset_with_sum_func); -bool setup_copy_fields(TMP_TABLE_PARAM *param,List<Item> &fields); +bool setup_copy_fields(THD *thd, TMP_TABLE_PARAM *param,List<Item> &fields); void copy_fields(TMP_TABLE_PARAM *param); -void copy_funcs(Item_result_field **func_ptr); +void copy_funcs(Item **func_ptr); bool create_myisam_from_heap(TABLE *table, TMP_TABLE_PARAM *param, int error, bool ignore_last_dupp_error); @@ -207,7 +212,7 @@ class store_key :public Sql_alloc char *null_ptr; char err; public: - store_key(Field *field_arg, char *ptr, char *null, uint length) + store_key(THD *thd, Field *field_arg, char *ptr, char *null, uint length) :null_ptr(null),err(0) { if (field_arg->type() == FIELD_TYPE_BLOB) @@ -216,7 +221,7 @@ class store_key :public Sql_alloc field_arg->table, field_arg->binary()); else { - to_field=field_arg->new_field(field_arg->table); + to_field=field_arg->new_field(&thd->mem_root,field_arg->table); if (to_field) to_field->move_field(ptr, (uchar*) null, 1); } @@ -232,9 +237,9 @@ class store_key_field: public store_key Copy_field copy_field; const char *field_name; public: - store_key_field(Field *to_field_arg, char *ptr, char *null_ptr_arg, + store_key_field(THD *thd, Field *to_field_arg, char *ptr, char *null_ptr_arg, uint length, Field *from_field, const char *name_arg) - :store_key(to_field_arg,ptr, + :store_key(thd, to_field_arg,ptr, null_ptr_arg ? null_ptr_arg : from_field->maybe_null() ? &err : NullS,length), field_name(name_arg) { @@ -243,12 +248,12 @@ class store_key_field: public store_key copy_field.set(to_field,from_field,0); } } - bool copy() - { - copy_field.do_copy(©_field); - return err != 0; - } - const char *name() const { return field_name; } + bool copy() + { + copy_field.do_copy(©_field); + return err != 0; + } + const char *name() const { return field_name; } }; @@ -257,16 +262,15 @@ class store_key_item :public store_key protected: Item *item; public: - store_key_item(Field *to_field_arg, char *ptr, char *null_ptr_arg, + store_key_item(THD *thd, Field *to_field_arg, char *ptr, char *null_ptr_arg, uint length, Item *item_arg) - :store_key(to_field_arg,ptr, + :store_key(thd, to_field_arg,ptr, null_ptr_arg ? null_ptr_arg : item_arg->maybe_null ? &err : NullS, length), item(item_arg) {} bool copy() { - item->save_in_field(to_field); - return err != 0; + return item->save_in_field(to_field, 1) || err != 0; } const char *name() const { return "func"; } }; @@ -276,10 +280,10 @@ class store_key_const_item :public store_key_item { bool inited; public: - store_key_const_item(Field *to_field_arg, char *ptr, + store_key_const_item(THD *thd, Field *to_field_arg, char *ptr, char *null_ptr_arg, uint length, Item *item_arg) - :store_key_item(to_field_arg,ptr, + :store_key_item(thd, to_field_arg,ptr, null_ptr_arg ? null_ptr_arg : item_arg->maybe_null ? &err : NullS, length, item_arg), inited(0) { @@ -289,7 +293,8 @@ public: if (!inited) { inited=1; - item->save_in_field(to_field); + if (item->save_in_field(to_field, 1)) + err= 1; } return err != 0; } @@ -297,3 +302,4 @@ public: }; bool cp_buffer_from_ref(TABLE_REF *ref); +bool error_if_full_join(JOIN *join); diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 585c30110b3..95197ecfc4b 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -20,6 +20,7 @@ #include "mysql_priv.h" #include "sql_select.h" // For select_describe #include "sql_acl.h" +#include "repl_failsafe.h" #include <my_dir.h> #ifdef HAVE_BERKELEY_DB @@ -45,6 +46,8 @@ store_create_info(THD *thd, TABLE *table, String *packet); static void append_identifier(THD *thd, String *packet, const char *name); +extern struct st_VioSSLAcceptorFd * ssl_acceptor_fd; + /**************************************************************************** ** Send list of databases ** A database is a directory in the mysql_data_home directory @@ -72,16 +75,16 @@ mysqld_show_dbs(THD *thd,const char *wild) DBUG_RETURN(1); if (mysql_find_files(thd,&files,NullS,mysql_data_home,wild,1)) DBUG_RETURN(1); - List_iterator<char> it(files); + List_iterator_fast<char> it(files); while ((file_name=it++)) { - if (!opt_safe_show_db || thd->master_access || + if (thd->master_access & (DB_ACLS | SHOW_DB_ACL) || acl_get(thd->host, thd->ip, (char*) &thd->remote.sin_addr, thd->priv_user, file_name) || (grant_option && !check_grant_db(thd, file_name))) - { + { thd->packet.length(0); - net_store_data(&thd->packet,file_name); + net_store_data(&thd->packet, thd->variables.convert_set, file_name); if (my_net_write(&thd->net, (char*) thd->packet.ptr(), thd->packet.length())) DBUG_RETURN(-1); @@ -95,34 +98,31 @@ mysqld_show_dbs(THD *thd,const char *wild) ** List all open tables in a database ***************************************************************************/ -int mysqld_show_open_tables(THD *thd,const char *db,const char *wild) +int mysqld_show_open_tables(THD *thd,const char *wild) { - Item_string *field=new Item_string("",0); List<Item> field_list; - char *end,*table_name; - List<char> tables; + OPEN_TABLE_LIST *open_list; + CONVERT *convert=thd->variables.convert_set; DBUG_ENTER("mysqld_show_open_tables"); - field->name=(char*) thd->alloc(20+(uint) strlen(db)+(wild ? (uint) strlen(wild)+4:0)); - end=strxmov(field->name,"Open_tables_in_",db,NullS); - if (wild && wild[0]) - strxmov(end," (",wild,")",NullS); - field->max_length=NAME_LEN; - field_list.push_back(field); - field_list.push_back(new Item_empty_string("Comment",80)); + field_list.push_back(new Item_empty_string("Database",NAME_LEN)); + field_list.push_back(new Item_empty_string("Table",NAME_LEN)); + field_list.push_back(new Item_int("In_use",0, 4)); + field_list.push_back(new Item_int("Name_locked",0, 4)); if (send_fields(thd,field_list,1)) DBUG_RETURN(1); - if (list_open_tables(thd,&tables,db,wild)) + if (!(open_list=list_open_tables(thd,wild)) && thd->fatal_error) DBUG_RETURN(-1); - List_iterator<char> it(tables); - while ((table_name=it++)) + for (; open_list ; open_list=open_list->next) { thd->packet.length(0); - net_store_data(&thd->packet,table_name); - net_store_data(&thd->packet,query_table_status(thd,db,table_name)); + net_store_data(&thd->packet,convert, open_list->db); + net_store_data(&thd->packet,convert, open_list->table); + net_store_data(&thd->packet,open_list->in_use); + net_store_data(&thd->packet,open_list->locked); if (my_net_write(&thd->net,(char*) thd->packet.ptr(),thd->packet.length())) { DBUG_RETURN(-1); @@ -159,11 +159,11 @@ int mysqld_show_tables(THD *thd,const char *db,const char *wild) DBUG_RETURN(1); if (mysql_find_files(thd,&files,db,path,wild,0)) DBUG_RETURN(-1); - List_iterator<char> it(files); + List_iterator_fast<char> it(files); while ((file_name=it++)) { thd->packet.length(0); - net_store_data(&thd->packet,file_name); + net_store_data(&thd->packet, thd->variables.convert_set, file_name); if (my_net_write(&thd->net,(char*) thd->packet.ptr(),thd->packet.length())) DBUG_RETURN(-1); } @@ -259,6 +259,7 @@ int mysqld_extend_show_tables(THD *thd,const char *db,const char *wild) char *file_name; TABLE *table; String *packet= &thd->packet; + CONVERT *convert=thd->variables.convert_set; DBUG_ENTER("mysqld_extend_show_tables"); (void) sprintf(path,"%s/%s",mysql_data_home,db); @@ -299,13 +300,13 @@ int mysqld_extend_show_tables(THD *thd,const char *db,const char *wild) if (mysql_find_files(thd,&files,db,path,wild,0)) DBUG_RETURN(-1); - List_iterator<char> it(files); + List_iterator_fast<char> it(files); while ((file_name=it++)) { TABLE_LIST table_list; bzero((char*) &table_list,sizeof(table_list)); packet->length(0); - net_store_data(packet,file_name); + net_store_data(packet,convert, file_name); table_list.db=(char*) db; table_list.real_name= table_list.alias= file_name; if (lower_case_table_names) @@ -314,7 +315,7 @@ int mysqld_extend_show_tables(THD *thd,const char *db,const char *wild) { for (uint i=2 ; i < field_list.elements ; i++) net_store_null(packet); - net_store_data(packet,thd->net.last_error); + net_store_data(packet,convert, thd->net.last_error); thd->net.last_error[0]=0; } else @@ -322,12 +323,12 @@ int mysqld_extend_show_tables(THD *thd,const char *db,const char *wild) struct tm tm_tmp; handler *file=table->file; file->info(HA_STATUS_VARIABLE | HA_STATUS_TIME | HA_STATUS_NO_LOCK); - net_store_data(packet, file->table_type()); - net_store_data(packet, + net_store_data(packet, convert, file->table_type()); + net_store_data(packet, convert, + (table->db_options_in_use & HA_OPTION_COMPRESS_RECORD) ? + "Compressed" : (table->db_options_in_use & HA_OPTION_PACK_RECORD) ? - "Dynamic" : - (table->db_options_in_use & HA_OPTION_COMPRESS_RECORD) - ? "Compressed" : "Fixed"); + "Dynamic" : "Fixed"); net_store_data(packet, (longlong) file->records); net_store_data(packet, (uint32) file->mean_rec_length); net_store_data(packet, (longlong) file->data_file_length); @@ -404,7 +405,7 @@ int mysqld_extend_show_tables(THD *thd,const char *db,const char *wild) my_raid_type(file->raid_type), file->raid_chunks, file->raid_chunksize/RAID_BLOCK_SIZE); ptr=strmov(ptr,buff); } - net_store_data(packet, option_buff+1, + net_store_data(packet, convert, option_buff+1, (ptr == option_buff ? 0 : (uint) (ptr-option_buff)-1)); } { @@ -437,6 +438,7 @@ mysqld_show_fields(THD *thd, TABLE_LIST *table_list,const char *wild, handler *file; char tmp[MAX_FIELD_WIDTH]; Item *item; + CONVERT *convert=thd->variables.convert_set; DBUG_ENTER("mysqld_show_fields"); DBUG_PRINT("enter",("db: %s table: %s",table_list->db, table_list->real_name)); @@ -492,18 +494,18 @@ mysqld_show_fields(THD *thd, TABLE_LIST *table_list,const char *wild, bool null_default_value=0; packet->length(0); - net_store_data(packet,field->field_name); + net_store_data(packet,convert,field->field_name); field->sql_type(type); - net_store_data(packet,type.ptr(),type.length()); + net_store_data(packet,convert,type.ptr(),type.length()); pos=(byte*) ((flags & NOT_NULL_FLAG) && field->type() != FIELD_TYPE_TIMESTAMP ? "" : "YES"); - net_store_data(packet,(const char*) pos); + net_store_data(packet,convert,(const char*) pos); pos=(byte*) ((field->flags & PRI_KEY_FLAG) ? "PRI" : (field->flags & UNIQUE_KEY_FLAG) ? "UNI" : (field->flags & MULTIPLE_KEY_FLAG) ? "MUL":""); - net_store_data(packet,(char*) pos); + net_store_data(packet,convert,(char*) pos); if (field->type() == FIELD_TYPE_TIMESTAMP || field->unireg_check == Field::NEXT_NUMBER) @@ -512,17 +514,17 @@ mysqld_show_fields(THD *thd, TABLE_LIST *table_list,const char *wild, { // Not null by default type.set(tmp,sizeof(tmp)); field->val_str(&type,&type); - net_store_data(packet,type.ptr(),type.length()); + net_store_data(packet,convert,type.ptr(),type.length()); } else if (field->maybe_null() || null_default_value) net_store_null(packet); // Null as default else - net_store_data(packet,tmp,0); + net_store_data(packet,convert,tmp,0); char *end=tmp; if (field->unireg_check == Field::NEXT_NUMBER) end=strmov(tmp,"auto_increment"); - net_store_data(packet,tmp,(uint) (end-tmp)); + net_store_data(packet,convert,tmp,(uint) (end-tmp)); if (verbose) { @@ -537,7 +539,7 @@ mysqld_show_fields(THD *thd, TABLE_LIST *table_list,const char *wild, end=strmov(end,grant_types.type_names[bitnr]); } } - net_store_data(packet,tmp+1,end == tmp ? 0 : (uint) (end-tmp-1)); + net_store_data(packet,convert, tmp+1,end == tmp ? 0 : (uint) (end-tmp-1)); } if (my_net_write(&thd->net,(char*) packet->ptr(),packet->length())) DBUG_RETURN(1); @@ -552,6 +554,7 @@ int mysqld_show_create(THD *thd, TABLE_LIST *table_list) { TABLE *table; + CONVERT *convert=thd->variables.convert_set; DBUG_ENTER("mysqld_show_create"); DBUG_PRINT("enter",("db: %s table: %s",table_list->db, table_list->real_name)); @@ -573,7 +576,7 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list) String *packet = &thd->packet; { packet->length(0); - net_store_data(packet, table->table_name); + net_store_data(packet,convert, table->table_name); /* A hack - we need to reserve some space for the length before we know what it is - let's assume that the length of create table @@ -639,6 +642,7 @@ mysqld_show_keys(THD *thd, TABLE_LIST *table_list) { TABLE *table; char buff[256]; + CONVERT *convert=thd->variables.convert_set; DBUG_ENTER("mysqld_show_keys"); DBUG_PRINT("enter",("db: %s table: %s",table_list->db, table_list->real_name)); @@ -658,12 +662,14 @@ mysqld_show_keys(THD *thd, TABLE_LIST *table_list) field_list.push_back(new Item_empty_string("Column_name",NAME_LEN)); field_list.push_back(item=new Item_empty_string("Collation",1)); item->maybe_null=1; - field_list.push_back(item=new Item_int("Cardinality",0,11)); + field_list.push_back(item=new Item_int("Cardinality",0,21)); item->maybe_null=1; field_list.push_back(item=new Item_int("Sub_part",0,3)); item->maybe_null=1; field_list.push_back(item=new Item_empty_string("Packed",10)); item->maybe_null=1; + field_list.push_back(new Item_empty_string("Null",3)); + field_list.push_back(new Item_empty_string("Index_type",16)); field_list.push_back(new Item_empty_string("Comment",255)); item->maybe_null=1; @@ -680,38 +686,49 @@ mysqld_show_keys(THD *thd, TABLE_LIST *table_list) for (uint j=0 ; j < key_info->key_parts ; j++,key_part++) { packet->length(0); - net_store_data(packet,table->table_name); - net_store_data(packet,((key_info->flags & HA_NOSAME) ? "0" :"1"), 1); - net_store_data(packet,key_info->name); + net_store_data(packet,convert,table->table_name); + net_store_data(packet,convert,((key_info->flags & HA_NOSAME) ? "0" :"1"), 1); + net_store_data(packet,convert,key_info->name); end=int10_to_str((long) (j+1),(char*) buff,10); - net_store_data(packet,buff,(uint) (end-buff)); - net_store_data(packet,key_part->field ? key_part->field->field_name : + net_store_data(packet,convert,buff,(uint) (end-buff)); + net_store_data(packet,convert, + key_part->field ? key_part->field->field_name : "?unknown field?"); - if (table->file->option_flag() & HA_READ_ORDER) - net_store_data(packet,((key_part->key_part_flag & HA_REVERSE_SORT) - ? "D" : "A"), 1); + if (table->file->index_flags(i) & HA_READ_ORDER) + net_store_data(packet,convert, + ((key_part->key_part_flag & HA_REVERSE_SORT) ? + "D" : "A"), 1); else net_store_null(packet); /* purecov: inspected */ KEY *key=table->key_info+i; if (key->rec_per_key[j]) { - ulong records=(table->file->records / key->rec_per_key[j]); - end=int10_to_str((long) records, buff, 10); - net_store_data(packet,buff,(uint) (end-buff)); + ha_rows records=(table->file->records / key->rec_per_key[j]); + end=longlong10_to_str((longlong) records, buff, 10); + net_store_data(packet,convert,buff,(uint) (end-buff)); } else net_store_null(packet); + + /* Check if we have a key part that only uses part of the field */ if (!key_part->field || key_part->length != table->field[key_part->fieldnr-1]->key_length()) { end=int10_to_str((long) key_part->length, buff,10); /* purecov: inspected */ - net_store_data(packet,buff,(uint) (end-buff)); /* purecov: inspected */ + net_store_data(packet,convert,buff,(uint) (end-buff)); /* purecov: inspected */ } else net_store_null(packet); net_store_null(packet); // No pack_information yet - net_store_data(packet,key_info->flags & HA_FULLTEXT ? "FULLTEXT":""); + + /* Null flag */ + uint flags= key_part->field ? key_part->field->flags : 0; + char *pos=(char*) ((flags & NOT_NULL_FLAG) ? "" : "YES"); + net_store_data(packet,convert,(const char*) pos); + net_store_data(packet,convert,table->file->index_type(i)); + /* Comment */ + net_store_data(packet,convert,""); if (my_net_write(&thd->net,(char*) packet->ptr(),packet->length())) DBUG_RETURN(1); /* purecov: inspected */ } @@ -756,27 +773,29 @@ mysqld_list_fields(THD *thd, TABLE_LIST *table_list, const char *wild) int mysqld_dump_create_info(THD *thd, TABLE *table, int fd) { + CONVERT *convert=thd->variables.convert_set; DBUG_ENTER("mysqld_dump_create_info"); DBUG_PRINT("enter",("table: %s",table->real_name)); + String* packet = &thd->packet; packet->length(0); - - if(store_create_info(thd,table,packet)) + if (store_create_info(thd,table,packet)) DBUG_RETURN(-1); - if(fd < 0) + if (convert) + convert->convert((char*) packet->ptr(), packet->length()); + if (fd < 0) { - if(my_net_write(&thd->net, (char*)packet->ptr(), packet->length())) + if (my_net_write(&thd->net, (char*)packet->ptr(), packet->length())) DBUG_RETURN(-1); VOID(net_flush(&thd->net)); } else { - if(my_write(fd, (const byte*) packet->ptr(), packet->length(), - MYF(MY_WME))) + if (my_write(fd, (const byte*) packet->ptr(), packet->length(), + MYF(MY_WME))) DBUG_RETURN(-1); } - DBUG_RETURN(0); } @@ -998,18 +1017,21 @@ public: template class I_List<thread_info>; #endif +#define LIST_PROCESS_HOST_LEN 64 void mysqld_list_processes(THD *thd,const char *user, bool verbose) { Item *field; List<Item> field_list; I_List<thread_info> thread_infos; - ulong max_query_length= verbose ? max_allowed_packet : PROCESS_LIST_WIDTH; + ulong max_query_length= (verbose ? thd->variables.max_allowed_packet : + PROCESS_LIST_WIDTH); + CONVERT *convert=thd->variables.convert_set; DBUG_ENTER("mysqld_list_processes"); field_list.push_back(new Item_int("Id",0,7)); field_list.push_back(new Item_empty_string("User",16)); - field_list.push_back(new Item_empty_string("Host",64)); + field_list.push_back(new Item_empty_string("Host",LIST_PROCESS_HOST_LEN)); field_list.push_back(field=new Item_empty_string("db",NAME_LEN)); field->maybe_null=1; field_list.push_back(new Item_empty_string("Command",16)); @@ -1035,11 +1057,17 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose) thread_info *thd_info=new thread_info; thd_info->thread_id=tmp->thread_id; - thd_info->user=thd->strdup(tmp->user ? tmp->user : (tmp->system_thread ? - "system user" : "unauthenticated user")); - thd_info->host=thd->strdup(tmp->host ? tmp->host : (tmp->ip ? tmp->ip : - (tmp->system_thread ? "none" : - "connecting host"))); + thd_info->user=thd->strdup(tmp->user ? tmp->user : + (tmp->system_thread ? + "system user" : "unauthenticated user")); + if (tmp->peer_port && (tmp->host || tmp->ip)) + { + if ((thd_info->host= thd->alloc(LIST_PROCESS_HOST_LEN+1))) + my_snprintf((char *) thd_info->host, LIST_PROCESS_HOST_LEN, + "%s:%u", thd->host_or_ip, tmp->peer_port); + } + else + thd_info->host= thd->strdup(thd->host_or_ip); if ((thd_info->db=tmp->db)) // Safe test thd_info->db=thd->strdup(thd_info->db); thd_info->command=(int) tmp->command; @@ -1071,9 +1099,8 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose) thd_info->query=0; if (tmp->query) { - uint length=(uint) strlen(tmp->query); - if (length > max_query_length) - length=max_query_length; + /* query_length is always set before tmp->query */ + uint length= min(max_query_length, tmp->query_length); thd_info->query=(char*) thd->memdup(tmp->query,length+1); thd_info->query[length]=0; } @@ -1090,28 +1117,28 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose) char buff[20],*end; packet->length(0); end=int10_to_str((long) thd_info->thread_id, buff,10); - net_store_data(packet,buff,(uint) (end-buff)); - net_store_data(packet,thd_info->user); - net_store_data(packet,thd_info->host); + net_store_data(packet,convert,buff,(uint) (end-buff)); + net_store_data(packet,convert,thd_info->user); + net_store_data(packet,convert,thd_info->host); if (thd_info->db) - net_store_data(packet,thd_info->db); + net_store_data(packet,convert,thd_info->db); else net_store_null(packet); if (thd_info->proc_info) - net_store_data(packet,thd_info->proc_info); + net_store_data(packet,convert,thd_info->proc_info); else - net_store_data(packet,command_name[thd_info->command]); + net_store_data(packet,convert,command_name[thd_info->command]); if (thd_info->start_time) - net_store_data(packet,(uint32) - (time((time_t*) 0) - thd_info->start_time)); + net_store_data(packet, + (uint32) (time((time_t*) 0) - thd_info->start_time)); else net_store_null(packet); if (thd_info->state_info) - net_store_data(packet,thd_info->state_info); + net_store_data(packet,convert,thd_info->state_info); else net_store_null(packet); if (thd_info->query) - net_store_data(packet,thd_info->query); + net_store_data(packet,convert,thd_info->query); else net_store_null(packet); if (my_net_write(&thd->net,(char*) packet->ptr(),packet->length())) @@ -1127,12 +1154,14 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose) *****************************************************************************/ -int mysqld_show(THD *thd, const char *wild, show_var_st *variables) +int mysqld_show(THD *thd, const char *wild, show_var_st *variables, + enum enum_var_type value_type) { - uint i; char buff[8192]; String packet2(buff,sizeof(buff)); List<Item> field_list; + CONVERT *convert=thd->variables.convert_set; + DBUG_ENTER("mysqld_show"); field_list.push_back(new Item_empty_string("Variable_name",30)); field_list.push_back(new Item_empty_string("Value",256)); @@ -1141,42 +1170,51 @@ int mysqld_show(THD *thd, const char *wild, show_var_st *variables) /* pthread_mutex_lock(&THR_LOCK_keycache); */ pthread_mutex_lock(&LOCK_status); - for (i=0; variables[i].name; i++) + for (; variables->name; variables++) { - if (!(wild && wild[0] && wild_case_compare(variables[i].name,wild))) + if (!(wild && wild[0] && wild_case_compare(variables->name,wild))) { packet2.length(0); - net_store_data(&packet2,variables[i].name); - switch (variables[i].type){ + net_store_data(&packet2,convert,variables->name); + SHOW_TYPE show_type=variables->type; + char *value=variables->value; + if (show_type == SHOW_SYS) + { + show_type= ((sys_var*) value)->type(); + value= (char*) ((sys_var*) value)->value_ptr(thd, value_type); + } + + switch (show_type) { case SHOW_LONG: case SHOW_LONG_CONST: - net_store_data(&packet2,(uint32) *(ulong*) variables[i].value); + net_store_data(&packet2,(uint32) *(ulong*) value); + break; + case SHOW_LONGLONG: + net_store_data(&packet2,(longlong) *(longlong*) value); break; - case SHOW_LONG_AS_LONGLONG: - net_store_data(&packet2,(longlong) *(ulong*) variables[i].value); + case SHOW_HA_ROWS: + net_store_data(&packet2,(longlong) *(ha_rows*) value); break; case SHOW_BOOL: - net_store_data(&packet2,(ulong) *(bool*) variables[i].value ? - "ON" : "OFF"); + net_store_data(&packet2,(ulong) *(bool*) value ? "ON" : "OFF"); break; case SHOW_MY_BOOL: - net_store_data(&packet2,(ulong) *(my_bool*) variables[i].value ? - "ON" : "OFF"); + net_store_data(&packet2,(ulong) *(my_bool*) value ? "ON" : "OFF"); break; case SHOW_INT_CONST: case SHOW_INT: - net_store_data(&packet2,(uint32) *(int*) variables[i].value); + net_store_data(&packet2,(uint32) *(int*) value); break; case SHOW_HAVE: { - SHOW_COMP_OPTION tmp= *(SHOW_COMP_OPTION*) variables[i].value; + SHOW_COMP_OPTION tmp= *(SHOW_COMP_OPTION*) value; net_store_data(&packet2, (tmp == SHOW_OPTION_NO ? "NO" : tmp == SHOW_OPTION_YES ? "YES" : "DISABLED")); break; } case SHOW_CHAR: - net_store_data(&packet2,variables[i].value); + net_store_data(&packet2,convert, value); break; case SHOW_STARTTIME: net_store_data(&packet2,(uint32) (thd->query_start() - start_time)); @@ -1184,15 +1222,189 @@ int mysqld_show(THD *thd, const char *wild, show_var_st *variables) case SHOW_QUESTION: net_store_data(&packet2,(uint32) thd->query_id); break; + case SHOW_RPL_STATUS: + net_store_data(&packet2, rpl_status_type[(int)rpl_status]); + break; +#ifndef EMBEDDED_LIBRARY + case SHOW_SLAVE_RUNNING: + { + LOCK_ACTIVE_MI; + net_store_data(&packet2, (active_mi->slave_running && + active_mi->rli.slave_running) + ? "ON" : "OFF"); + UNLOCK_ACTIVE_MI; + break; + } +#endif case SHOW_OPENTABLES: net_store_data(&packet2,(uint32) cached_tables()); break; case SHOW_CHAR_PTR: - { - char *value= *(char**) variables[i].value; - net_store_data(&packet2,value ? value : ""); - break; + { + value= *(char**) value; + net_store_data(&packet2,convert, value ? value : ""); + break; + } +#ifdef HAVE_OPENSSL + /* First group - functions relying on CTX */ + case SHOW_SSL_CTX_SESS_ACCEPT: + net_store_data(&packet2,(uint32) + (!ssl_acceptor_fd ? 0 : + SSL_CTX_sess_accept(ssl_acceptor_fd->ssl_context_))); + break; + case SHOW_SSL_CTX_SESS_ACCEPT_GOOD: + net_store_data(&packet2,(uint32) + (!ssl_acceptor_fd ? 0 : + SSL_CTX_sess_accept_good(ssl_acceptor_fd->ssl_context_))); + break; + case SHOW_SSL_CTX_SESS_CONNECT_GOOD: + net_store_data(&packet2,(uint32) + (!ssl_acceptor_fd ? 0 : + SSL_CTX_sess_connect_good(ssl_acceptor_fd->ssl_context_))); + break; + case SHOW_SSL_CTX_SESS_ACCEPT_RENEGOTIATE: + net_store_data(&packet2,(uint32) + (!ssl_acceptor_fd ? 0 : + SSL_CTX_sess_accept_renegotiate(ssl_acceptor_fd->ssl_context_))); + break; + case SHOW_SSL_CTX_SESS_CONNECT_RENEGOTIATE: + net_store_data(&packet2,(uint32) + (!ssl_acceptor_fd ? 0 : + SSL_CTX_sess_connect_renegotiate(ssl_acceptor_fd->ssl_context_))); + break; + case SHOW_SSL_CTX_SESS_CB_HITS: + net_store_data(&packet2,(uint32) + (!ssl_acceptor_fd ? 0 : + SSL_CTX_sess_cb_hits(ssl_acceptor_fd->ssl_context_))); + break; + case SHOW_SSL_CTX_SESS_HITS: + net_store_data(&packet2,(uint32) + (!ssl_acceptor_fd ? 0 : + SSL_CTX_sess_hits(ssl_acceptor_fd->ssl_context_))); + break; + case SHOW_SSL_CTX_SESS_CACHE_FULL: + net_store_data(&packet2,(uint32) + (!ssl_acceptor_fd ? 0 : + SSL_CTX_sess_cache_full(ssl_acceptor_fd->ssl_context_))); + break; + case SHOW_SSL_CTX_SESS_MISSES: + net_store_data(&packet2,(uint32) + (!ssl_acceptor_fd ? 0 : + SSL_CTX_sess_misses(ssl_acceptor_fd->ssl_context_))); + break; + case SHOW_SSL_CTX_SESS_TIMEOUTS: + net_store_data(&packet2,(uint32) + (!ssl_acceptor_fd ? 0 : + SSL_CTX_sess_timeouts(ssl_acceptor_fd->ssl_context_))); + break; + case SHOW_SSL_CTX_SESS_NUMBER: + net_store_data(&packet2,(uint32) + (!ssl_acceptor_fd ? 0 : + SSL_CTX_sess_number(ssl_acceptor_fd->ssl_context_))); + break; + case SHOW_SSL_CTX_SESS_CONNECT: + net_store_data(&packet2,(uint32) + (!ssl_acceptor_fd ? 0 : + SSL_CTX_sess_connect(ssl_acceptor_fd->ssl_context_))); + break; + case SHOW_SSL_CTX_SESS_GET_CACHE_SIZE: + net_store_data(&packet2,(uint32) + (!ssl_acceptor_fd ? 0 : + SSL_CTX_sess_get_cache_size(ssl_acceptor_fd->ssl_context_))); + break; + case SHOW_SSL_CTX_GET_VERIFY_MODE: + net_store_data(&packet2,(uint32) + (!ssl_acceptor_fd ? 0 : + SSL_CTX_get_verify_mode(ssl_acceptor_fd->ssl_context_))); + break; + case SHOW_SSL_CTX_GET_VERIFY_DEPTH: + net_store_data(&packet2,(uint32) + (!ssl_acceptor_fd ? 0 : + SSL_CTX_get_verify_depth(ssl_acceptor_fd->ssl_context_))); + break; + case SHOW_SSL_CTX_GET_SESSION_CACHE_MODE: + if (!ssl_acceptor_fd) + { + net_store_data(&packet2,"NONE" ); + break; + } + switch (SSL_CTX_get_session_cache_mode(ssl_acceptor_fd->ssl_context_)) + { + case SSL_SESS_CACHE_OFF: + net_store_data(&packet2,"OFF" ); + break; + case SSL_SESS_CACHE_CLIENT: + net_store_data(&packet2,"CLIENT" ); + break; + case SSL_SESS_CACHE_SERVER: + net_store_data(&packet2,"SERVER" ); + break; + case SSL_SESS_CACHE_BOTH: + net_store_data(&packet2,"BOTH" ); + break; + case SSL_SESS_CACHE_NO_AUTO_CLEAR: + net_store_data(&packet2,"NO_AUTO_CLEAR" ); + break; + case SSL_SESS_CACHE_NO_INTERNAL_LOOKUP: + net_store_data(&packet2,"NO_INTERNAL_LOOKUP" ); + break; + default: + net_store_data(&packet2,"Unknown"); + break; + } + break; + /* First group - functions relying on SSL */ + case SHOW_SSL_GET_VERSION: + net_store_data(&packet2, thd->net.vio->ssl_ ? + SSL_get_version(thd->net.vio->ssl_) : ""); + break; + case SHOW_SSL_SESSION_REUSED: + net_store_data(&packet2,(uint32) (thd->net.vio->ssl_ ? + SSL_session_reused(thd->net.vio->ssl_) : 0)); + break; + case SHOW_SSL_GET_DEFAULT_TIMEOUT: + net_store_data(&packet2,(uint32) (thd->net.vio->ssl_ ? + SSL_get_default_timeout(thd->net.vio->ssl_):0)); + break; + case SHOW_SSL_GET_VERIFY_MODE: + net_store_data(&packet2,(uint32) (thd->net.vio->ssl_ ? + SSL_get_verify_mode(thd->net.vio->ssl_):0)); + break; + case SHOW_SSL_GET_VERIFY_DEPTH: + net_store_data(&packet2,(uint32) (thd->net.vio->ssl_ ? + SSL_get_verify_depth(thd->net.vio->ssl_):0)); + break; + case SHOW_SSL_GET_CIPHER: + net_store_data(&packet2, thd->net.vio->ssl_ ? + SSL_get_cipher(thd->net.vio->ssl_) : ""); + break; + case SHOW_SSL_GET_CIPHER_LIST: + if (thd->net.vio->ssl_) + { + char buf[1024], *pos; + pos=buf; + for (int i=0 ; i++ ;) + { + const char *p=SSL_get_cipher_list(thd->net.vio->ssl_,i); + if (p == NULL) + break; + pos=strmov(pos, p); + *pos++= ':'; + } + if (pos != buf) + pos--; // Remove last ':' + *pos=0; + net_store_data(&packet2, buf); } + else + net_store_data(&packet2, ""); + break; + +#endif /* HAVE_OPENSSL */ + case SHOW_UNDEF: // Show never happen + case SHOW_SYS: + net_store_data(&packet2, ""); // Safety + break; } if (my_net_write(&thd->net, (char*) packet2.ptr(),packet2.length())) goto err; /* purecov: inspected */ @@ -1210,6 +1422,6 @@ int mysqld_show(THD *thd, const char *wild, show_var_st *variables) } #ifdef __GNUC__ -template class List_iterator<char>; +template class List_iterator_fast<char>; template class List<char>; #endif diff --git a/sql/sql_sort.h b/sql/sql_sort.h new file mode 100644 index 00000000000..62c5f1cb164 --- /dev/null +++ b/sql/sql_sort.h @@ -0,0 +1,55 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* Defines used by filesort and uniques */ + +#define MERGEBUFF 7 +#define MERGEBUFF2 15 + +typedef struct st_buffpek { /* Struktur om sorteringsbuffrarna */ + my_off_t file_pos; /* Where we are in the sort file */ + uchar *base,*key; /* key pointers */ + ha_rows count; /* Number of rows in table */ + ulong mem_count; /* numbers of keys in memory */ + ulong max_keys; /* Max keys in buffert */ +} BUFFPEK; + + +typedef struct st_sort_param { + uint sort_length; /* Length of sort columns */ + uint keys; /* Max keys / buffert */ + uint ref_length; /* Length of record ref. */ + ha_rows max_rows,examined_rows; + TABLE *sort_form; /* For quicker make_sortkey */ + SORT_FIELD *local_sortorder; + SORT_FIELD *end; + uchar *unique_buff; + bool not_killable; +#ifdef USE_STRCOLL + char* tmp_buffer; +#endif +} SORTPARAM; + + +int merge_many_buff(SORTPARAM *param, uchar *sort_buffer, + BUFFPEK *buffpek, + uint *maxbuffer, IO_CACHE *t_file); +uint read_to_buffer(IO_CACHE *fromfile,BUFFPEK *buffpek, + uint sort_length); +int merge_buffers(SORTPARAM *param,IO_CACHE *from_file, + IO_CACHE *to_file, uchar *sort_buffer, + BUFFPEK *lastbuff,BUFFPEK *Fb, + BUFFPEK *Tb,int flag); diff --git a/sql/sql_string.cc b/sql/sql_string.cc index e6cdd089bf1..2dcda2d40c2 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -1,19 +1,18 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - - This program file 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, +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - 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 */ + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* This file is originally from the mysql distribution. Coded by monty */ @@ -21,7 +20,7 @@ #pragma implementation // gcc: Class implementation #endif -#include <global.h> +#include <my_global.h> #include <my_sys.h> #include <m_string.h> #include <m_ctype.h> @@ -41,19 +40,16 @@ extern void sql_element_free(void *ptr); bool String::real_alloc(uint32 arg_length) { arg_length=ALIGN_SIZE(arg_length+1); + str_length=0; if (Alloced_length < arg_length) { free(); if (!(Ptr=(char*) my_malloc(arg_length,MYF(MY_WME)))) - { - str_length=0; return TRUE; - } Alloced_length=arg_length; alloced=1; } Ptr[0]=0; - str_length=0; return FALSE; } @@ -362,6 +358,37 @@ skipp: return -1; } +/* + Search after a string without regarding to case + This needs to be replaced when we have character sets per string +*/ + +int String::strstr_case(const String &s,uint32 offset) +{ + if (s.length()+offset <= str_length) + { + if (!s.length()) + return ((int) offset); // Empty string is always found + + register const char *str = Ptr+offset; + register const char *search=s.ptr(); + const char *end=Ptr+str_length-s.length()+1; + const char *search_end=s.ptr()+s.length(); +skipp: + while (str != end) + { + if (my_sort_order[*str++] == my_sort_order[*search]) + { + register char *i,*j; + i=(char*) str; j=(char*) search+1; + while (j != search_end) + if (my_sort_order[*i++] != my_sort_order[*j++]) goto skipp; + return (int) (str-Ptr) -1; + } + } + } + return -1; +} /* ** Search string from end. Offset is offset to the end of string @@ -577,7 +604,7 @@ int wild_case_compare(const char *str,const char *str_end, { do { - if (str == str_end) // Skipp one char if possible + if (str == str_end) // Skip one char if possible return (result); INC_PTR(str,str_end); } while (++wildstr < wildend && *wildstr == wild_one); @@ -588,7 +615,7 @@ int wild_case_compare(const char *str,const char *str_end, { // Found wild_many wildstr++; /* Remove any '%' and '_' from the wild search string */ - for ( ; wildstr != wildend ; wildstr++) + for (; wildstr != wildend ; wildstr++) { if (*wildstr == wild_many) continue; @@ -668,8 +695,11 @@ int wild_case_compare(const char *str,const char *str_end, int wild_case_compare(String &match,String &wild, char escape) { - return wild_case_compare(match.ptr(),match.ptr()+match.length(), - wild.ptr(), wild.ptr()+wild.length(),escape); + DBUG_ENTER("wild_case_compare"); + DBUG_PRINT("enter",("match='%s', wild='%s', escape='%c'" + ,match.ptr(),wild.ptr(),escape)); + DBUG_RETURN(wild_case_compare(match.ptr(),match.ptr()+match.length(), + wild.ptr(), wild.ptr()+wild.length(),escape)); } /* @@ -679,6 +709,9 @@ int wild_case_compare(String &match,String &wild, char escape) int wild_compare(const char *str,const char *str_end, const char *wildstr,const char *wildend,char escape) { + DBUG_ENTER("wild_compare"); + DBUG_PRINT("enter",("str='%s', str_end='%s', wildstr='%s', wildend='%s', escape='%c'" + ,str,str_end,wildstr,wildend,escape)); int result= -1; // Not found, using wildcards while (wildstr != wildend) { @@ -687,17 +720,21 @@ int wild_compare(const char *str,const char *str_end, if (*wildstr == escape && wildstr+1 != wildend) wildstr++; if (str == str_end || *wildstr++ != *str++) - return(1); + { + DBUG_RETURN(1); + } if (wildstr == wildend) - return (str != str_end); // Match if both are at end + { + DBUG_RETURN(str != str_end); // Match if both are at end + } result=1; // Found an anchor char } if (*wildstr == wild_one) { do { - if (str == str_end) // Skipp one char if possible - return (result); + if (str == str_end) // Skip one char if possible + DBUG_RETURN(result); str++; } while (*++wildstr == wild_one && wildstr != wildend); if (wildstr == wildend) @@ -707,24 +744,29 @@ int wild_compare(const char *str,const char *str_end, { // Found wild_many wildstr++; /* Remove any '%' and '_' from the wild search string */ - for ( ; wildstr != wildend ; wildstr++) + for (; wildstr != wildend ; wildstr++) { if (*wildstr == wild_many) continue; if (*wildstr == wild_one) { if (str == str_end) - return (-1); + { + DBUG_RETURN(-1); + } str++; continue; } break; // Not a wild character } if (wildstr == wildend) - return(0); // Ok if wild_many is last + { + DBUG_RETURN(0); // Ok if wild_many is last + } if (str == str_end) - return -1; - + { + DBUG_RETURN(-1); + } char cmp; if ((cmp= *wildstr) == escape && wildstr+1 != wildend) cmp= *++wildstr; @@ -733,22 +775,30 @@ int wild_compare(const char *str,const char *str_end, { while (str != str_end && *str != cmp) str++; - if (str++ == str_end) return (-1); + if (str++ == str_end) + { + DBUG_RETURN(-1); + } { int tmp=wild_compare(str,str_end,wildstr,wildend,escape); if (tmp <= 0) - return (tmp); + { + DBUG_RETURN(tmp); + } } } while (str != str_end && wildstr[0] != wild_many); - return(-1); + DBUG_RETURN(-1); } } - return (str != str_end ? 1 : 0); + DBUG_RETURN(str != str_end ? 1 : 0); } int wild_compare(String &match,String &wild, char escape) { - return wild_compare(match.ptr(),match.ptr()+match.length(), - wild.ptr(), wild.ptr()+wild.length(),escape); + DBUG_ENTER("wild_compare"); + DBUG_PRINT("enter",("match='%s', wild='%s', escape='%c'" + ,match.ptr(),wild.ptr(),escape)); + DBUG_RETURN(wild_compare(match.ptr(),match.ptr()+match.length(), + wild.ptr(), wild.ptr()+wild.length(),escape)); } diff --git a/sql/sql_string.h b/sql/sql_string.h index 31dea9991cc..ad7455ecbf1 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -1,19 +1,18 @@ -/* 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, +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - 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 */ + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* This file is originally from the mysql distribution. Coded by monty */ @@ -161,6 +160,7 @@ public: bool append(const char *s,uint32 arg_length=0); bool append(IO_CACHE* file, uint32 arg_length); int strstr(const String &search,uint32 offset=0); // Returns offset to substring or -1 + int strstr_case(const String &s,uint32 offset=0); int strrstr(const String &search,uint32 offset=0); // Returns offset to substring or -1 bool replace(uint32 offset,uint32 arg_length,const String &to); inline bool append(char chr) diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 2ff7c9c1a75..156d2b842f1 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2003 MySQL AB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,17 +18,19 @@ /* drop and alter of tables */ #include "mysql_priv.h" -#include <hash.h> #ifdef HAVE_BERKELEY_DB -#include <ha_berkeley.h> +#include "ha_berkeley.h" #endif +#include <hash.h> #include <myisam.h> +#include <assert.h> #ifdef __WIN__ #include <io.h> #endif extern HASH open_cache; +static const char *primary_key_name="PRIMARY"; static bool check_if_keyname_exists(const char *name,KEY *start, KEY *end); static char *make_unique_key_name(const char *field_name,KEY *start,KEY *end); @@ -38,20 +40,32 @@ static int copy_data_between_tables(TABLE *from,TABLE *to, ORDER *order, ha_rows *copied,ha_rows *deleted); -/***************************************************************************** -** Remove all possbile tables and give a compact errormessage for all -** wrong tables. -** This will wait for all users to free the table before dropping it -*****************************************************************************/ +/* + delete (drop) tables. + + SYNOPSIS + mysql_rm_table() + thd Thread handle + tables List of tables to delete + if_exists If 1, don't give error if one table doesn't exists + + NOTES + Will delete all tables that can be deleted and give a compact error + messages for tables that could not be deleted. + If a table is in use, we will wait for all users to free the table + before dropping it + + Wait if global_read_lock (FLUSH TABLES WITH READ LOCK) is set. + + RETURN + 0 ok. In this case ok packet is sent to user + -1 Error (Error message given but not sent to user) + +*/ int mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists) { - char path[FN_REFLEN]; - String wrong_tables; - bool some_tables_deleted=0; - uint error= 1; - db_type table_type; - TABLE_LIST *table; + int error; DBUG_ENTER("mysql_rm_table"); /* mark for close and remove all cached entries */ @@ -74,16 +88,96 @@ int mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists) } } - + error=mysql_rm_table_part2(thd,tables,if_exists,0); + + err: + pthread_mutex_unlock(&LOCK_open); + + pthread_mutex_lock(&thd->mysys_var->mutex); + thd->mysys_var->current_mutex= 0; + thd->mysys_var->current_cond= 0; + pthread_mutex_unlock(&thd->mysys_var->mutex); + + if (error) + DBUG_RETURN(-1); + send_ok(&thd->net); + DBUG_RETURN(0); +} + + +/* + delete (drop) tables. + + SYNOPSIS + mysql_rm_table_part2_with_lock() + thd Thread handle + tables List of tables to delete + if_exists If 1, don't give error if one table doesn't exists + dont_log_query Don't write query to log files + + NOTES + Works like documented in mysql_rm_table(), but don't check + global_read_lock and don't send_ok packet to server. + + RETURN + 0 ok + 1 error +*/ + +int mysql_rm_table_part2_with_lock(THD *thd, + TABLE_LIST *tables, bool if_exists, + bool dont_log_query) +{ + int error; + thd->mysys_var->current_mutex= &LOCK_open; + thd->mysys_var->current_cond= &COND_refresh; + VOID(pthread_mutex_lock(&LOCK_open)); + + error=mysql_rm_table_part2(thd,tables, if_exists, dont_log_query); + + pthread_mutex_unlock(&LOCK_open); + + pthread_mutex_lock(&thd->mysys_var->mutex); + thd->mysys_var->current_mutex= 0; + thd->mysys_var->current_cond= 0; + pthread_mutex_unlock(&thd->mysys_var->mutex); + return error; +} + + +/* + TODO: + When logging to the binary log, we should log + tmp_tables and transactional tables as separate statements if we + are in a transaction; This is needed to get these tables into the + cached binary log that is only written on COMMIT. + + The current code only writes DROP statements that only uses temporary + tables to the cache binary log. This should be ok on most cases, but + not all. +*/ + +int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, + bool dont_log_query) +{ + TABLE_LIST *table; + char path[FN_REFLEN]; + String wrong_tables; + db_type table_type; + int error; + bool some_tables_deleted=0, tmp_table_deleted=0; + DBUG_ENTER("mysql_rm_table_part2"); + if (lock_table_names(thd, tables)) - goto err; + DBUG_RETURN(1); for (table=tables ; table ; table=table->next) { - char *db=table->db ? table->db : (thd->db ? thd->db : (char*) ""); + char *db=table->db ? table->db : thd->db; + mysql_ha_closeall(thd, table); if (!close_temporary_table(thd, db, table->real_name)) { - some_tables_deleted=1; // Log query + tmp_table_deleted=1; continue; // removed temporary table } @@ -96,18 +190,11 @@ int mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists) } drop_locked_tables(thd,db,table->real_name); if (thd->killed) - { - VOID(pthread_cond_broadcast(&COND_refresh)); // Signal to refresh - VOID(pthread_mutex_unlock(&LOCK_open)); - pthread_mutex_lock(&thd->mysys_var->mutex); - thd->mysys_var->current_mutex= 0; - thd->mysys_var->current_cond= 0; - pthread_mutex_unlock(&thd->mysys_var->mutex); DBUG_RETURN(-1); - } + /* remove form file and isam files */ - (void) sprintf(path,"%s/%s/%s%s",mysql_data_home,db,table->real_name, - reg_ext); + strxmov(path, mysql_data_home, "/", db, "/", table->real_name, reg_ext, + NullS); (void) unpack_filename(path,path); error=0; @@ -140,36 +227,29 @@ int mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists) wrong_tables.append(String(table->real_name)); } } - if (some_tables_deleted) + if (some_tables_deleted || tmp_table_deleted) { - mysql_update_log.write(thd, thd->query,thd->query_length); - if (mysql_bin_log.is_open()) + query_cache_invalidate3(thd, tables, 0); + if (!dont_log_query) { - Query_log_event qinfo(thd, thd->query); - mysql_bin_log.write(&qinfo); + mysql_update_log.write(thd, thd->query,thd->query_length); + if (mysql_bin_log.is_open()) + { + Query_log_event qinfo(thd, thd->query, thd->query_length, + tmp_table_deleted && !some_tables_deleted); + mysql_bin_log.write(&qinfo); + } } } - - error = 0; - unlock_table_names(thd, tables); - - err: - pthread_mutex_unlock(&LOCK_open); - - pthread_mutex_lock(&thd->mysys_var->mutex); - thd->mysys_var->current_mutex= 0; - thd->mysys_var->current_cond= 0; - pthread_mutex_unlock(&thd->mysys_var->mutex); + unlock_table_names(thd, tables); + error= 0; if (wrong_tables.length()) { my_error(ER_BAD_TABLE_ERROR,MYF(0),wrong_tables.c_ptr()); - error=1; + error= 1; } - if (error) - DBUG_RETURN(-1); - send_ok(&thd->net); - DBUG_RETURN(0); + DBUG_RETURN(error); } @@ -187,10 +267,79 @@ int quick_rm_table(enum db_type base,const char *db, return ha_delete_table(base,path) || error; } -/***************************************************************************** - * Create a table. - * If one creates a temporary table, this is automaticly opened - ****************************************************************************/ +/* + Sort keys in the following order: + - PRIMARY KEY + - UNIQUE keyws where all column are NOT NULL + - Other UNIQUE keys + - Normal keys + - Fulltext keys + + This will make checking for duplicated keys faster and ensure that + PRIMARY keys are prioritized. +*/ + + +static int sort_keys(KEY *a, KEY *b) +{ + if (a->flags & HA_NOSAME) + { + if (!(b->flags & HA_NOSAME)) + return -1; + if ((a->flags ^ b->flags) & HA_NULL_PART_KEY) + { + /* Sort NOT NULL keys before other keys */ + return (a->flags & HA_NULL_PART_KEY) ? 1 : -1; + } + if (a->name == primary_key_name) + return -1; + if (b->name == primary_key_name) + return 1; + } + else if (b->flags & HA_NOSAME) + return 1; // Prefer b + + if ((a->flags ^ b->flags) & HA_FULLTEXT) + { + return (a->flags & HA_FULLTEXT) ? 1 : -1; + } + /* + Prefer original key order. usable_key_parts contains here + the original key position. + */ + return ((a->usable_key_parts < b->usable_key_parts) ? -1 : + (a->usable_key_parts > b->usable_key_parts) ? 1 : + 0); +} + + +/* + Create a table + + SYNOPSIS + mysql_create_table() + thd Thread object + db Database + table_name Table name + create_info Create information (like MAX_ROWS) + fields List of fields to create + keys List of keys to create + tmp_table Set to 1 if this is an internal temporary table + (From ALTER TABLE) + no_log Don't log the query to binary log. + + DESCRIPTION + If one creates a temporary table, this is automaticly opened + + no_log is needed for the case of CREATE ... SELECT, + as the logging will be done later in sql_insert.cc + select_field_count is also used for CREATE ... SELECT, + and must be zero for standard create of table. + + RETURN VALUES + 0 ok + -1 error +*/ int mysql_create_table(THD *thd,const char *db, const char *table_name, HA_CREATE_INFO *create_info, @@ -226,7 +375,7 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, file=get_new_handler((TABLE*) 0, create_info->db_type); if ((create_info->options & HA_LEX_CREATE_TMP_TABLE) && - (file->option_flag() & HA_NO_TEMP_TABLES)) + (file->table_flags() & HA_NO_TEMP_TABLES)) { my_error(ER_ILLEGAL_HA,MYF(0),table_name); DBUG_RETURN(-1); @@ -325,13 +474,13 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, DBUG_RETURN(-1); } if (auto_increment && - (file->option_flag() & HA_WRONG_ASCII_ORDER)) + (file->table_flags() & HA_NO_AUTO_INCREMENT)) { my_error(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT,MYF(0)); DBUG_RETURN(-1); } - if (blob_columns && (file->option_flag() & HA_NO_BLOBS)) + if (blob_columns && (file->table_flags() & HA_NO_BLOBS)) { my_error(ER_TABLE_CANT_HANDLE_BLOB,MYF(0)); DBUG_RETURN(-1); @@ -342,10 +491,9 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, List_iterator<Key> key_iterator(keys); uint key_parts=0,key_count=keys.elements; List<Key> keys_in_order; // Add new keys here - Key *primary_key=0; - bool unique_key=0; + bool primary_key=0,unique_key=0; Key *key; - uint tmp; + uint tmp, key_number; tmp=min(file->max_keys(), MAX_KEY); if (key_count > tmp) { @@ -353,12 +501,7 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, DBUG_RETURN(-1); } - /* - Check keys; - Put PRIMARY KEY first, then UNIQUE keys and other keys last - This will make checking for duplicated keys faster and ensure that - primary keys are prioritized. - */ + /* Calculate number of key segements */ while ((key=key_iterator++)) { @@ -374,33 +517,6 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, DBUG_RETURN(-1); } key_parts+=key->columns.elements; - if (key->type == Key::PRIMARY) - { - if (primary_key) - { - my_error(ER_MULTIPLE_PRI_KEY,MYF(0)); - DBUG_RETURN(-1); - } - primary_key=key; - } - else if (key->type == Key::UNIQUE) - { - unique_key=1; - if (keys_in_order.push_front(key)) - DBUG_RETURN(-1); - } - else if (keys_in_order.push_back(key)) - DBUG_RETURN(-1); - } - if (primary_key) - { - if (keys_in_order.push_front(primary_key)) - DBUG_RETURN(-1); - } - else if (!unique_key && (file->option_flag() & HA_REQUIRE_PRIMARY_KEY)) - { - my_error(ER_REQUIRES_PRIMARY_KEY,MYF(0)); - DBUG_RETURN(-1); } key_info_buffer=key_info=(KEY*) sql_calloc(sizeof(KEY)*key_count); @@ -408,8 +524,9 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, if (!key_info_buffer || ! key_part_info) DBUG_RETURN(-1); // Out of memory - List_iterator<Key> key_iterator_in_order(keys_in_order); - for (; (key=key_iterator_in_order++) ; key_info++) + key_iterator.rewind(); + key_number=0; + for (; (key=key_iterator++) ; key_info++, key_number++) { uint key_length=0; key_part_spec *column; @@ -418,10 +535,13 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, (key->type == Key::FULLTEXT) ? HA_FULLTEXT : HA_NOSAME; key_info->key_parts=(uint8) key->columns.elements; key_info->key_part=key_part_info; + key_info->usable_key_parts= key_number; + key_info->algorithm=key->algorithm; + /* TODO: Add proper checks if handler supports key_type and algorithm */ if (key->type == Key::FULLTEXT) { - if (file->option_flag() & HA_NO_FULLTEXT_KEY) + if (!(file->table_flags() & HA_CAN_FULLTEXT)) { my_error(ER_TABLE_CANT_HANDLE_FULLTEXT, MYF(0)); DBUG_RETURN(-1); @@ -445,7 +565,7 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, } if (f_is_blob(sql_field->pack_flag)) { - if (!(file->option_flag() & HA_BLOB_KEY)) + if (!(file->table_flags() & HA_BLOB_KEY)) { my_printf_error(ER_BLOB_USED_AS_KEY,ER(ER_BLOB_USED_AS_KEY),MYF(0), column->field_name); @@ -471,16 +591,17 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, my_error(ER_PRIMARY_CANT_HAVE_NULL, MYF(0)); DBUG_RETURN(-1); } - if (!(file->option_flag() & HA_NULL_KEY)) + if (!(file->table_flags() & HA_NULL_KEY)) { my_printf_error(ER_NULL_COLUMN_IN_INDEX,ER(ER_NULL_COLUMN_IN_INDEX), MYF(0),column->field_name); DBUG_RETURN(-1); } + key_info->flags|= HA_NULL_PART_KEY; } if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER) { - if (column_nr == 0 || (file->option_flag() & HA_AUTO_PART_KEY)) + if (column_nr == 0 || (file->table_flags() & HA_AUTO_PART_KEY)) auto_increment--; // Field is used } key_part_info->fieldnr= field; @@ -500,14 +621,14 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, } else if (column->length > length || ((f_is_packed(sql_field->pack_flag) || - ((file->option_flag() & HA_NO_PREFIX_CHAR_KEYS) && + ((file->table_flags() & HA_NO_PREFIX_CHAR_KEYS) && (key_info->flags & HA_NOSAME))) && column->length != length)) { my_error(ER_WRONG_SUB_KEY,MYF(0)); DBUG_RETURN(-1); } - if (!(file->option_flag() & HA_NO_PREFIX_CHAR_KEYS)) + if (!(file->table_flags() & HA_NO_PREFIX_CHAR_KEYS)) length=column->length; } else if (length == 0) @@ -536,7 +657,15 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, if (column_nr == 0) { if (key->type == Key::PRIMARY) - key_name="PRIMARY"; + { + if (primary_key) + { + my_error(ER_MULTIPLE_PRI_KEY,MYF(0)); + DBUG_RETURN(-1); + } + key_name=primary_key_name; + primary_key=1; + } else if (!(key_name = key->name())) key_name=make_unique_key_name(sql_field->field_name, key_info_buffer,key_info); @@ -548,18 +677,29 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, key_info->name=(char*) key_name; } } + if (!(key_info->flags & HA_NULL_PART_KEY)) + unique_key=1; key_info->key_length=(uint16) key_length; - if (key_length > file->max_key_length() && key->type != Key::FULLTEXT) + uint max_key_length= min(file->max_key_length(), MAX_KEY_LENGTH); + if (key_length > max_key_length && key->type != Key::FULLTEXT) { - my_error(ER_TOO_LONG_KEY,MYF(0),file->max_key_length()); + my_error(ER_TOO_LONG_KEY,MYF(0),max_key_length); DBUG_RETURN(-1); } } + if (!unique_key && !primary_key && + (file->table_flags() & HA_REQUIRE_PRIMARY_KEY)) + { + my_error(ER_REQUIRES_PRIMARY_KEY,MYF(0)); + DBUG_RETURN(-1); + } if (auto_increment > 0) { my_error(ER_WRONG_AUTO_KEY,MYF(0)); DBUG_RETURN(-1); } + /* Sort keys in optimized order */ + qsort((gptr) key_info_buffer, key_count, sizeof(KEY), (qsort_cmp) sort_keys); /* Check if table exists */ if (create_info->options & HA_LEX_CREATE_TMP_TABLE) @@ -595,7 +735,6 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, thd->proc_info="creating table"; - create_info->create_statement = thd->query; create_info->table_options=db_options; if (rea_create_table(path, create_info, fields, key_count, key_info_buffer)) @@ -603,16 +742,6 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, /* my_error(ER_CANT_CREATE_TABLE,MYF(0),table_name,my_errno); */ goto end; } - if (!tmp_table && !no_log) - { - // Must be written before unlock - mysql_update_log.write(thd,thd->query, thd->query_length); - if (mysql_bin_log.is_open()) - { - Query_log_event qinfo(thd, thd->query); - mysql_bin_log.write(&qinfo); - } - } if (create_info->options & HA_LEX_CREATE_TMP_TABLE) { /* Open table and put in temporary table list */ @@ -622,6 +751,18 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, goto end; } } + if (!tmp_table && !no_log) + { + // Must be written before unlock + mysql_update_log.write(thd,thd->query, thd->query_length); + if (mysql_bin_log.is_open()) + { + Query_log_event qinfo(thd, thd->query, thd->query_length, + test(create_info->options & + HA_LEX_CREATE_TMP_TABLE)); + mysql_bin_log.write(&qinfo); + } + } error=0; end: VOID(pthread_mutex_unlock(&LOCK_open)); @@ -676,7 +817,7 @@ TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, DBUG_ENTER("create_table_from_items"); /* Add selected items to field list */ - List_iterator<Item> it(*items); + List_iterator_fast<Item> it(*items); Item *item; Field *tmp_field; tmp_table.db_create_options=0; @@ -694,9 +835,12 @@ TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info, my_error(ER_WRONG_COLUMN_NAME,MYF(0),item->name); DBUG_RETURN(0); } - - Field *field=create_tmp_field(&tmp_table,item,item->type(), - (Item_result_field***) 0, &tmp_field,0,0); + Field *field; + if (item->type() == Item::FUNC_ITEM) + field=item->tmp_table_field(&tmp_table); + else + field=create_tmp_field(thd, &tmp_table, item, item->type(), + (Item ***) 0, &tmp_field,0,0); if (!field || !(cr_field=new create_field(field,(item->type() == Item::FIELD_ITEM ? ((Item_field *)item)->field : @@ -772,9 +916,11 @@ bool close_cached_table(THD *thd,TABLE *table) { bool result=0; DBUG_ENTER("close_cached_table"); + safe_mutex_assert_owner(&LOCK_open); + if (table) { - DBUG_PRINT("enter",("table: %s", table->table_name)); + DBUG_PRINT("enter",("table: %s", table->real_name)); VOID(table->file->extra(HA_EXTRA_FORCE_REOPEN)); // Close all data files /* Mark all tables that are in use as 'old' */ mysql_lock_abort(thd,table); // end threads waiting on lock @@ -824,7 +970,9 @@ static int send_check_errmsg(THD* thd, TABLE_LIST* table, return 1; } -static int prepare_for_restore(THD* thd, TABLE_LIST* table) + +static int prepare_for_restore(THD* thd, TABLE_LIST* table, + HA_CHECK_OPT *check_opt) { DBUG_ENTER("prepare_for_restore"); @@ -841,65 +989,123 @@ static int prepare_for_restore(THD* thd, TABLE_LIST* table) char* table_name = table->real_name; char* db = thd->db ? thd->db : table->db; - if (!fn_format(src_path, table_name, backup_dir, reg_ext, 4 + 64)) + if (fn_format_relative_to_data_home(src_path, table_name, backup_dir, + reg_ext)) DBUG_RETURN(-1); // protect buffer overflow sprintf(dst_path, "%s/%s/%s", mysql_real_data_home, db, table_name); - int lock_retcode; - pthread_mutex_lock(&LOCK_open); - if ((lock_retcode = lock_table_name(thd, table)) < 0) + if (lock_and_wait_for_table_name(thd,table)) + DBUG_RETURN(-1); + + if (my_copy(src_path, + fn_format(dst_path, dst_path,"", reg_ext, 4), + MYF(MY_WME))) { + pthread_mutex_lock(&LOCK_open); + unlock_table_name(thd, table); pthread_mutex_unlock(&LOCK_open); - DBUG_RETURN(-1); + DBUG_RETURN(send_check_errmsg(thd, table, "restore", + "Failed copying .frm file")); } - - if (lock_retcode && wait_for_locked_table_names(thd, table)) + if (mysql_truncate(thd, table, 1)) { + pthread_mutex_lock(&LOCK_open); unlock_table_name(thd, table); pthread_mutex_unlock(&LOCK_open); - DBUG_RETURN(-1); + DBUG_RETURN(send_check_errmsg(thd, table, "restore", + "Failed generating table from .frm file")); } + } + + /* + Now we should be able to open the partially restored table + to finish the restore in the handler later on + */ + if (!(table->table = reopen_name_locked_table(thd, table))) + { + pthread_mutex_lock(&LOCK_open); + unlock_table_name(thd, table); pthread_mutex_unlock(&LOCK_open); + } + DBUG_RETURN(0); +} - if (my_copy(src_path, - fn_format(dst_path, dst_path,"", - reg_ext, 4), - MYF(MY_WME))) + +static int prepare_for_repair(THD* thd, TABLE_LIST* table, + HA_CHECK_OPT *check_opt) +{ + DBUG_ENTER("prepare_for_repair"); + + if (!(check_opt->sql_flags & TT_USEFRM)) + { + DBUG_RETURN(0); + } + else + { + + char from[FN_REFLEN],tmp[FN_REFLEN]; + char* db = thd->db ? thd->db : table->db; + + sprintf(from, "%s/%s/%s", mysql_real_data_home, db, table->real_name); + fn_format(from, from, "", MI_NAME_DEXT, 4); + sprintf(tmp,"%s-%lx_%lx", from, current_pid, thd->thread_id); + + pthread_mutex_lock(&LOCK_open); + close_cached_table(thd,table->table); + pthread_mutex_unlock(&LOCK_open); + + if (lock_and_wait_for_table_name(thd,table)) + DBUG_RETURN(-1); + + if (my_rename(from, tmp, MYF(MY_WME))) { pthread_mutex_lock(&LOCK_open); unlock_table_name(thd, table); pthread_mutex_unlock(&LOCK_open); - DBUG_RETURN(send_check_errmsg(thd, table, "restore", - "Failed copying .frm file")); + DBUG_RETURN(send_check_errmsg(thd, table, "repair", + "Failed renaming .MYD file")); } - bool save_no_send_ok = thd->net.no_send_ok; - thd->net.no_send_ok = 1; - // generate table will try to send OK which messes up the output - // for the client - - if (generate_table(thd, table, 0)) + if (mysql_truncate(thd, table, 1)) { pthread_mutex_lock(&LOCK_open); unlock_table_name(thd, table); pthread_mutex_unlock(&LOCK_open); - thd->net.no_send_ok = save_no_send_ok; - DBUG_RETURN(send_check_errmsg(thd, table, "restore", + DBUG_RETURN(send_check_errmsg(thd, table, "repair", "Failed generating table from .frm file")); } - - thd->net.no_send_ok = save_no_send_ok; + if (my_rename(tmp, from, MYF(MY_WME))) + { + pthread_mutex_lock(&LOCK_open); + unlock_table_name(thd, table); + pthread_mutex_unlock(&LOCK_open); + DBUG_RETURN(send_check_errmsg(thd, table, "repair", + "Failed restoring .MYD file")); + } } + /* + Now we should be able to open the partially repaired table + to finish the repair in the handler later on. + */ + if (!(table->table = reopen_name_locked_table(thd, table))) + { + pthread_mutex_lock(&LOCK_open); + unlock_table_name(thd, table); + pthread_mutex_unlock(&LOCK_open); + } DBUG_RETURN(0); } + static int mysql_admin_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt, const char *operator_name, thr_lock_type lock_type, - bool open_for_modify, bool restore, + bool open_for_modify, uint extra_open_options, + int (*prepare_func)(THD *, TABLE_LIST *, + HA_CHECK_OPT *), int (handler::*operator_func) (THD *, HA_CHECK_OPT *)) { @@ -931,23 +1137,15 @@ static int mysql_admin_table(THD* thd, TABLE_LIST* tables, table->table = open_ltable(thd, table, lock_type); thd->open_options&= ~extra_open_options; packet->length(0); - if (restore) + if (prepare_func) { - switch (prepare_for_restore(thd, table)) { - case 1: continue; // error, message written to net - case -1: goto err; // error, message could be written to net - default: ;// should be 0 otherwise - } - - // now we should be able to open the partially restored table - // to finish the restore in the handler later on - if (!(table->table = reopen_name_locked_table(thd, table))) - { - pthread_mutex_lock(&LOCK_open); - unlock_table_name(thd, table); - pthread_mutex_unlock(&LOCK_open); + switch ((*prepare_func)(thd, table, check_opt)) { + case 1: continue; // error, message written to net + case -1: goto err; // error, message could be written to net + default: ; // should be 0 otherwise } } + if (!table->table) { const char *err_msg; @@ -972,6 +1170,7 @@ static int mysql_admin_table(THD* thd, TABLE_LIST* tables, sprintf(buff, ER(ER_OPEN_AS_READONLY), table_name); net_store_data(packet, buff); close_thread_tables(thd); + table->table=0; // For query cache if (my_net_write(&thd->net, (char*) thd->packet.ptr(), packet->length())) goto err; @@ -1007,8 +1206,13 @@ static int mysql_admin_table(THD* thd, TABLE_LIST* tables, switch (result_code) { case HA_ADMIN_NOT_IMPLEMENTED: - net_store_data(packet, "error"); - net_store_data(packet, ER(ER_CHECK_NOT_IMPLEMENTED)); + { + char buf[ERRMSGSIZE+20]; + my_snprintf(buf, ERRMSGSIZE, + ER(ER_CHECK_NOT_IMPLEMENTED), operator_name); + net_store_data(packet, "error"); + net_store_data(packet, buf); + } break; case HA_ADMIN_OK: @@ -1051,8 +1255,11 @@ static int mysql_admin_table(THD* thd, TABLE_LIST* tables, remove_table_from_cache(thd, table->table->table_cache_key, table->table->real_name); pthread_mutex_unlock(&LOCK_open); + /* May be something modified consequently we have to invalidate cache */ + query_cache_invalidate3(thd, table->table, 0); } close_thread_tables(thd); + table->table=0; // For query cache if (my_net_write(&thd->net, (char*) packet->ptr(), packet->length())) goto err; @@ -1062,9 +1269,12 @@ static int mysql_admin_table(THD* thd, TABLE_LIST* tables, DBUG_RETURN(0); err: close_thread_tables(thd); // Shouldn't be needed + if (table) + table->table=0; DBUG_RETURN(-1); } + int mysql_backup_table(THD* thd, TABLE_LIST* table_list) { DBUG_ENTER("mysql_backup_table"); @@ -1073,22 +1283,27 @@ int mysql_backup_table(THD* thd, TABLE_LIST* table_list) &handler::backup)); } + int mysql_restore_table(THD* thd, TABLE_LIST* table_list) { DBUG_ENTER("mysql_restore_table"); DBUG_RETURN(mysql_admin_table(thd, table_list, 0, - "restore", TL_WRITE, 1, 1,0, + "restore", TL_WRITE, 1, 0, + &prepare_for_restore, &handler::restore)); } + int mysql_repair_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt) { DBUG_ENTER("mysql_repair_table"); DBUG_RETURN(mysql_admin_table(thd, tables, check_opt, - "repair", TL_WRITE, 1, 0, HA_OPEN_FOR_REPAIR, + "repair", TL_WRITE, 1, HA_OPEN_FOR_REPAIR, + &prepare_for_repair, &handler::repair)); } + int mysql_optimize_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt) { DBUG_ENTER("mysql_optimize_table"); @@ -1124,7 +1339,7 @@ int mysql_check_table(THD* thd, TABLE_LIST* tables,HA_CHECK_OPT* check_opt) DBUG_ENTER("mysql_check_table"); DBUG_RETURN(mysql_admin_table(thd, tables, check_opt, "check", lock_type, - 0, 0, HA_OPEN_FOR_REPAIR, + 0, HA_OPEN_FOR_REPAIR, 0, &handler::check)); } @@ -1137,25 +1352,30 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, List<Alter_column> &alter_list, ORDER *order, bool drop_primary, - enum enum_duplicates handle_duplicates) + enum enum_duplicates handle_duplicates, + enum enum_enable_or_disable keys_onoff, + bool simple_alter) { TABLE *table,*new_table; int error; char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN], - *table_name,*db; + *table_name,*db; + char index_file[FN_REFLEN], data_file[FN_REFLEN]; bool use_timestamp=0; ha_rows copied,deleted; ulonglong next_insert_id; - uint save_time_stamp,db_create_options; + uint save_time_stamp,db_create_options, used_fields; enum db_type old_db_type,new_db_type; DBUG_ENTER("mysql_alter_table"); thd->proc_info="init"; table_name=table_list->real_name; db=table_list->db; - if (!new_db) + if (!new_db || !strcmp(new_db,db)) new_db=db; + used_fields=create_info->used_fields; + mysql_ha_closeall(thd, table_list); if (!(table=open_ltable(thd,table_list,TL_WRITE_ALLOW_READ))) DBUG_RETURN(-1); @@ -1200,53 +1420,61 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, if (create_info->db_type == DB_TYPE_DEFAULT) create_info->db_type=old_db_type; new_db_type=create_info->db_type= ha_checktype(create_info->db_type); - if (create_info->row_type == ROW_TYPE_DEFAULT) + if (create_info->row_type == ROW_TYPE_NOT_USED) create_info->row_type=table->row_type; - /* Check if the user only wants to do a simple RENAME */ + /* In some simple cases we need not to recreate the table */ thd->proc_info="setup"; - if (new_name != table_name && - !fields.elements && !keys.elements && ! drop_list.elements && - !alter_list.elements && !drop_primary && - new_db_type == old_db_type && create_info->max_rows == 0 && - create_info->auto_increment_value == 0 && !table->tmp_table) + if (simple_alter && !table->tmp_table) { - thd->proc_info="rename"; - VOID(pthread_mutex_lock(&LOCK_open)); - /* Then do a 'simple' rename of the table */ error=0; - if (!access(new_name_buff,F_OK)) + if (new_name != table_name || new_db != db) { - my_error(ER_TABLE_EXISTS_ERROR,MYF(0),new_name); - error= -1; - } - else - { - *fn_ext(new_name)=0; - close_cached_table(thd,table); - if (mysql_rename_table(old_db_type,db,table_name,new_db,new_name)) - error= -1; + thd->proc_info="rename"; + VOID(pthread_mutex_lock(&LOCK_open)); + /* Then do a 'simple' rename of the table */ + error=0; + if (!access(new_name_buff,F_OK)) + { + my_error(ER_TABLE_EXISTS_ERROR,MYF(0),new_name); + error= -1; + } + else + { + *fn_ext(new_name)=0; + close_cached_table(thd,table); + if (mysql_rename_table(old_db_type,db,table_name,new_db,new_name)) + error= -1; + } + VOID(pthread_cond_broadcast(&COND_refresh)); + VOID(pthread_mutex_unlock(&LOCK_open)); } - if (!error && (error=ha_commit_rename(thd))) + if (!error) { - my_error(ER_GET_ERRNO,MYF(0),error); - error=1; + switch (keys_onoff) { + case LEAVE_AS_IS: + break; + case ENABLE: + error=table->file->activate_all_index(thd); + break; + case DISABLE: + table->file->deactivate_non_unique_index(HA_POS_ERROR); + break; + } } - - VOID(pthread_cond_broadcast(&COND_refresh)); - VOID(pthread_mutex_unlock(&LOCK_open)); if (!error) { mysql_update_log.write(thd, thd->query, thd->query_length); if (mysql_bin_log.is_open()) { - Query_log_event qinfo(thd, thd->query); + Query_log_event qinfo(thd, thd->query, thd->query_length, 0); mysql_bin_log.write(&qinfo); } send_ok(&thd->net); } - + table_list->table=0; // For query cache + query_cache_invalidate3(thd, table_list, 0); DBUG_RETURN(error); } @@ -1276,7 +1504,7 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, { /* Reset auto_increment value if it was dropped */ if (MTYP_TYPENR(field->unireg_check) == Field::NEXT_NUMBER && - !(create_info->used_fields & HA_CREATE_USED_AUTO)) + !(used_fields & HA_CREATE_USED_AUTO)) { create_info->auto_increment_value=0; create_info->used_fields|=HA_CREATE_USED_AUTO; @@ -1301,8 +1529,11 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, def->field=field; if (def->sql_type == FIELD_TYPE_TIMESTAMP) use_timestamp=1; - create_list.push_back(def); - def_it.remove(); + if (!def->after) + { + create_list.push_back(def); + def_it.remove(); + } } else { // Use old field value @@ -1333,7 +1564,7 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, List_iterator<create_field> find_it(create_list); while ((def=def_it++)) // Add new columns { - if (def->change) + if (def->change && ! def->field) { my_error(ER_BAD_FIELD_ERROR,MYF(0),def->change,table_name); DBUG_RETURN(-1); @@ -1462,20 +1693,25 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, goto err; } + db_create_options=table->db_create_options & ~(HA_OPTION_PACK_RECORD); (void) sprintf(tmp_name,"%s-%lx_%lx", tmp_file_prefix, current_pid, thd->thread_id); create_info->db_type=new_db_type; - if (!create_info->max_rows) - create_info->max_rows=table->max_rows; - if (!create_info->avg_row_length) - create_info->avg_row_length=table->avg_row_length; - table->file->update_create_info(create_info); if (!create_info->comment) create_info->comment=table->comment; + /* let new create options override the old ones */ - db_create_options=table->db_create_options & ~(HA_OPTION_PACK_RECORD); - if (create_info->table_options & - (HA_OPTION_PACK_KEYS | HA_OPTION_NO_PACK_KEYS)) + if (!(used_fields & HA_CREATE_USED_MIN_ROWS)) + create_info->min_rows=table->min_rows; + if (!(used_fields & HA_CREATE_USED_MAX_ROWS)) + create_info->max_rows=table->max_rows; + if (!(used_fields & HA_CREATE_USED_AVG_ROW_LENGTH)) + create_info->avg_row_length=table->avg_row_length; + + table->file->update_create_info(create_info); + if ((create_info->table_options & + (HA_OPTION_PACK_KEYS | HA_OPTION_NO_PACK_KEYS)) || + (used_fields & HA_CREATE_USED_PACK_KEYS)) db_create_options&= ~(HA_OPTION_PACK_KEYS | HA_OPTION_NO_PACK_KEYS); if (create_info->table_options & (HA_OPTION_CHECKSUM | HA_OPTION_NO_CHECKSUM)) @@ -1489,6 +1725,53 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, if (table->tmp_table) create_info->options|=HA_LEX_CREATE_TMP_TABLE; + /* + Handling of symlinked tables: + If no rename: + Create new data file and index file on the same disk as the + old data and index files. + Copy data. + Rename new data file over old data file and new index file over + old index file. + Symlinks are not changed. + + If rename: + Create new data file and index file on the same disk as the + old data and index files. Create also symlinks to point at + the new tables. + Copy data. + At end, rename temporary tables and symlinks to temporary table + to final table name. + Remove old table and old symlinks + + If rename is made to another database: + Create new tables in new database. + Copy data. + Remove old table and symlinks. + */ + + if (!strcmp(db, new_db)) // Ignore symlink if db changed + { + if (create_info->index_file_name) + { + /* Fix index_file_name to have 'tmp_name' as basename */ + strmov(index_file, tmp_name); + create_info->index_file_name=fn_same(index_file, + create_info->index_file_name, + 1); + } + if (create_info->data_file_name) + { + /* Fix data_file_name to have 'tmp_name' as basename */ + strmov(data_file, tmp_name); + create_info->data_file_name=fn_same(data_file, + create_info->data_file_name, + 1); + } + } + else + create_info->data_file_name=create_info->index_file_name=0; + if ((error=mysql_create_table(thd, new_db, tmp_name, create_info, create_list,key_list,1,1))) // no logging @@ -1555,7 +1838,7 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, mysql_update_log.write(thd, thd->query,thd->query_length); if (mysql_bin_log.is_open()) { - Query_log_event qinfo(thd, thd->query); + Query_log_event qinfo(thd, thd->query, thd->query_length, 0); mysql_bin_log.write(&qinfo); } goto end_temporary; @@ -1579,7 +1862,7 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, thd->proc_info="rename result table"; sprintf(old_name,"%s2-%lx-%lx", tmp_file_prefix, current_pid, thd->thread_id); - if (new_name != table_name) + if (new_name != table_name || new_db != db) { if (!access(new_name_buff,F_OK)) { @@ -1597,7 +1880,7 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, { /* Win32 and InnoDB can't drop a table that is in use, so we must - close all the original table at before doing the rename + close the original table at before doing the rename */ table_name=thd->strdup(table_name); // must be saved if (close_cached_table(thd,table)) @@ -1630,24 +1913,30 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, } if (error) { - // This shouldn't happen. We solve this the safe way by - // closing the locked table. + /* + This shouldn't happen. We solve this the safe way by + closing the locked table. + */ close_cached_table(thd,table); VOID(pthread_mutex_unlock(&LOCK_open)); goto err; } if (thd->lock || new_name != table_name) // True if WIN32 { - // Not table locking or alter table with rename - // free locks and remove old table + /* + Not table locking or alter table with rename + free locks and remove old table + */ close_cached_table(thd,table); VOID(quick_rm_table(old_db_type,db,old_name)); } else { - // Using LOCK TABLES without rename. - // This code is never executed on WIN32! - // Remove old renamed table, reopen table and get new locks + /* + Using LOCK TABLES without rename. + This code is never executed on WIN32! + Remove old renamed table, reopen table and get new locks + */ if (table) { VOID(table->file->extra(HA_EXTRA_FORCE_REOPEN)); // Use new file @@ -1678,7 +1967,7 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, mysql_update_log.write(thd, thd->query,thd->query_length); if (mysql_bin_log.is_open()) { - Query_log_event qinfo(thd, thd->query); + Query_log_event qinfo(thd, thd->query, thd->query_length, 0); mysql_bin_log.write(&qinfo); } VOID(pthread_cond_broadcast(&COND_refresh)); @@ -1706,6 +1995,8 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name, (void) berkeley_flush_logs(); } #endif + table_list->table=0; // For query cache + query_cache_invalidate3(thd, table_list, 0); end_temporary: sprintf(tmp_name,ER(ER_INSERT_INFO),(ulong) (copied+deleted), @@ -1715,7 +2006,6 @@ end_temporary: DBUG_RETURN(0); err: - (void) ha_commit_rename(thd); // Just for safety DBUG_RETURN(-1); } @@ -1774,8 +2064,8 @@ copy_data_between_tables(TABLE *from,TABLE *to, if (setup_order(thd, &tables, fields, all_fields, order) || !(sortorder=make_unireg_sortorder(order, &length)) || - (from->found_records = filesort(&from, sortorder, length, - (SQL_SELECT *) 0, 0L, HA_POS_ERROR, + (from->found_records = filesort(from, sortorder, length, + (SQL_SELECT *) 0, 0L, HA_POS_ERROR, &examined_rows)) == HA_POS_ERROR) goto err; @@ -1825,7 +2115,7 @@ copy_data_between_tables(TABLE *from,TABLE *to, } end_read_record(&info); free_io_cache(from); - delete [] copy; + delete [] copy; // This is never 0 uint tmp_error; if ((tmp_error=to->file->extra(HA_EXTRA_NO_CACHE))) { diff --git a/sql/sql_test.cc b/sql/sql_test.cc index c4c2855a63e..b226bc1300a 100644 --- a/sql/sql_test.cc +++ b/sql/sql_test.cc @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -21,6 +21,7 @@ #include "mysql_priv.h" #include "sql_select.h" #include <hash.h> +#include <thr_alarm.h> /* Intern key cache variables */ extern "C" pthread_mutex_t THR_LOCK_keycache; @@ -96,8 +97,7 @@ void print_cached_tables(void) } -void TEST_filesort(TABLE **table,SORT_FIELD *sortorder,uint s_length, - ha_rows special) +void TEST_filesort(SORT_FIELD *sortorder,uint s_length, ha_rows special) { char buff[256],buff2[256]; String str(buff,sizeof(buff)),out(buff2,sizeof(buff2)); @@ -240,6 +240,18 @@ Open streams: %10lu\n", (ulong) cached_tables(), (ulong) my_file_opened, (ulong) my_stream_opened); + + ALARM_INFO alarm_info; +#ifndef DONT_USE_THR_ALARM + thr_alarm_info(&alarm_info); + printf("\nAlarm status:\n\ +Active alarms: %u\n\ +Max used alarms: %u\n\ +Next alarm time: %lu\n", + alarm_info.active_alarms, + alarm_info.max_used_alarms, + alarm_info.next_alarm_time); +#endif fflush(stdout); if (thd) thd->proc_info="malloc"; diff --git a/sql/sql_udf.cc b/sql/sql_udf.cc index 82a5e5bc002..8a1c19568ae 100644 --- a/sql/sql_udf.cc +++ b/sql/sql_udf.cc @@ -1,21 +1,19 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - +/* Copyright (C) 2000 MySQL AB + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - - /* This implements 'user defined functions' */ /* @@ -52,11 +50,11 @@ extern "C" { FreeLibrary((HMODULE)lib); } - + #elif !defined(OS2) #include <dlfcn.h> #endif - + #include <stdarg.h> #include <hash.h> } @@ -100,8 +98,8 @@ static void init_syms(udf_func *tmp) } } -static byte* get_hash_key(const byte *buff,uint *length, - my_bool not_used __attribute__((unused))) +extern "C" byte* get_hash_key(const byte *buff,uint *length, + my_bool not_used __attribute__((unused))) { udf_func *udf=(udf_func*) buff; *length=(uint) udf->name_length; @@ -134,21 +132,20 @@ void udf_init() sql_print_error("Can't allocate memory for udf structures"); hash_free(&udf_hash); free_root(&mem,MYF(0)); + delete new_thd; DBUG_VOID_RETURN; } initialized = 1; - new_thd->mysys_var=my_thread_var; - new_thd->version = refresh_version; //current_thd->version; - new_thd->current_tablenr = 0; - new_thd->open_tables = 0; - new_thd->db = my_strdup("mysql", MYF(0)); + new_thd->store_globals(); + new_thd->db= my_strdup("mysql", MYF(0)); + new_thd->db_length=5; bzero((gptr) &tables,sizeof(tables)); tables.alias= tables.real_name= (char*) "func"; tables.lock_type = TL_READ; tables.db=new_thd->db; - if (open_tables(new_thd, &tables)) + if (open_and_lock_tables(new_thd, &tables)) { DBUG_PRINT("error",("Can't open udf table")); sql_print_error("Can't open mysql/func table"); @@ -181,9 +178,10 @@ void udf_init() { if (!(dl = dlopen(tmp->dl, RTLD_NOW))) { + /* Print warning to log */ sql_print_error(ER(ER_CANT_OPEN_LIBRARY), tmp->dl,errno,dlerror()); - del_udf(tmp); + /* Keep the udf in the hash so that we can remove it later */ continue; } new_dl=1; @@ -217,19 +215,25 @@ void udf_free() for (uint idx=0 ; idx < udf_hash.records ; idx++) { udf_func *udf=(udf_func*) hash_element(&udf_hash,idx); - if (udf->dl) + if (udf->dlhandle) // Not closed before { + /* Mark all versions using the same handler as closed */ for (uint j=idx+1 ; j < udf_hash.records ; j++) { udf_func *tmp=(udf_func*) hash_element(&udf_hash,j); - if (tmp->dl && !strcmp(udf->dl,tmp->dl)) - tmp->dl=0; + if (udf->dlhandle == tmp->dlhandle) + tmp->dlhandle=0; // Already closed } + dlclose(udf->dlhandle); } - dlclose(udf->dlhandle); } hash_free(&udf_hash); free_root(&mem,MYF(0)); + if (initialized) + { + initialized= 0; + pthread_mutex_destroy(&THR_LOCK_udf); + } DBUG_VOID_RETURN; } @@ -245,9 +249,9 @@ static void del_udf(udf_func *udf) else { /* - ** The functions is in use ; Rename the functions instead of removing it. - ** The functions will be automaticly removed when the least threads - ** doesn't use it anymore + The functions is in use ; Rename the functions instead of removing it. + The functions will be automaticly removed when the least threads + doesn't use it anymore */ char *name= udf->name; uint name_length=udf->name_length; @@ -265,6 +269,10 @@ void free_udf(udf_func *udf) pthread_mutex_lock(&THR_LOCK_udf); if (!--udf->usage_count) { + /* + We come here when someone has deleted the udf function + while another thread still was using the udf + */ hash_delete(&udf_hash,(byte*) udf); using_udf_functions=udf_hash.records != 0; if (!find_udf_dl(udf->dl)) @@ -274,6 +282,7 @@ void free_udf(udf_func *udf) DBUG_VOID_RETURN; } + /* This is only called if using_udf_functions != 0 */ udf_func *find_udf(const char *name,uint length,bool mark_used) @@ -283,20 +292,26 @@ udf_func *find_udf(const char *name,uint length,bool mark_used) /* TODO: This should be changed to reader locks someday! */ pthread_mutex_lock(&THR_LOCK_udf); - udf=(udf_func*) hash_search(&udf_hash,(byte*) name, - length ? length : (uint) strlen(name)); - if (mark_used) - udf->usage_count++; + if ((udf=(udf_func*) hash_search(&udf_hash,(byte*) name, + length ? length : (uint) strlen(name)))) + { + if (!udf->dlhandle) + udf=0; // Could not be opened + else if (mark_used) + udf->usage_count++; + } pthread_mutex_unlock(&THR_LOCK_udf); DBUG_RETURN(udf); } + static void *find_udf_dl(const char *dl) { DBUG_ENTER("find_udf_dl"); - /* because only the function name is hashed, we have to search trough - ** all rows to find the dl. + /* + Because only the function name is hashed, we have to search trough + all rows to find the dl. */ for (uint idx=0 ; idx < udf_hash.records ; idx++) { @@ -313,7 +328,7 @@ static void *find_udf_dl(const char *dl) static udf_func *add_udf(char *name, Item_result ret, char *dl, Item_udftype type) { - if (!name || !dl) + if (!name || !dl || !(uint) type || (uint) type > (uint) UDFTYPE_AGGREGATE) return 0; udf_func *tmp= (udf_func*) alloc_root(&mem, sizeof(udf_func)); if (!tmp) @@ -365,7 +380,7 @@ int mysql_create_function(THD *thd,udf_func *udf) } pthread_mutex_lock(&THR_LOCK_udf); - if (hash_search(&udf_hash,(byte*) udf->name, udf->name_length)) + if ((hash_search(&udf_hash,(byte*) udf->name, udf->name_length))) { net_printf(&thd->net, ER_UDF_EXISTS, udf->name); goto err; @@ -391,8 +406,7 @@ int mysql_create_function(THD *thd,udf_func *udf) } udf->name=strdup_root(&mem,udf->name); udf->dl=strdup_root(&mem,udf->dl); - if (!udf->name || !udf->dl || - !(u_d=add_udf(udf->name,udf->returns,udf->dl,udf->type))) + if (!(u_d=add_udf(udf->name,udf->returns,udf->dl,udf->type))) { send_error(&thd->net,0); // End of memory goto err; @@ -451,13 +465,18 @@ int mysql_drop_function(THD *thd,const char *udf_name) DBUG_RETURN(1); } pthread_mutex_lock(&THR_LOCK_udf); - if (!(udf=(udf_func*) hash_search(&udf_hash,(byte*) udf_name, (uint) strlen(udf_name)))) + if (!(udf=(udf_func*) hash_search(&udf_hash,(byte*) udf_name, + (uint) strlen(udf_name)))) { net_printf(&thd->net, ER_FUNCTION_NOT_DEFINED, udf_name); goto err; } del_udf(udf); - if (!find_udf_dl(udf->dl)) + /* + Close the handle if this was function that was found during boot or + CREATE FUNCTION and it's not in use by any other udf function + */ + if (udf->dlhandle && !find_udf_dl(udf->dl)) dlclose(udf->dlhandle); bzero((char*) &tables,sizeof(tables)); @@ -483,10 +502,3 @@ int mysql_drop_function(THD *thd,const char *udf_name) } #endif /* HAVE_DLOPEN */ - -/* -** Local variables: -** tab-width: 8 -** c-basic-offset: 2 -** End: -*/ diff --git a/sql/sql_udf.h b/sql/sql_udf.h index d0b20f0a734..1ee9c44ce48 100644 --- a/sql/sql_udf.h +++ b/sql/sql_udf.h @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ diff --git a/sql/sql_union.cc b/sql/sql_union.cc new file mode 100644 index 00000000000..faa106d4f42 --- /dev/null +++ b/sql/sql_union.cc @@ -0,0 +1,286 @@ +/* Copyright (C) 2000 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + + +/* + UNION of select's + UNION's were introduced by Monty and Sinisa <sinisa@mysql.com> +*/ + + +#include "mysql_priv.h" +#include "sql_select.h" + + +int mysql_union(THD *thd, LEX *lex,select_result *result) +{ + SELECT_LEX *sl, *last_sl, *lex_sl; + ORDER *order; + List<Item> item_list; + TABLE *table; + int describe=(lex->select_lex.options & SELECT_DESCRIBE) ? 1 : 0; + int res; + bool found_rows_for_union= 0; + TABLE_LIST result_table_list; + TABLE_LIST *first_table=(TABLE_LIST *)lex->select_lex.table_list.first; + TMP_TABLE_PARAM tmp_table_param; + select_union *union_result; + DBUG_ENTER("mysql_union"); + + /* Fix tables 'to-be-unioned-from' list to point at opened tables */ + last_sl= &lex->select_lex; + for (sl= last_sl; + sl && sl->linkage != NOT_A_SELECT; + last_sl=sl, sl=sl->next) + { + for (TABLE_LIST *cursor= (TABLE_LIST *)sl->table_list.first; + cursor; + cursor=cursor->next) + { + cursor->table= (my_reinterpret_cast(TABLE_LIST*) (cursor->table))->table; + } + } + + /* last_sel now points at the last select where the ORDER BY is stored */ + if (sl) + { + /* + The found SL is an extra SELECT_LEX argument that contains + the ORDER BY and LIMIT parameter for the whole UNION + */ + lex_sl= sl; + order= (ORDER *) lex_sl->order_list.first; + found_rows_for_union = (lex->select_lex.options & OPTION_FOUND_ROWS && + !describe && sl->select_limit); + if (found_rows_for_union) + lex->select_lex.options ^= OPTION_FOUND_ROWS; + // This is done to eliminate unnecessary slowing down of the first query + if (!order || !describe) + last_sl->next=0; // Remove this extra element + } + else if (!last_sl->braces) + { + lex_sl= last_sl; // ORDER BY is here + order= (ORDER *) lex_sl->order_list.first; + } + else + { + lex_sl=0; + order=0; + } + + if (describe) + { + Item *item; + item_list.push_back(new Item_empty_string("table",NAME_LEN)); + item_list.push_back(new Item_empty_string("type",10)); + item_list.push_back(item=new Item_empty_string("possible_keys", + NAME_LEN*MAX_KEY)); + item->maybe_null=1; + item_list.push_back(item=new Item_empty_string("key",NAME_LEN)); + item->maybe_null=1; + item_list.push_back(item=new Item_int("key_len",0,3)); + item->maybe_null=1; + item_list.push_back(item=new Item_empty_string("ref", + NAME_LEN*MAX_REF_PARTS)); + item->maybe_null=1; + item_list.push_back(new Item_real("rows",0.0,0,10)); + item_list.push_back(new Item_empty_string("Extra",255)); + } + else + { + Item *item; + List_iterator<Item> it(lex->select_lex.item_list); + TABLE_LIST *first_table= (TABLE_LIST*) lex->select_lex.table_list.first; + + /* Create a list of items that will be in the result set */ + while ((item= it++)) + if (item_list.push_back(item)) + DBUG_RETURN(-1); + if (setup_fields(thd,first_table,item_list,0,0,1)) + DBUG_RETURN(-1); + } + + bzero((char*) &tmp_table_param,sizeof(tmp_table_param)); + tmp_table_param.field_count=item_list.elements; + if (!(table=create_tmp_table(thd, &tmp_table_param, item_list, + (ORDER*) 0, !describe & !lex->union_option, + 1, 0, + (lex->select_lex.options | thd->options | + TMP_TABLE_ALL_COLUMNS)))) + DBUG_RETURN(-1); + table->file->extra(HA_EXTRA_WRITE_CACHE); + table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); + bzero((char*) &result_table_list,sizeof(result_table_list)); + result_table_list.db= (char*) ""; + result_table_list.real_name=result_table_list.alias= (char*) "union"; + result_table_list.table=table; + + if (!(union_result=new select_union(table))) + { + res= -1; + goto exit; + } + union_result->not_describe= !describe; + union_result->tmp_table_param=&tmp_table_param; + for (sl= &lex->select_lex; sl; sl=sl->next) + { + lex->select=sl; + thd->offset_limit=sl->offset_limit; + thd->select_limit=sl->select_limit+sl->offset_limit; + if (thd->select_limit < sl->select_limit) + thd->select_limit= HA_POS_ERROR; // no limit + if (thd->select_limit == HA_POS_ERROR) + sl->options&= ~OPTION_FOUND_ROWS; + + res=mysql_select(thd, (describe && sl->linkage==NOT_A_SELECT) ? + first_table : (TABLE_LIST*) sl->table_list.first, + sl->item_list, + sl->where, + (sl->braces) ? (ORDER *)sl->order_list.first : + (ORDER *) 0, + (ORDER*) sl->group_list.first, + sl->having, + (ORDER*) NULL, + sl->options | thd->options | SELECT_NO_UNLOCK | + ((describe) ? SELECT_DESCRIBE : 0), + union_result); + if (res) + goto exit; + } + if (union_result->flush()) + { + res= 1; // Error is already sent + goto exit; + } + delete union_result; + + /* Send result to 'result' */ + lex->select = &lex->select_lex; + res =-1; + { + /* Create a list of fields in the temporary table */ + List_iterator<Item> it(item_list); + Field **field; +#if 0 + List<Item_func_match> ftfunc_list; + ftfunc_list.empty(); +#else + thd->lex.select_lex.ftfunc_list.empty(); +#endif + + for (field=table->field ; *field ; field++) + { + (void) it++; + (void) it.replace(new Item_field(*field)); + } + if (!thd->fatal_error) // Check if EOM + { + if (lex_sl) + { + thd->offset_limit=lex_sl->offset_limit; + thd->select_limit=lex_sl->select_limit+lex_sl->offset_limit; + if (thd->select_limit < lex_sl->select_limit) + thd->select_limit= HA_POS_ERROR; // no limit + if (thd->select_limit == HA_POS_ERROR) + thd->options&= ~OPTION_FOUND_ROWS; + } + else + { + thd->offset_limit= 0; + thd->select_limit= thd->variables.select_limit; + } + if (describe) + thd->select_limit= HA_POS_ERROR; // no limit + res=mysql_select(thd,&result_table_list, + item_list, NULL, (describe) ? 0 : order, + (ORDER*) NULL, NULL, (ORDER*) NULL, + thd->options, result); + if (found_rows_for_union && !res) + thd->limit_found_rows = (ulonglong)table->file->records; + } + } + +exit: + free_tmp_table(thd,table); + DBUG_RETURN(res); +} + + +/*************************************************************************** +** store records in temporary table for UNION +***************************************************************************/ + +select_union::select_union(TABLE *table_par) + :table(table_par), not_describe(0) +{ + bzero((char*) &info,sizeof(info)); + /* + We can always use DUP_IGNORE because the temporary table will only + contain a unique key if we are using not using UNION ALL + */ + info.handle_duplicates=DUP_IGNORE; +} + +select_union::~select_union() +{ +} + + +int select_union::prepare(List<Item> &list) +{ + if (not_describe && list.elements != table->fields) + { + my_message(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT, + ER(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT),MYF(0)); + return -1; + } + return 0; +} + +bool select_union::send_data(List<Item> &values) +{ + if (thd->offset_limit) + { // using limit offset,count + thd->offset_limit--; + return 0; + } + + fill_record(table->field,values); + if ((write_record(table,&info))) + { + if (create_myisam_from_heap(table, tmp_table_param, info.last_errno, 0)) + return 1; + } + return 0; +} + +bool select_union::send_eof() +{ + return 0; +} + +bool select_union::flush() +{ + int error; + if ((error=table->file->extra(HA_EXTRA_NO_CACHE))) + { + table->file->print_error(error,MYF(0)); + ::send_error(&thd->net); + return 1; + } + return 0; +} diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 6a4cdb80e5d..d1a2d4d5524 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -1,24 +1,27 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -/* Update of records */ +/* Update of records + Multi-table updates were introduced by Monty and Sinisa <sinisa@mysql.com> +*/ #include "mysql_priv.h" #include "sql_acl.h" +#include "sql_select.h" /* Return 0 if row hasn't changed */ @@ -26,10 +29,12 @@ static bool compare_record(TABLE *table, ulong query_id) { if (!table->blob_fields) return cmp_record(table,1); + /* Compare null bits */ if (memcmp(table->null_flags, table->null_flags+table->rec_buff_length, table->null_bytes)) return 1; // Diff in NULL value + /* Compare updated fields */ for (Field **ptr=table->field ; *ptr ; ptr++) { if ((*ptr)->query_id == query_id && @@ -40,16 +45,20 @@ static bool compare_record(TABLE *table, ulong query_id) } -int mysql_update(THD *thd,TABLE_LIST *table_list,List<Item> &fields, - List<Item> &values, COND *conds, +int mysql_update(THD *thd, + TABLE_LIST *table_list, + List<Item> &fields, + List<Item> &values, + COND *conds, + ORDER *order, ha_rows limit, - enum enum_duplicates handle_duplicates, - thr_lock_type lock_type) + enum enum_duplicates handle_duplicates) { bool using_limit=limit != HA_POS_ERROR; - bool used_key_is_modified, using_transactions; + bool safe_update= thd->options & OPTION_SAFE_UPDATES; + bool used_key_is_modified, transactional_table, log_delayed; int error=0; - uint save_time_stamp, used_index, want_privilege; + uint used_index, want_privilege; ulong query_id=thd->query_id, timestamp_query_id; key_map old_used_keys; TABLE *table; @@ -59,9 +68,8 @@ int mysql_update(THD *thd,TABLE_LIST *table_list,List<Item> &fields, LINT_INIT(used_index); LINT_INIT(timestamp_query_id); - if (!(table = open_ltable(thd,table_list,lock_type))) + if (!(table = open_ltable(thd,table_list,table_list->lock_type))) DBUG_RETURN(-1); /* purecov: inspected */ - save_time_stamp=table->time_stamp; table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); thd->proc_info="init"; @@ -83,11 +91,12 @@ int mysql_update(THD *thd,TABLE_LIST *table_list,List<Item> &fields, { timestamp_query_id=table->timestamp_field->query_id; table->timestamp_field->query_id=thd->query_id-1; + table->time_stamp= table->timestamp_field->offset() +1; } /* Check the fields we are going to modify */ table->grant.want_privilege=want_privilege; - if (setup_fields(thd,table_list,fields,1,0)) + if (setup_fields(thd,table_list,fields,1,0,0)) DBUG_RETURN(-1); /* purecov: inspected */ if (table->timestamp_field) { @@ -100,9 +109,8 @@ int mysql_update(THD *thd,TABLE_LIST *table_list,List<Item> &fields, /* Check values */ table->grant.want_privilege=(SELECT_ACL & ~table->grant.privilege); - if (setup_fields(thd,table_list,values,0,0)) + if (setup_fields(thd,table_list,values,0,0,0)) { - table->time_stamp=save_time_stamp; // Restore timestamp pointer DBUG_RETURN(-1); /* purecov: inspected */ } @@ -110,12 +118,9 @@ int mysql_update(THD *thd,TABLE_LIST *table_list,List<Item> &fields, table->used_keys=0; select=make_select(table,0,0,conds,&error); if (error || - (select && select->check_quick(test(thd->options & OPTION_SAFE_UPDATES), - limit)) || - !limit) + (select && select->check_quick(safe_update, limit)) || !limit) { delete select; - table->time_stamp=save_time_stamp; // Restore timestamp pointer if (error) { DBUG_RETURN(-1); // Error in where @@ -126,11 +131,10 @@ int mysql_update(THD *thd,TABLE_LIST *table_list,List<Item> &fields, /* If running in safe sql mode, don't allow updates without keys */ if (!table->quick_keys) { - thd->lex.options|=QUERY_NO_INDEX_USED; - if ((thd->options & OPTION_SAFE_UPDATES) && limit == HA_POS_ERROR) + thd->lex.select_lex.options|=QUERY_NO_INDEX_USED; + if (safe_update && !using_limit) { delete select; - table->time_stamp=save_time_stamp; send_error(&thd->net,ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE); DBUG_RETURN(1); } @@ -146,11 +150,11 @@ int mysql_update(THD *thd,TABLE_LIST *table_list,List<Item> &fields, used_key_is_modified=check_if_key_used(table, used_index, fields); else used_key_is_modified=0; - if (used_key_is_modified) + if (used_key_is_modified || order) { /* - ** We can't update table directly; We must first search after all - ** matching rows before updating the table! + We can't update table directly; We must first search after all + matching rows before updating the table! */ table->file->extra(HA_EXTRA_DONT_USE_CURSOR_TO_UPDATE); IO_CACHE tempfile; @@ -158,7 +162,6 @@ int mysql_update(THD *thd,TABLE_LIST *table_list,List<Item> &fields, DISK_BUFFER_SIZE, MYF(MY_WME))) { delete select; /* purecov: inspected */ - table->time_stamp=save_time_stamp; // Restore timestamp pointer /* purecov: inspected */ DBUG_RETURN(-1); } if (old_used_keys & ((key_map) 1 << used_index)) @@ -166,8 +169,35 @@ int mysql_update(THD *thd,TABLE_LIST *table_list,List<Item> &fields, table->key_read=1; table->file->extra(HA_EXTRA_KEYREAD); } + + if (order) + { + uint length; + SORT_FIELD *sortorder; + TABLE_LIST tables; + List<Item> fields; + List<Item> all_fields; + ha_rows examined_rows; + + bzero((char*) &tables,sizeof(tables)); + tables.table = table; + + table->io_cache = (IO_CACHE *) my_malloc(sizeof(IO_CACHE), + MYF(MY_FAE | MY_ZEROFILL)); + if (setup_order(thd, &tables, fields, all_fields, order) || + !(sortorder=make_unireg_sortorder(order, &length)) || + (table->found_records = filesort(table, sortorder, length, + (SQL_SELECT *) 0, 0L, + HA_POS_ERROR, &examined_rows)) + == HA_POS_ERROR) + { + delete select; + DBUG_RETURN(-1); + } + } + init_read_record(&info,thd,table,select,0,1); - thd->proc_info="searching"; + thd->proc_info="Searching rows for update"; while (!(error=info.read_record(&info)) && !thd->killed) { @@ -183,7 +213,7 @@ int mysql_update(THD *thd,TABLE_LIST *table_list,List<Item> &fields, } else { - if (!(test_flags & 512)) /* For debugging */ + if (!(test_flags & 512)) /* For debugging */ { DBUG_DUMP("record",(char*) table->record[0],table->reclength); } @@ -205,7 +235,7 @@ int mysql_update(THD *thd,TABLE_LIST *table_list,List<Item> &fields, select->cond=0; } else - { + { select= new SQL_SELECT; select->head=table; } @@ -215,13 +245,10 @@ int mysql_update(THD *thd,TABLE_LIST *table_list,List<Item> &fields, if (error >= 0) { delete select; - table->time_stamp=save_time_stamp; // Restore timestamp pointer - DBUG_RETURN(-1); + DBUG_RETURN(-1); } } - if (!(test_flags & TEST_READCHECK)) /* For debugging */ - VOID(table->file->extra(HA_EXTRA_NO_READCHECK)); if (handle_duplicates == DUP_IGNORE) table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); init_read_record(&info,thd,table,select,0,1); @@ -229,7 +256,7 @@ int mysql_update(THD *thd,TABLE_LIST *table_list,List<Item> &fields, ha_rows updated=0L,found=0L; thd->count_cuted_fields=1; /* calc cuted fields */ thd->cuted_fields=0L; - thd->proc_info="updating"; + thd->proc_info="Updating"; query_id=thd->query_id; while (!(error=info.read_record(&info)) && !thd->killed) @@ -266,29 +293,42 @@ int mysql_update(THD *thd,TABLE_LIST *table_list,List<Item> &fields, } end_read_record(&info); thd->proc_info="end"; - VOID(table->file->extra(HA_EXTRA_READCHECK)); VOID(table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY)); - table->time_stamp=save_time_stamp; // Restore auto timestamp pointer - using_transactions=table->file->has_transactions(); - if (updated && (error <= 0 || !using_transactions)) + transactional_table= table->file->has_transactions(); + log_delayed= (transactional_table || table->tmp_table); + if (updated && (error <= 0 || !transactional_table)) { mysql_update_log.write(thd,thd->query,thd->query_length); if (mysql_bin_log.is_open()) { - Query_log_event qinfo(thd, thd->query, using_transactions); - if (mysql_bin_log.write(&qinfo) && using_transactions) - error=1; + Query_log_event qinfo(thd, thd->query, thd->query_length, + log_delayed); + if (mysql_bin_log.write(&qinfo) && transactional_table) + error=1; // Rollback update } - if (!using_transactions) + if (!log_delayed) thd->options|=OPTION_STATUS_NO_TRANS_UPDATE; } - if (using_transactions && ha_autocommit_or_rollback(thd, error >= 0)) - error=1; + if (transactional_table) + { + if (ha_autocommit_or_rollback(thd, error >= 0)) + error=1; + } + + /* + Store table for future invalidation or invalidate it in + the query cache if something changed + */ + if (updated) + { + query_cache_invalidate3(thd, table_list, 1); + } if (thd->lock) { mysql_unlock_tables(thd, thd->lock); thd->lock=0; } + delete select; if (error >= 0) send_error(&thd->net,thd->killed ? ER_SERVER_SHUTDOWN : 0); /* purecov: inspected */ @@ -303,5 +343,527 @@ int mysql_update(THD *thd,TABLE_LIST *table_list,List<Item> &fields, DBUG_PRINT("info",("%d records updated",updated)); } thd->count_cuted_fields=0; /* calc cuted fields */ + free_io_cache(table); + DBUG_RETURN(0); +} + + +/*************************************************************************** + Update multiple tables from join +***************************************************************************/ + +/* + Setup multi-update handling and call SELECT to do the join +*/ + +int mysql_multi_update(THD *thd, + TABLE_LIST *table_list, + List<Item> *fields, + List<Item> *values, + COND *conds, + ulong options, + enum enum_duplicates handle_duplicates) +{ + int res; + multi_update *result; + TABLE_LIST *tl; + DBUG_ENTER("mysql_multi_update"); + + table_list->grant.want_privilege=(SELECT_ACL & ~table_list->grant.privilege); + if ((res=open_and_lock_tables(thd,table_list))) + DBUG_RETURN(res); + + thd->select_limit=HA_POS_ERROR; + if (setup_fields(thd, table_list, *fields, 1, 0, 0)) + DBUG_RETURN(-1); + + /* + Count tables and setup timestamp handling + */ + for (tl= (TABLE_LIST*) table_list ; tl ; tl=tl->next) + { + TABLE *table= tl->table; + if (table->timestamp_field) + { + table->time_stamp=0; + // Only set timestamp column if this is not modified + if (table->timestamp_field->query_id != thd->query_id) + table->time_stamp= table->timestamp_field->offset() +1; + } + } + + if (!(result=new multi_update(thd, table_list, fields, values, + handle_duplicates))) + DBUG_RETURN(-1); + + List<Item> total_list; + res= mysql_select(thd,table_list,total_list, + conds, (ORDER *) NULL, (ORDER *)NULL, (Item *) NULL, + (ORDER *)NULL, + options | SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK, + result); + delete result; + DBUG_RETURN(res); +} + + +multi_update::multi_update(THD *thd_arg, TABLE_LIST *table_list, + List<Item> *field_list, List<Item> *value_list, + enum enum_duplicates handle_duplicates_arg) + :all_tables(table_list), update_tables(0), thd(thd_arg), tmp_tables(0), + updated(0), found(0), fields(field_list), values(value_list), + table_count(0), copy_field(0), handle_duplicates(handle_duplicates_arg), + do_update(1), trans_safe(0) +{} + + +/* + Connect fields with tables and create list of tables that are updated +*/ + +int multi_update::prepare(List<Item> ¬_used_values) +{ + TABLE_LIST *table_ref; + SQL_LIST update; + table_map tables_to_update= 0; + Item_field *item; + List_iterator_fast<Item> field_it(*fields); + List_iterator_fast<Item> value_it(*values); + uint i, max_fields; + DBUG_ENTER("multi_update::prepare"); + + thd->count_cuted_fields=1; + thd->cuted_fields=0L; + thd->proc_info="updating main table"; + + while ((item= (Item_field *) field_it++)) + tables_to_update|= item->used_tables(); + + if (!tables_to_update) + { + my_error(ER_NOT_SUPPORTED_YET, MYF(0), + "You didn't specify any tables to UPDATE"); + DBUG_RETURN(1); + } + + /* + We have to check values after setup_tables to get used_keys right in + reference tables + */ + + if (setup_fields(thd, all_tables, *values, 1,0,0)) + DBUG_RETURN(1); + + /* + Save tables beeing updated in update_tables + update_table->shared is position for table + Don't use key read on tables that are updated + */ + + update.empty(); + for (table_ref= all_tables; table_ref; table_ref=table_ref->next) + { + TABLE *table=table_ref->table; + if (tables_to_update & table->map) + { + TABLE_LIST *tl= (TABLE_LIST*) thd->memdup((char*) table_ref, + sizeof(*tl)); + if (!tl) + DBUG_RETURN(1); + update.link_in_list((byte*) tl, (byte**) &tl->next); + tl->shared= table_count++; + table->no_keyread=1; + table->used_keys=0; + table->pos_in_table_list= tl; + } + } + table_count= update.elements; + update_tables= (TABLE_LIST*) update.first; + + tmp_tables = (TABLE **) thd->calloc(sizeof(TABLE *) * table_count); + tmp_table_param = (TMP_TABLE_PARAM*) thd->calloc(sizeof(TMP_TABLE_PARAM) * + table_count); + fields_for_table= (List_item **) thd->alloc(sizeof(List_item *) * + table_count); + values_for_table= (List_item **) thd->alloc(sizeof(List_item *) * + table_count); + if (thd->fatal_error) + DBUG_RETURN(1); + for (i=0 ; i < table_count ; i++) + { + fields_for_table[i]= new List_item; + values_for_table[i]= new List_item; + } + if (thd->fatal_error) + DBUG_RETURN(1); + + /* Split fields into fields_for_table[] and values_by_table[] */ + + field_it.rewind(); + while ((item= (Item_field *) field_it++)) + { + Item *value= value_it++; + uint offset= item->field->table->pos_in_table_list->shared; + fields_for_table[offset]->push_back(item); + values_for_table[offset]->push_back(value); + } + if (thd->fatal_error) + DBUG_RETURN(1); + + /* Allocate copy fields */ + max_fields=0; + for (i=0 ; i < table_count ; i++) + set_if_bigger(max_fields, fields_for_table[i]->elements); + copy_field= new Copy_field[max_fields]; + DBUG_RETURN(thd->fatal_error != 0); +} + + +/* + Store first used table in main_table as this should be updated first + This is because we know that no row in this table will be read twice. + + Create temporary tables to store changed values for all other tables + that are updated. +*/ + +bool +multi_update::initialize_tables(JOIN *join) +{ + TABLE_LIST *table_ref; + DBUG_ENTER("initialize_tables"); + + if ((thd->options & OPTION_SAFE_UPDATES) && error_if_full_join(join)) + DBUG_RETURN(1); + main_table=join->join_tab->table; + trans_safe= transactional_tables= main_table->file->has_transactions(); + log_delayed= trans_safe || main_table->tmp_table != NO_TMP_TABLE; + + /* Create a temporary table for all tables after except main table */ + for (table_ref= update_tables; table_ref; table_ref=table_ref->next) + { + TABLE *table=table_ref->table; + if (table != main_table) + { + uint cnt= table_ref->shared; + ORDER group; + List<Item> temp_fields= *fields_for_table[cnt]; + TMP_TABLE_PARAM *tmp_param= tmp_table_param+cnt; + + /* + Create a temporary table to store all fields that are changed for this + table. The first field in the temporary table is a pointer to the + original row so that we can find and update it + */ + + /* ok to be on stack as this is not referenced outside of this func */ + Field_string offset(table->file->ref_length, 0, "offset", + table, 1); + if (temp_fields.push_front(new Item_field(((Field *) &offset)))) + DBUG_RETURN(1); + + /* Make an unique key over the first field to avoid duplicated updates */ + bzero((char*) &group, sizeof(group)); + group.asc= 1; + group.item= (Item**) temp_fields.head_ref(); + + tmp_param->quick_group=1; + tmp_param->field_count=temp_fields.elements; + tmp_param->group_parts=1; + tmp_param->group_length= table->file->ref_length; + if (!(tmp_tables[cnt]=create_tmp_table(thd, + tmp_param, + temp_fields, + (ORDER*) &group, 0, 0, 0, + TMP_TABLE_ALL_COLUMNS))) + DBUG_RETURN(1); + tmp_tables[cnt]->file->extra(HA_EXTRA_WRITE_CACHE); + } + } DBUG_RETURN(0); } + + +multi_update::~multi_update() +{ + TABLE_LIST *table; + for (table= update_tables ; table; table= table->next) + table->table->no_keyread=0; + + if (tmp_tables) + { + for (uint cnt = 0; cnt < table_count; cnt++) + { + if (tmp_tables[cnt]) + { + free_tmp_table(thd, tmp_tables[cnt]); + tmp_table_param[cnt].cleanup(); + } + } + } + if (copy_field) + delete [] copy_field; + thd->count_cuted_fields=0; // Restore this setting + if (!trans_safe) + thd->options|=OPTION_STATUS_NO_TRANS_UPDATE; +} + + +bool multi_update::send_data(List<Item> ¬_used_values) +{ + TABLE_LIST *cur_table; + DBUG_ENTER("multi_update::send_data"); + + for (cur_table= update_tables; cur_table ; cur_table= cur_table->next) + { + TABLE *table= cur_table->table; + /* Check if we are using outer join and we didn't find the row */ + if (table->status & (STATUS_NULL_ROW | STATUS_UPDATED)) + continue; + + uint offset= cur_table->shared; + table->file->position(table->record[0]); + if (table == main_table) + { + table->status|= STATUS_UPDATED; + store_record(table,1); + if (fill_record(*fields_for_table[offset], *values_for_table[offset])) + DBUG_RETURN(1); + found++; + if (compare_record(table, thd->query_id)) + { + int error; + if (!updated++) + { + /* + Inform the main table that we are going to update the table even + while we may be scanning it. This will flush the read cache + if it's used. + */ + main_table->file->extra(HA_EXTRA_PREPARE_FOR_UPDATE); + } + if ((error=table->file->update_row(table->record[1], + table->record[0]))) + { + table->file->print_error(error,MYF(0)); + updated--; + DBUG_RETURN(1); + } + } + } + else + { + int error; + TABLE *tmp_table= tmp_tables[offset]; + fill_record(tmp_table->field+1, *values_for_table[offset]); + found++; + /* Store pointer to row */ + memcpy((char*) tmp_table->field[0]->ptr, + (char*) table->file->ref, table->file->ref_length); + /* Write row, ignoring duplicated updates to a row */ + if ((error= tmp_table->file->write_row(tmp_table->record[0])) && + (error != HA_ERR_FOUND_DUPP_KEY && + error != HA_ERR_FOUND_DUPP_UNIQUE)) + { + if (create_myisam_from_heap(table, tmp_table_param + offset, error, 1)) + { + do_update=0; + DBUG_RETURN(1); // Not a table_is_full error + } + } + } + } + DBUG_RETURN(0); +} + + +void multi_update::send_error(uint errcode,const char *err) +{ + /* First send error what ever it is ... */ + ::send_error(&thd->net,errcode,err); + + /* If nothing updated return */ + if (!updated) + return; + + /* Something already updated so we have to invalidate cache */ + query_cache_invalidate3(thd, update_tables, 1); + + /* + If all tables that has been updated are trans safe then just do rollback. + If not attempt to do remaining updates. + */ + + if (trans_safe) + ha_rollback_stmt(thd); + else if (do_update && table_count > 1) + { + /* Add warning here */ + VOID(do_updates(0)); + } +} + + +int multi_update::do_updates(bool from_send_error) +{ + TABLE_LIST *cur_table; + int local_error; + ha_rows org_updated; + TABLE *table; + DBUG_ENTER("do_updates"); + + do_update= 0; // Don't retry this function + for (cur_table= update_tables; cur_table ; cur_table= cur_table->next) + { + table = cur_table->table; + if (table == main_table) + continue; // Already updated + + org_updated= updated; + byte *ref_pos; + TABLE *tmp_table= tmp_tables[cur_table->shared]; + tmp_table->file->extra(HA_EXTRA_CACHE); // Change to read cache + table->file->extra(HA_EXTRA_NO_CACHE); + + /* + Setup copy functions to copy fields from temporary table + */ + List_iterator_fast<Item> field_it(*fields_for_table[cur_table->shared]); + Field **field= tmp_table->field+1; // Skip row pointer + Copy_field *copy_field_ptr= copy_field, *copy_field_end; + for ( ; *field ; field++) + { + Item_field *item= (Item_field* ) field_it++; + (copy_field_ptr++)->set(item->field, *field, 0); + } + copy_field_end=copy_field_ptr; + + if ((local_error = tmp_table->file->rnd_init(1))) + goto err; + + ref_pos= (byte*) tmp_table->field[0]->ptr; + for (;;) + { + if (thd->killed && trans_safe) + goto err; + if ((local_error=tmp_table->file->rnd_next(tmp_table->record[0]))) + { + if (local_error == HA_ERR_END_OF_FILE) + break; + if (local_error == HA_ERR_RECORD_DELETED) + continue; // May happen on dup key + goto err; + } + if ((local_error= table->file->rnd_pos(table->record[0], ref_pos))) + goto err; + table->status|= STATUS_UPDATED; + store_record(table,1); + + /* Copy data from temporary table to current table */ + for (copy_field_ptr=copy_field; + copy_field_ptr != copy_field_end; + copy_field_ptr++) + (*copy_field_ptr->do_copy)(copy_field_ptr); + + if (compare_record(table, thd->query_id)) + { + if ((local_error=table->file->update_row(table->record[1], + table->record[0]))) + { + if (local_error != HA_ERR_FOUND_DUPP_KEY || + handle_duplicates != DUP_IGNORE) + goto err; + } + updated++; + if (table->tmp_table != NO_TMP_TABLE) + log_delayed= 1; + } + } + + if (updated != org_updated) + { + if (table->tmp_table != NO_TMP_TABLE) + log_delayed= 1; // Tmp tables forces delay log + if (table->file->has_transactions()) + log_delayed= transactional_tables= 1; + else + trans_safe= 0; // Can't do safe rollback + } + } + DBUG_RETURN(0); + +err: + if (!from_send_error) + table->file->print_error(local_error,MYF(0)); + + if (updated != org_updated) + { + if (table->tmp_table != NO_TMP_TABLE) + log_delayed= 1; + if (table->file->has_transactions()) + log_delayed= transactional_tables= 1; + else + trans_safe= 0; + } + DBUG_RETURN(1); +} + + +/* out: 1 if error, 0 if success */ + +bool multi_update::send_eof() +{ + char buff[80]; + thd->proc_info="updating reference tables"; + + /* Does updates for the last n - 1 tables, returns 0 if ok */ + int local_error = (table_count) ? do_updates(0) : 0; + thd->proc_info= "end"; + + /* + Write the SQL statement to the binlog if we updated + rows and we succeeded or if we updated some non + transacational tables + */ + + if (updated && (local_error <= 0 || !trans_safe)) + { + mysql_update_log.write(thd,thd->query,thd->query_length); + if (mysql_bin_log.is_open()) + { + Query_log_event qinfo(thd, thd->query, thd->query_length, + log_delayed); + if (mysql_bin_log.write(&qinfo) && trans_safe) + local_error= 1; // Rollback update + } + if (!log_delayed) + thd->options|=OPTION_STATUS_NO_TRANS_UPDATE; + } + + if (transactional_tables) + { + if (ha_autocommit_or_rollback(thd, local_error != 0)) + local_error=1; + } + + if (local_error > 0) // if the above log write did not fail ... + { + /* Safety: If we haven't got an error before (should not happen) */ + my_message(ER_UNKNOWN_ERROR, "An error occured in multi-table update", + MYF(0)); + ::send_error(&thd->net); + return 1; + } + + + sprintf(buff,ER(ER_UPDATE_INFO), (long) found, (long) updated, + (long) thd->cuted_fields); + if (updated) + { + query_cache_invalidate3(thd, update_tables, 1); + } + ::send_ok(&thd->net, + (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated, + thd->insert_id_used ? thd->insert_id() : 0L,buff); + return 0; +} diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index d751dcd0927..2f339f30eb4 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1,4 +1,4 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB +/* Copyright (C) 2000-2003 MySQL AB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,11 +21,13 @@ #define YYINITDEPTH 100 #define YYMAXDEPTH 3200 /* Because of 64K stack */ #define Lex current_lex +#define Select Lex->select #include "mysql_priv.h" -#include "slave.h" +#include "slave.h" #include "sql_acl.h" #include "lex_symbol.h" #include <myisam.h> +#include <myisammrg.h> extern void yyerror(const char*); int yylex(void *yylval); @@ -42,7 +44,7 @@ inline Item *or_or_concat(Item* A, Item* B) %union { int num; ulong ulong_num; - ulonglong ulonglong_num; + ulonglong ulonglong_number; LEX_STRING lex_str; LEX_STRING *lex_str_ptr; LEX_SYMBOL symbol; @@ -51,17 +53,21 @@ inline Item *or_or_concat(Item* A, Item* B) Item *item; List<Item> *item_list; List<String> *string_list; - Key::Keytype key_type; - enum db_type db_type; - enum row_type row_type; - enum enum_tx_isolation tx_isolation; String *string; key_part_spec *key_part; TABLE_LIST *table_list; udf_func *udf; - interval_type interval; LEX_USER *lex_user; + sys_var *variable; + Key::Keytype key_type; + enum db_type db_type; + enum row_type row_type; + enum ha_rkey_function ha_rkey_mode; + enum enum_tx_isolation tx_isolation; + enum Item_cast cast_type; enum Item_udftype udf_type; + thr_lock_type lock_type; + interval_type interval; } %{ @@ -72,6 +78,12 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token END_OF_INPUT +%token CLOSE_SYM +%token HANDLER_SYM +%token LAST_SYM +%token NEXT_SYM +%token PREV_SYM + %token EQ %token EQUAL_SYM %token GE @@ -84,46 +96,57 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token SHIFT_RIGHT %token SET_VAR -%token AVG_SYM -%token COUNT_SYM -%token MAX_SYM -%token MIN_SYM -%token SUM_SYM -%token STD_SYM - +%token ABORT_SYM %token ADD -%token ALTER %token AFTER_SYM -%token ANALYZE_SYM -%token BEGIN_SYM +%token ALTER +%token ANALYZE_SYM +%token AVG_SYM +%token BEGIN_SYM +%token BINLOG_SYM %token CHANGE -%token COMMENT_SYM -%token COMMIT_SYM +%token CLIENT_SYM +%token COMMENT_SYM +%token COMMIT_SYM +%token COUNT_SYM %token CREATE %token CROSS +%token CUBE_SYM %token DELETE_SYM %token DO_SYM %token DROP -%token INSERT +%token EVENTS_SYM +%token EXECUTE_SYM %token FLUSH_SYM -%token SELECT_SYM -%token MASTER_SYM -%token REPAIR -%token RESET_SYM -%token PURGE -%token SLAVE -%token START_SYM -%token STOP_SYM -%token TRUNCATE_SYM -%token ROLLBACK_SYM -%token OPTIMIZE -%token SHOW -%token UPDATE_SYM +%token INSERT +%token IO_THREAD %token KILL_SYM %token LOAD -%token LOCK_SYM %token LOCKS_SYM +%token LOCK_SYM +%token MASTER_SYM +%token MAX_SYM +%token MIN_SYM +%token NONE_SYM +%token OPTIMIZE +%token PURGE +%token REPAIR +%token REPLICATION +%token RESET_SYM +%token ROLLBACK_SYM +%token ROLLUP_SYM +%token SELECT_SYM +%token SHOW +%token SLAVE +%token SQL_THREAD +%token START_SYM +%token STD_SYM +%token STOP_SYM +%token SUM_SYM +%token SUPER_SYM +%token TRUNCATE_SYM %token UNLOCK_SYM +%token UPDATE_SYM %token ACTION %token AGGREGATE_SYM @@ -132,16 +155,19 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token AS %token ASC %token AUTO_INC -%token AUTOCOMMIT %token AVG_ROW_LENGTH -%token BACKUP_SYM +%token BACKUP_SYM %token BERKELEY_DB_SYM %token BINARY %token BIT_SYM %token BOOL_SYM +%token BOOLEAN_SYM %token BOTH %token BY +%token CACHE_SYM %token CASCADE +%token CAST_SYM +%token CHARSET %token CHECKSUM_SYM %token CHECK_SYM %token COMMITTED_SYM @@ -149,6 +175,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token COLUMN_SYM %token CONCURRENT %token CONSTRAINT +%token CONVERT_SYM %token DATABASES %token DATA_SYM %token DEFAULT @@ -156,10 +183,14 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token DELAY_KEY_WRITE_SYM %token DESC %token DESCRIBE +%token DES_KEY_FILE +%token DISABLE_SYM %token DISTINCT %token DYNAMIC_SYM +%token ENABLE_SYM %token ENCLOSED %token ESCAPED +%token DIRECTORY_SYM %token ESCAPE_SYM %token EXISTS %token EXTENDED_SYM @@ -167,13 +198,12 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token FIRST_SYM %token FIXED_SYM %token FLOAT_NUM +%token FORCE_SYM %token FOREIGN %token FROM %token FULL -%token FULLTEXT_SYM -%token GEMINI_SYM -%token GEMINI_SPIN_RETRIES -%token GLOBAL_SYM +%token FULLTEXT_SYM +%token GLOBAL_SYM %token GRANT %token GRANTS %token GREATEST_SYM @@ -186,48 +216,57 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token IDENT %token IGNORE_SYM %token INDEX +%token INDEXES %token INFILE %token INNER_SYM %token INNOBASE_SYM %token INTO %token IN_SYM -%token ISOLATION +%token ISOLATION %token ISAM_SYM %token JOIN_SYM %token KEYS %token KEY_SYM %token LEADING %token LEAST_SYM -%token LEVEL_SYM +%token LEVEL_SYM %token LEX_HOSTNAME %token LIKE %token LINES %token LOCAL_SYM +%token LOG_SYM %token LOGS_SYM %token LONG_NUM %token LONG_SYM %token LOW_PRIORITY -%token MASTER_HOST_SYM -%token MASTER_USER_SYM -%token MASTER_LOG_FILE_SYM -%token MASTER_LOG_POS_SYM -%token MASTER_PASSWORD_SYM -%token MASTER_PORT_SYM -%token MASTER_CONNECT_RETRY_SYM +%token MASTER_HOST_SYM +%token MASTER_USER_SYM +%token MASTER_LOG_FILE_SYM +%token MASTER_LOG_POS_SYM +%token MASTER_PASSWORD_SYM +%token MASTER_PORT_SYM +%token MASTER_CONNECT_RETRY_SYM +%token MASTER_SERVER_ID_SYM +%token RELAY_LOG_FILE_SYM +%token RELAY_LOG_POS_SYM %token MATCH %token MAX_ROWS +%token MAX_CONNECTIONS_PER_HOUR +%token MAX_QUERIES_PER_HOUR +%token MAX_UPDATES_PER_HOUR %token MEDIUM_SYM %token MERGE_SYM %token MIN_ROWS %token MYISAM_SYM %token NATIONAL_SYM %token NATURAL +%token NEW_SYM %token NCHAR_SYM %token NOT -%token FOREIGN_KEY_CHECKS %token NO_SYM %token NULL_SYM %token NUM +%token OFFSET_SYM %token ON %token OPEN_SYM %token OPTION @@ -237,13 +276,14 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token ORDER_SYM %token OUTER %token OUTFILE -%token DUMPFILE +%token DUMPFILE %token PACK_KEYS_SYM %token PARTIAL %token PRIMARY_SYM %token PRIVILEGES %token PROCESS %token PROCESSLIST_SYM +%token QUERY_SYM %token RAID_0_SYM %token RAID_STRIPED_SYM %token RAID_TYPE @@ -256,7 +296,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token RELOAD %token RENAME %token REPEATABLE_SYM -%token RESTORE_SYM +%token REQUIRE_SYM +%token RESOURCES +%token RESTORE_SYM %token RESTRICT %token REVOKE %token ROWS_SYM @@ -266,9 +308,11 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token SERIALIZABLE_SYM %token SESSION_SYM %token SHUTDOWN +%token SSL_SYM %token STARTING %token STATUS_SYM %token STRAIGHT_JOIN +%token SUBJECT_SYM %token TABLES %token TABLE_SYM %token TEMPORARY @@ -285,11 +329,11 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token UDF_RETURNS_SYM %token UDF_SONAME_SYM %token UDF_SYM -%token UNCOMMITTED_SYM +%token UNCOMMITTED_SYM %token UNION_SYM %token UNIQUE_SYM -%token UNIQUE_CHECKS %token USAGE +%token USE_FRM %token USE_SYM %token USING %token VALUES @@ -297,12 +341,14 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token WHERE %token WITH %token WRITE_SYM -%token COMPRESSED_SYM +%token X509_SYM +%token XOR +%token COMPRESSED_SYM %token BIGINT %token BLOB_SYM %token CHAR_SYM -%token CHANGED +%token CHANGED %token COALESCE %token DATETIME %token DATE_SYM @@ -320,8 +366,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token MEDIUMTEXT %token NUMERIC_SYM %token PRECISION -%token QUICK +%token QUICK %token REAL +%token SIGNED_SYM %token SMALLINT %token STRING_SYM %token TEXT_SYM @@ -330,20 +377,21 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token TINYBLOB %token TINYINT %token TINYTEXT +%token ULONGLONG_NUM %token UNSIGNED %token VARBINARY %token VARCHAR %token VARYING %token ZEROFILL -%token AGAINST +%token AGAINST %token ATAN %token BETWEEN_SYM %token BIT_AND %token BIT_OR %token CASE_SYM %token CONCAT -%token CONCAT_WS +%token CONCAT_WS %token CURDATE %token CURTIME %token DATABASE @@ -354,6 +402,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token DAY_SECOND_SYM %token DAY_SYM %token DECODE_SYM +%token DES_ENCRYPT_SYM +%token DES_DECRYPT_SYM %token ELSE %token ELT_FUNC %token ENCODE_SYM @@ -370,15 +420,16 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token HOUR_SYM %token IDENTIFIED_SYM %token IF -%token INSERT_ID +%token INSERT_METHOD %token INTERVAL_SYM %token LAST_INSERT_ID %token LEFT %token LOCATE %token MAKE_SET_SYM +%token MASTER_POS_WAIT %token MINUTE_SECOND_SYM %token MINUTE_SYM -%token MODE_SYM +%token MODE_SYM %token MODIFY_SYM %token MONTH_SYM %token NOW_SYM @@ -405,30 +456,24 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token USER %token WEEK_SYM %token WHEN_SYM -%token WORK_SYM +%token WORK_SYM %token YEAR_MONTH_SYM %token YEAR_SYM %token YEARWEEK -%token BENCHMARK_SYM -%token END -%token THEN_SYM - -%token SQL_BIG_TABLES -%token SQL_BIG_SELECTS -%token SQL_SELECT_LIMIT -%token SQL_MAX_JOIN_SIZE -%token SQL_LOG_BIN -%token SQL_LOG_OFF -%token SQL_LOG_UPDATE -%token SQL_LOW_PRIORITY_UPDATES -%token SQL_SMALL_RESULT +%token BENCHMARK_SYM +%token END +%token THEN_SYM + %token SQL_BIG_RESULT +%token SQL_CACHE_SYM +%token SQL_CALC_FOUND_ROWS +%token SQL_NO_CACHE_SYM +%token SQL_SMALL_RESULT %token SQL_BUFFER_RESULT -%token SQL_WARNINGS -%token SQL_AUTO_IS_NULL -%token SQL_SAFE_UPDATES -%token SQL_QUOTE_SHOW_CREATE -%token SQL_SLAVE_SKIP_COUNTER + +%token ISSUER_SYM +%token SUBJECT_SYM +%token CIPHER_SYM %left SET_VAR %left OR_OR_CONCAT OR @@ -441,12 +486,20 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %left '-' '+' %left '*' '/' '%' %left NEG '~' +%left XOR +%left '^' %right NOT %right BINARY +/* These don't actually affect the way the query is really evaluated, but + they silence a few warnings for shift/reduce conflicts. */ +%left ',' +%left STRAIGHT_JOIN JOIN_SYM +%nonassoc CROSS INNER_SYM NATURAL LEFT RIGHT + %type <lex_str> IDENT TEXT_STRING REAL_NUM FLOAT_NUM NUM LONG_NUM HEX_NUM LEX_HOSTNAME - field_ident select_alias ident ident_or_text + ULONGLONG_NUM field_ident select_alias ident ident_or_text %type <lex_str_ptr> opt_table_alias @@ -459,27 +512,31 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); opt_escape %type <string> - text_string + text_string %type <num> - type int_type real_type order_dir opt_field_spec set_option lock_option + type int_type real_type order_dir opt_field_spec lock_option udf_type if_exists opt_local opt_table_options table_options - table_option opt_if_not_exists + table_option opt_if_not_exists opt_var_type opt_var_ident_type + opt_temporary %type <ulong_num> - ULONG_NUM raid_types + ULONG_NUM raid_types merge_insert_types + +%type <ulonglong_number> + ulonglong_num -%type <ulonglong_num> - ULONGLONG_NUM +%type <lock_type> + replace_lock_option opt_low_priority insert_lock_option load_data_lock %type <item> literal text_literal insert_ident order_ident simple_ident select_item2 expr opt_expr opt_else sum_expr in_sum_expr table_wild opt_pad no_in_expr expr_expr simple_expr no_and_expr - using_list + using_list expr_or_default set_expr_or_default %type <item_list> - expr_list udf_expr_list when_list ident_list + expr_list udf_expr_list when_list ident_list ident_list_arg %type <key_type> key_type opt_unique_or_fulltext @@ -503,7 +560,11 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %type <row_type> row_types -%type <tx_isolation> tx_isolation isolation_types +%type <tx_isolation> isolation_types + +%type <ha_rkey_mode> handler_rkey_mode + +%type <cast_type> cast_type %type <udf_type> udf_func_type @@ -511,31 +572,36 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %type <lex_user> user grant_user +%type <variable> internal_variable_name + %type <NONE> query verb_clause create change select do drop insert replace insert2 insert_values update delete truncate rename show describe load alter optimize flush reset purge begin commit rollback slave master_def master_defs - repair restore backup analyze check - field_list field_list_item field_spec kill + repair restore backup analyze check start + field_list field_list_item field_spec kill column_def key_def select_item_list select_item values_list no_braces limit_clause delete_limit_clause fields opt_values values procedure_list procedure_list2 procedure_item - when_list2 expr_list2 + when_list2 expr_list2 handler opt_precision opt_ignore opt_column opt_restrict grant revoke set lock unlock string_list field_options field_option field_opt_list opt_binary table_lock_list table_lock varchar references opt_on_delete opt_on_delete_list opt_on_delete_item use opt_delete_options opt_delete_option - opt_outer table_list table opt_option opt_place opt_low_priority + opt_outer table_list table_name opt_option opt_place opt_attribute opt_attribute_list attribute column_list column_list_id opt_column_list grant_privileges opt_table user_list grant_option grant_privilege grant_privilege_list - flush_options flush_option insert_lock_option replace_lock_option + flush_options flush_option equal optional_braces opt_key_definition key_usage_list2 opt_mi_check_type opt_to mi_check_types normal_join table_to_table_list table_to_table opt_table_list opt_as - END_OF_INPUT + handler_rkey_function handler_read_or_scan + single_multi table_wild_list table_wild_one opt_wild opt_union union_list + precision union_option opt_and +END_OF_INPUT %type <NONE> '-' '+' '*' '/' '%' '(' ')' @@ -548,7 +614,7 @@ query: { THD *thd=current_thd; if (!thd->bootstrap && - (!(thd->lex.options & OPTION_FOUND_COMMENT))) + (!(thd->lex.select_lex.options & OPTION_FOUND_COMMENT))) { send_error(¤t_thd->net,ER_EMPTY_QUERY); YYABORT; @@ -558,8 +624,7 @@ query: thd->lex.sql_command = SQLCOM_EMPTY_QUERY; } } - | verb_clause END_OF_INPUT {} - ; + | verb_clause END_OF_INPUT {}; verb_clause: alter @@ -592,12 +657,13 @@ verb_clause: | select | set | slave + | start | show | truncate + | handler | unlock | update - | use - ; + | use; /* change master */ @@ -606,16 +672,15 @@ change: { LEX *lex = Lex; lex->sql_command = SQLCOM_CHANGE_MASTER; - memset(&lex->mi, 0, sizeof(lex->mi)); - } master_defs + bzero((char*) &lex->mi, sizeof(lex->mi)); + } + master_defs {} - ; + ; master_defs: - master_def - | - master_defs ',' master_def - ; + master_def + | master_defs ',' master_def; master_def: MASTER_HOST_SYM EQ TEXT_STRING @@ -643,7 +708,7 @@ master_def: Lex->mi.port = $3; } | - MASTER_LOG_POS_SYM EQ ULONGLONG_NUM + MASTER_LOG_POS_SYM EQ ulonglong_num { Lex->mi.pos = $3; } @@ -652,7 +717,16 @@ master_def: { Lex->mi.connect_retry = $3; } - ; + | + RELAY_LOG_FILE_SYM EQ TEXT_STRING + { + Lex->mi.relay_log_name = $3.str; + } + | + RELAY_LOG_POS_SYM EQ ULONG_NUM + { + Lex->mi.relay_log_pos = $3; + }; /* create a table */ @@ -664,7 +738,8 @@ create: lex->sql_command= SQLCOM_CREATE_TABLE; if (!add_table_to_list($5, ($2 & HA_LEX_CREATE_TMP_TABLE ? - &tmp_table_alias : (LEX_STRING*) 0),1)) + &tmp_table_alias : (LEX_STRING*) 0), + TL_OPTION_UPDATING)) YYABORT; lex->create_list.empty(); lex->key_list.empty(); @@ -672,103 +747,100 @@ create: lex->change=NullS; bzero((char*) &lex->create_info,sizeof(lex->create_info)); lex->create_info.options=$2 | $4; - lex->create_info.db_type= default_table_type; + lex->create_info.db_type= (enum db_type) lex->thd->variables.table_type; } create2 - {} + {} | CREATE opt_unique_or_fulltext INDEX ident ON table_ident { - Lex->sql_command= SQLCOM_CREATE_INDEX; - if (!add_table_to_list($6,NULL,1)) + LEX *lex=Lex; + lex->sql_command= SQLCOM_CREATE_INDEX; + if (!add_table_to_list($6, NULL, TL_OPTION_UPDATING)) YYABORT; - Lex->create_list.empty(); - Lex->key_list.empty(); - Lex->col_list.empty(); - Lex->change=NullS; + lex->create_list.empty(); + lex->key_list.empty(); + lex->col_list.empty(); + lex->change=NullS; } '(' key_list ')' { - Lex->key_list.push_back(new Key($2,$4.str,Lex->col_list)); - Lex->col_list.empty(); + LEX *lex=Lex; + lex->key_list.push_back(new Key($2,$4.str,lex->col_list)); + lex->col_list.empty(); } | CREATE DATABASE opt_if_not_exists ident { - Lex->sql_command=SQLCOM_CREATE_DB; - Lex->name=$4.str; - Lex->create_info.options=$3; + LEX *lex=Lex; + lex->sql_command=SQLCOM_CREATE_DB; + lex->name=$4.str; + lex->create_info.options=$3; } | CREATE udf_func_type UDF_SYM ident { - Lex->sql_command = SQLCOM_CREATE_FUNCTION; - Lex->udf.name=$4.str; - Lex->udf.name_length=$4.length; - Lex->udf.type= $2; + LEX *lex=Lex; + lex->sql_command = SQLCOM_CREATE_FUNCTION; + lex->udf.name=$4.str; + lex->udf.name_length=$4.length; + lex->udf.type= $2; } UDF_RETURNS_SYM udf_type UDF_SONAME_SYM TEXT_STRING { - Lex->udf.returns=(Item_result) $7; - Lex->udf.dl=$9.str; - } - ; + LEX *lex=Lex; + lex->udf.returns=(Item_result) $7; + lex->udf.dl=$9.str; + }; create2: '(' field_list ')' opt_create_table_options create3 {} - | opt_create_table_options create3 {} - ; + | opt_create_table_options create3 {}; create3: /* empty */ {} | opt_duplicate opt_as SELECT_SYM { - Lex->lock_option= (using_update_log) ? TL_READ_NO_INSERT : TL_READ; - mysql_init_select(Lex); + LEX *lex=Lex; + lex->lock_option= (using_update_log) ? TL_READ_NO_INSERT : TL_READ; + mysql_init_select(lex); } - select_options select_item_list opt_select_from {} - ; + select_options select_item_list opt_select_from opt_union {}; opt_as: /* empty */ {} - | AS {} - ; + | AS {}; opt_table_options: /* empty */ { $$= 0; } - | table_options { $$= $1;} - ; + | table_options { $$= $1;}; table_options: table_option { $$=$1; } - | table_option table_options { $$= $1 | $2; } - ; + | table_option table_options { $$= $1 | $2; }; table_option: - TEMPORARY { $$=HA_LEX_CREATE_TMP_TABLE; } - ; + TEMPORARY { $$=HA_LEX_CREATE_TMP_TABLE; }; opt_if_not_exists: /* empty */ { $$= 0; } - | IF NOT EXISTS { $$=HA_LEX_CREATE_IF_NOT_EXISTS; } - ; + | IF NOT EXISTS { $$=HA_LEX_CREATE_IF_NOT_EXISTS; }; opt_create_table_options: /* empty */ - | create_table_options - ; + | create_table_options; create_table_options: create_table_option - | create_table_option create_table_options - ; + | create_table_option create_table_options; create_table_option: TYPE_SYM EQ table_types { Lex->create_info.db_type= $3; } - | MAX_ROWS EQ ULONGLONG_NUM { Lex->create_info.max_rows= $3; } - | MIN_ROWS EQ ULONGLONG_NUM { Lex->create_info.min_rows= $3; } - | AVG_ROW_LENGTH EQ ULONG_NUM { Lex->create_info.avg_row_length=$3; } + | MAX_ROWS EQ ulonglong_num { Lex->create_info.max_rows= $3; Lex->create_info.used_fields|= HA_CREATE_USED_MAX_ROWS;} + | MIN_ROWS EQ ulonglong_num { Lex->create_info.min_rows= $3; Lex->create_info.used_fields|= HA_CREATE_USED_MIN_ROWS;} + | AVG_ROW_LENGTH EQ ULONG_NUM { Lex->create_info.avg_row_length=$3; Lex->create_info.used_fields|= HA_CREATE_USED_AVG_ROW_LENGTH;} | PASSWORD EQ TEXT_STRING { Lex->create_info.password=$3.str; } | COMMENT_SYM EQ TEXT_STRING { Lex->create_info.comment=$3.str; } - | AUTO_INC EQ ULONGLONG_NUM { Lex->create_info.auto_increment_value=$3; Lex->create_info.used_fields|= HA_CREATE_USED_AUTO;} - | PACK_KEYS_SYM EQ ULONG_NUM { Lex->create_info.table_options|= $3 ? HA_OPTION_PACK_KEYS : HA_OPTION_NO_PACK_KEYS; } + | AUTO_INC EQ ulonglong_num { Lex->create_info.auto_increment_value=$3; Lex->create_info.used_fields|= HA_CREATE_USED_AUTO;} + | PACK_KEYS_SYM EQ ULONG_NUM { Lex->create_info.table_options|= $3 ? HA_OPTION_PACK_KEYS : HA_OPTION_NO_PACK_KEYS; Lex->create_info.used_fields|= HA_CREATE_USED_PACK_KEYS;} + | PACK_KEYS_SYM EQ DEFAULT { Lex->create_info.table_options&= ~(HA_OPTION_PACK_KEYS | HA_OPTION_NO_PACK_KEYS); Lex->create_info.used_fields|= HA_CREATE_USED_PACK_KEYS;} | CHECKSUM_SYM EQ ULONG_NUM { Lex->create_info.table_options|= $3 ? HA_OPTION_CHECKSUM : HA_OPTION_NO_CHECKSUM; } | DELAY_KEY_WRITE_SYM EQ ULONG_NUM { Lex->create_info.table_options|= $3 ? HA_OPTION_DELAY_KEY_WRITE : HA_OPTION_NO_DELAY_KEY_WRITE; } | ROW_FORMAT_SYM EQ row_types { Lex->create_info.row_type= $3; } @@ -779,16 +851,20 @@ create_table_option: { /* Move the union list to the merge_list */ LEX *lex=Lex; - TABLE_LIST *table_list= (TABLE_LIST*) lex->table_list.first; - lex->create_info.merge_list= lex->table_list; + TABLE_LIST *table_list= (TABLE_LIST*) lex->select->table_list.first; + lex->create_info.merge_list= lex->select->table_list; lex->create_info.merge_list.elements--; lex->create_info.merge_list.first= (byte*) (table_list->next); - lex->table_list.elements=1; - lex->table_list.next= (byte**) &(table_list->next); + lex->select->table_list.elements=1; + lex->select->table_list.next= (byte**) &(table_list->next); table_list->next=0; lex->create_info.used_fields|= HA_CREATE_USED_UNION; } - ; + | CHARSET opt_equal ident {} + | CHAR_SYM SET opt_equal ident {} + | INSERT_METHOD EQ merge_insert_types { Lex->create_info.merge_insert_method= $3; Lex->create_info.used_fields|= HA_CREATE_USED_INSERT_METHOD;} + | DATA_SYM DIRECTORY_SYM EQ TEXT_STRING { Lex->create_info.data_file_name= $4.str; } + | INDEX DIRECTORY_SYM EQ TEXT_STRING { Lex->create_info.index_file_name= $4.str; }; table_types: ISAM_SYM { $$= DB_TYPE_ISAM; } @@ -796,86 +872,98 @@ table_types: | MERGE_SYM { $$= DB_TYPE_MRG_MYISAM; } | HEAP_SYM { $$= DB_TYPE_HEAP; } | BERKELEY_DB_SYM { $$= DB_TYPE_BERKELEY_DB; } - | INNOBASE_SYM { $$= DB_TYPE_INNOBASE; } - | GEMINI_SYM { $$= DB_TYPE_GEMINI; } - ; + | INNOBASE_SYM { $$= DB_TYPE_INNODB; }; row_types: DEFAULT { $$= ROW_TYPE_DEFAULT; } | FIXED_SYM { $$= ROW_TYPE_FIXED; } | DYNAMIC_SYM { $$= ROW_TYPE_DYNAMIC; } - | COMPRESSED_SYM { $$= ROW_TYPE_COMPRESSED; } - ; + | COMPRESSED_SYM { $$= ROW_TYPE_COMPRESSED; }; raid_types: RAID_STRIPED_SYM { $$= RAID_TYPE_0; } | RAID_0_SYM { $$= RAID_TYPE_0; } - | ULONG_NUM { $$=$1;} - ; + | ULONG_NUM { $$=$1;}; + +merge_insert_types: + NO_SYM { $$= MERGE_INSERT_DISABLED; } + | FIRST_SYM { $$= MERGE_INSERT_TO_FIRST; } + | LAST_SYM { $$= MERGE_INSERT_TO_LAST; }; opt_select_from: /* empty */ - | select_from select_lock_type - ; + | select_from select_lock_type; udf_func_type: /* empty */ { $$ = UDFTYPE_FUNCTION; } - | AGGREGATE_SYM { $$ = UDFTYPE_AGGREGATE; } - ; + | AGGREGATE_SYM { $$ = UDFTYPE_AGGREGATE; }; udf_type: STRING_SYM {$$ = (int) STRING_RESULT; } | REAL {$$ = (int) REAL_RESULT; } - | INT_SYM {$$ = (int) INT_RESULT; } - ; + | INT_SYM {$$ = (int) INT_RESULT; }; field_list: field_list_item - | field_list ',' field_list_item - ; + | field_list ',' field_list_item; + field_list_item: - field_spec + column_def + | key_def + ; + +column_def: + field_spec check_constraint | field_spec references { Lex->col_list.empty(); /* Alloced by sql_alloc */ } - | key_type opt_ident '(' key_list ')' + ; + +key_def: + key_type opt_ident '(' key_list ')' { - Lex->key_list.push_back(new Key($1,$2,Lex->col_list)); - Lex->col_list.empty(); /* Alloced by sql_alloc */ + LEX *lex=Lex; + lex->key_list.push_back(new Key($1,$2,lex->col_list)); + lex->col_list.empty(); /* Alloced by sql_alloc */ } | opt_constraint FOREIGN KEY_SYM opt_ident '(' key_list ')' references { Lex->col_list.empty(); /* Alloced by sql_alloc */ } - | opt_constraint CHECK_SYM '(' expr ')' + | opt_constraint check_constraint { Lex->col_list.empty(); /* Alloced by sql_alloc */ } ; -opt_constraint: +check_constraint: /* empty */ - | CONSTRAINT opt_ident + | CHECK_SYM expr ; +opt_constraint: + /* empty */ + | CONSTRAINT opt_ident; + field_spec: field_ident { - Lex->length=Lex->dec=0; Lex->type=0; Lex->interval=0; - Lex->default_value=0; + LEX *lex=Lex; + lex->length=lex->dec=0; lex->type=0; lex->interval=0; + lex->default_value=0; } type opt_attribute { + LEX *lex=Lex; if (add_field_to_list($1.str, (enum enum_field_types) $3, - Lex->length,Lex->dec,Lex->type, - Lex->default_value,Lex->change, - Lex->interval)) + lex->length,lex->dec,lex->type, + lex->default_value,lex->change, + lex->interval)) YYABORT; - } - ; + }; type: int_type opt_len field_options { Lex->length=$2; $$=$1; } @@ -925,84 +1013,82 @@ type: { $$=FIELD_TYPE_DECIMAL;} | ENUM {Lex->interval_list.empty();} '(' string_list ')' { - Lex->interval=typelib(Lex->interval_list); + LEX *lex=Lex; + lex->interval=typelib(lex->interval_list); $$=FIELD_TYPE_ENUM; } | SET { Lex->interval_list.empty();} '(' string_list ')' { - Lex->interval=typelib(Lex->interval_list); + LEX *lex=Lex; + lex->interval=typelib(lex->interval_list); $$=FIELD_TYPE_SET; - } - ; + }; char: CHAR_SYM {} | NCHAR_SYM {} - | NATIONAL_SYM CHAR_SYM {} - ; + | NATIONAL_SYM CHAR_SYM {}; varchar: char VARYING {} | VARCHAR {} | NATIONAL_SYM VARCHAR {} - | NCHAR_SYM VARCHAR {} - ; + | NCHAR_SYM VARCHAR {}; int_type: INT_SYM { $$=FIELD_TYPE_LONG; } | TINYINT { $$=FIELD_TYPE_TINY; } | SMALLINT { $$=FIELD_TYPE_SHORT; } | MEDIUMINT { $$=FIELD_TYPE_INT24; } - | BIGINT { $$=FIELD_TYPE_LONGLONG; } - ; + | BIGINT { $$=FIELD_TYPE_LONGLONG; }; real_type: REAL { $$= current_thd->sql_mode & MODE_REAL_AS_FLOAT ? FIELD_TYPE_FLOAT : FIELD_TYPE_DOUBLE; } | DOUBLE_SYM { $$=FIELD_TYPE_DOUBLE; } - | DOUBLE_SYM PRECISION { $$=FIELD_TYPE_DOUBLE; } - ; + | DOUBLE_SYM PRECISION { $$=FIELD_TYPE_DOUBLE; }; + float_options: /* empty */ {} | '(' NUM ')' { Lex->length=$2.str; } - | '(' NUM ',' NUM ')' { Lex->length=$2.str; Lex->dec=$4.str; } - ; + | precision {}; + +precision: + '(' NUM ',' NUM ')' + { + LEX *lex=Lex; + lex->length=$2.str; lex->dec=$4.str; + }; field_options: /* empty */ {} - | field_opt_list {} - ; + | field_opt_list {}; field_opt_list: field_opt_list field_option {} - | field_option {} - ; + | field_option {}; field_option: - UNSIGNED { Lex->type|= UNSIGNED_FLAG;} - | ZEROFILL { Lex->type|= UNSIGNED_FLAG | ZEROFILL_FLAG; } - ; + SIGNED_SYM {} + | UNSIGNED { Lex->type|= UNSIGNED_FLAG;} + | ZEROFILL { Lex->type|= UNSIGNED_FLAG | ZEROFILL_FLAG; }; opt_len: /* empty */ { $$=(char*) 0; } /* use default length */ - | '(' NUM ')' { $$=$2.str; } - ; + | '(' NUM ')' { $$=$2.str; }; opt_precision: /* empty */ {} - | '(' NUM ',' NUM ')' { Lex->length=$2.str; Lex->dec=$4.str; } - ; + | precision {}; opt_attribute: /* empty */ {} - | opt_attribute_list {} - ; + | opt_attribute_list {}; opt_attribute_list: opt_attribute_list attribute {} - | attribute - ; + | attribute; attribute: NULL_SYM { Lex->type&= ~ NOT_NULL_FLAG; } @@ -1012,11 +1098,12 @@ attribute: | PRIMARY_SYM KEY_SYM { Lex->type|= PRI_KEY_FLAG | NOT_NULL_FLAG; } | UNIQUE_SYM { Lex->type|= UNIQUE_FLAG; } | UNIQUE_SYM KEY_SYM { Lex->type|= UNIQUE_KEY_FLAG; } - ; + | COMMENT_SYM text_literal {}; opt_binary: /* empty */ {} | BINARY { Lex->type|=BINARY_FLAG; } + | CHAR_SYM SET opt_equal ident {} ; references: @@ -1024,33 +1111,29 @@ references: | REFERENCES table_ident '(' key_list ')' opt_on_delete { Lex->col_list.empty(); /* Alloced by sql_alloc */ - } - ; + }; opt_on_delete: /* empty */ {} - | opt_on_delete_list {} - ; + | opt_on_delete_list {}; opt_on_delete_list: opt_on_delete_list opt_on_delete_item {} - | opt_on_delete_item {} - ; + | opt_on_delete_item {}; + opt_on_delete_item: ON DELETE_SYM delete_option {} | ON UPDATE_SYM delete_option {} | MATCH FULL {} - | MATCH PARTIAL {} - ; + | MATCH PARTIAL {}; delete_option: RESTRICT {} | CASCADE {} | SET NULL_SYM {} | NO_SYM ACTION {} - | SET DEFAULT {} - ; + | SET DEFAULT {}; key_type: opt_constraint PRIMARY_SYM KEY_SYM { $$= Key::PRIMARY; } @@ -1058,44 +1141,37 @@ key_type: | FULLTEXT_SYM { $$= Key::FULLTEXT; } | FULLTEXT_SYM key_or_index { $$= Key::FULLTEXT; } | opt_constraint UNIQUE_SYM { $$= Key::UNIQUE; } - | opt_constraint UNIQUE_SYM key_or_index { $$= Key::UNIQUE; } - ; + | opt_constraint UNIQUE_SYM key_or_index { $$= Key::UNIQUE; }; key_or_index: KEY_SYM {} - | INDEX {} - ; + | INDEX {}; keys_or_index: KEYS {} | INDEX {} - ; + | INDEXES {}; opt_unique_or_fulltext: /* empty */ { $$= Key::MULTIPLE; } | UNIQUE_SYM { $$= Key::UNIQUE; } - | FULLTEXT_SYM { $$= Key::FULLTEXT; } - ; + | FULLTEXT_SYM { $$= Key::FULLTEXT; }; key_list: key_list ',' key_part order_dir { Lex->col_list.push_back($3); } - | key_part order_dir { Lex->col_list.push_back($1); } - ; + | key_part order_dir { Lex->col_list.push_back($1); }; key_part: ident { $$=new key_part_spec($1.str); } - | ident '(' NUM ')' { $$=new key_part_spec($1.str,(uint) atoi($3.str)); } - ; + | ident '(' NUM ')' { $$=new key_part_spec($1.str,(uint) atoi($3.str)); }; opt_ident: /* empty */ { $$=(char*) 0; } /* Defaultlength */ - | field_ident { $$=$1.str; } - ; + | field_ident { $$=$1.str; }; string_list: text_string { Lex->interval_list.push_back($1); } - | string_list ',' text_string { Lex->interval_list.push_back($3); } - ; + | string_list ',' text_string { Lex->interval_list.push_back($3); }; /* ** Alter table @@ -1107,7 +1183,7 @@ alter: LEX *lex=Lex; lex->sql_command = SQLCOM_ALTER_TABLE; lex->name=0; - if (!add_table_to_list($4, NULL,1)) + if (!add_table_to_list($4, NULL, TL_OPTION_UPDATING)) YYABORT; lex->drop_primary=0; lex->create_list.empty(); @@ -1115,12 +1191,15 @@ alter: lex->col_list.empty(); lex->drop_list.empty(); lex->alter_list.empty(); - lex->order_list.elements=0; - lex->order_list.first=0; - lex->order_list.next= (byte**) &lex->order_list.first; - lex->db=lex->name=0; + lex->select->order_list.elements=0; + lex->select->order_list.first=0; + lex->select->order_list.next= (byte**) &lex->select->order_list.first; + lex->select->db=lex->name=0; bzero((char*) &lex->create_info,sizeof(lex->create_info)); lex->create_info.db_type= DB_TYPE_DEFAULT; + lex->create_info.row_type= ROW_TYPE_NOT_USED; + lex->alter_keys_onoff=LEAVE_AS_IS; + lex->simple_alter=1; } alter_list {} @@ -1128,91 +1207,152 @@ alter: alter_list: | alter_list_item - | alter_list ',' alter_list_item - ; + | alter_list ',' alter_list_item; add_column: - ADD opt_column { Lex->change=0;} - ; + ADD opt_column { Lex->change=0; }; alter_list_item: - add_column field_list_item opt_place - | add_column '(' field_list ')' - | CHANGE opt_column field_ident { Lex->change= $3.str; } field_spec + add_column column_def opt_place { Lex->simple_alter=0; } + | ADD key_def { Lex->simple_alter=0; } + | add_column '(' field_list ')' { Lex->simple_alter=0; } + | CHANGE opt_column field_ident + { + LEX *lex=Lex; + lex->change= $3.str; lex->simple_alter=0; + } + field_spec opt_place | MODIFY_SYM opt_column field_ident { - Lex->length=Lex->dec=0; Lex->type=0; Lex->interval=0; - Lex->default_value=0; + LEX *lex=Lex; + lex->length=lex->dec=0; lex->type=0; lex->interval=0; + lex->default_value=0; + lex->simple_alter=0; } type opt_attribute { + LEX *lex=Lex; if (add_field_to_list($3.str, (enum enum_field_types) $5, - Lex->length,Lex->dec,Lex->type, - Lex->default_value, $3.str, - Lex->interval)) - YYABORT; + lex->length,lex->dec,lex->type, + lex->default_value, $3.str, + lex->interval)) + YYABORT; } + opt_place | DROP opt_column field_ident opt_restrict - { Lex->drop_list.push_back(new Alter_drop(Alter_drop::COLUMN, - $3.str)); } - | DROP PRIMARY_SYM KEY_SYM { Lex->drop_primary=1; } - | DROP FOREIGN KEY_SYM opt_ident {} + { + LEX *lex=Lex; + lex->drop_list.push_back(new Alter_drop(Alter_drop::COLUMN, + $3.str)); lex->simple_alter=0; + } + | DROP PRIMARY_SYM KEY_SYM + { + LEX *lex=Lex; + lex->drop_primary=1; lex->simple_alter=0; + } + | DROP FOREIGN KEY_SYM opt_ident { Lex->simple_alter=0; } | DROP key_or_index field_ident - { Lex->drop_list.push_back(new Alter_drop(Alter_drop::KEY, - $3.str)); } + { + LEX *lex=Lex; + lex->drop_list.push_back(new Alter_drop(Alter_drop::KEY, + $3.str)); + lex->simple_alter=0; + } + | DISABLE_SYM KEYS { Lex->alter_keys_onoff=DISABLE; } + | ENABLE_SYM KEYS { Lex->alter_keys_onoff=ENABLE; } | ALTER opt_column field_ident SET DEFAULT literal - { Lex->alter_list.push_back(new Alter_column($3.str,$6)); } + { + LEX *lex=Lex; + lex->alter_list.push_back(new Alter_column($3.str,$6)); + lex->simple_alter=0; + } | ALTER opt_column field_ident DROP DEFAULT - { Lex->alter_list.push_back(new Alter_column($3.str,(Item*) 0)); } - | RENAME opt_to table_alias table_ident - { Lex->db=$4->db.str ; Lex->name= $4->table.str; } - | create_table_options - | order_clause - ; + { + LEX *lex=Lex; + lex->alter_list.push_back(new Alter_column($3.str,(Item*) 0)); + lex->simple_alter=0; + } + | RENAME opt_to table_ident + { + LEX *lex=Lex; + lex->select->db=$3->db.str; + lex->name= $3->table.str; + } + | create_table_options { Lex->simple_alter=0; } + | order_clause { Lex->simple_alter=0; }; opt_column: /* empty */ {} - | COLUMN_SYM {} - ; + | COLUMN_SYM {}; opt_ignore: /* empty */ { Lex->duplicates=DUP_ERROR; } - | IGNORE_SYM { Lex->duplicates=DUP_IGNORE; } - ; + | IGNORE_SYM { Lex->duplicates=DUP_IGNORE; }; opt_restrict: /* empty */ {} | RESTRICT {} - | CASCADE {} - ; + | CASCADE {}; opt_place: /* empty */ {} | AFTER_SYM ident { store_position_for_column($2.str); } - | FIRST_SYM { store_position_for_column(first_keyword); } - ; + | FIRST_SYM { store_position_for_column(first_keyword); }; opt_to: /* empty */ {} | TO_SYM {} - | AS {} - ; + | EQ {} + | AS {}; +/* + * The first two deprecate the last two--delete the last two for 4.1 release + */ slave: - SLAVE START_SYM + START_SYM SLAVE slave_thread_opts + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_SLAVE_START; + lex->type = 0; + } + | + STOP_SYM SLAVE slave_thread_opts { - Lex->sql_command = SQLCOM_SLAVE_START; - Lex->type = 0; + LEX *lex=Lex; + lex->sql_command = SQLCOM_SLAVE_STOP; + lex->type = 0; } | - SLAVE STOP_SYM + SLAVE START_SYM slave_thread_opts { - Lex->sql_command = SQLCOM_SLAVE_STOP; - Lex->type = 0; + LEX *lex=Lex; + lex->sql_command = SQLCOM_SLAVE_START; + lex->type = 0; } + | + SLAVE STOP_SYM slave_thread_opts + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_SLAVE_STOP; + lex->type = 0; + }; + +start: + START_SYM TRANSACTION_SYM { Lex->sql_command = SQLCOM_BEGIN;} + {} ; +slave_thread_opts: + slave_thread_opt + | slave_thread_opts ',' slave_thread_opt; + +slave_thread_opt: + /*empty*/ {} + | SQL_THREAD { Lex->slave_thd_opt|=SLAVE_SQL; } + | IO_THREAD { Lex->slave_thd_opt|=SLAVE_IO; } + ; + restore: RESTORE_SYM table_or_tables { @@ -1221,8 +1361,7 @@ restore: table_list FROM TEXT_STRING { Lex->backup_dir = $6.str; - } - ; + }; backup: BACKUP_SYM table_or_tables @@ -1232,43 +1371,38 @@ backup: table_list TO_SYM TEXT_STRING { Lex->backup_dir = $6.str; - } - ; + }; repair: REPAIR table_or_tables { - Lex->sql_command = SQLCOM_REPAIR; - Lex->check_opt.init(); + LEX *lex=Lex; + lex->sql_command = SQLCOM_REPAIR; + lex->check_opt.init(); } - table_list opt_mi_check_type + table_list opt_mi_repair_type {} ; -opt_mi_check_type: +opt_mi_repair_type: /* empty */ { Lex->check_opt.flags = T_MEDIUM; } - | TYPE_SYM EQ mi_check_types {} - | mi_check_types {} - ; + | mi_repair_types {}; -mi_check_types: - mi_check_type {} - | mi_check_type mi_check_types {} - ; +mi_repair_types: + mi_repair_type {} + | mi_repair_type mi_repair_types {}; -mi_check_type: - QUICK { Lex->check_opt.quick = 1; } - | FAST_SYM { Lex->check_opt.flags|= T_FAST; } - | MEDIUM_SYM { Lex->check_opt.flags|= T_MEDIUM; } +mi_repair_type: + QUICK { Lex->check_opt.flags|= T_QUICK; } | EXTENDED_SYM { Lex->check_opt.flags|= T_EXTEND; } - | CHANGED { Lex->check_opt.flags|= T_CHECK_ONLY_CHANGED; } - ; + | USE_FRM { Lex->check_opt.sql_flags|= TT_USEFRM; }; analyze: ANALYZE_SYM table_or_tables { - Lex->sql_command = SQLCOM_ANALYZE; - Lex->check_opt.init(); + LEX *lex=Lex; + lex->sql_command = SQLCOM_ANALYZE; + lex->check_opt.init(); } table_list opt_mi_check_type {} @@ -1277,18 +1411,35 @@ analyze: check: CHECK_SYM table_or_tables { - Lex->sql_command = SQLCOM_CHECK; - Lex->check_opt.init(); + LEX *lex=Lex; + lex->sql_command = SQLCOM_CHECK; + lex->check_opt.init(); } table_list opt_mi_check_type {} ; +opt_mi_check_type: + /* empty */ { Lex->check_opt.flags = T_MEDIUM; } + | mi_check_types {}; + +mi_check_types: + mi_check_type {} + | mi_check_type mi_check_types {}; + +mi_check_type: + QUICK { Lex->check_opt.flags|= T_QUICK; } + | FAST_SYM { Lex->check_opt.flags|= T_FAST; } + | MEDIUM_SYM { Lex->check_opt.flags|= T_MEDIUM; } + | EXTENDED_SYM { Lex->check_opt.flags|= T_EXTEND; } + | CHANGED { Lex->check_opt.flags|= T_CHECK_ONLY_CHANGED; }; + optimize: OPTIMIZE table_or_tables { - Lex->sql_command = SQLCOM_OPTIMIZE; - Lex->check_opt.init(); + LEX *lex=Lex; + lex->sql_command = SQLCOM_OPTIMIZE; + lex->check_opt.init(); } table_list opt_mi_check_type {} @@ -1305,17 +1456,15 @@ rename: table_to_table_list: table_to_table - | table_to_table_list ',' table_to_table - {} - ; + | table_to_table_list ',' table_to_table; table_to_table: table_ident TO_SYM table_ident - { if (!add_table_to_list($1,NULL,1,TL_IGNORE) || - !add_table_to_list($3,NULL,1,TL_IGNORE)) + { + if (!add_table_to_list($1, NULL, TL_OPTION_UPDATING, TL_IGNORE) || + !add_table_to_list($3, NULL, TL_OPTION_UPDATING, TL_IGNORE)) YYABORT; - } - ; + }; /* Select : retrieve data from table @@ -1323,54 +1472,86 @@ table_to_table: select: - SELECT_SYM + select_init { Lex->sql_command=SQLCOM_SELECT; }; + +select_init: + SELECT_SYM select_part2 { Select->braces= 0; } opt_union + | + '(' SELECT_SYM select_part2 ')' { Select->braces= 1;} union_opt; + + +select_part2: { LEX *lex=Lex; - lex->sql_command= SQLCOM_SELECT; lex->lock_option=TL_READ; - mysql_init_select(lex); + mysql_init_select(lex); } - select_options select_item_list select_into select_lock_type - {} - ; + select_options select_item_list select_into select_lock_type; select_into: - /* empty */ + limit_clause {} | select_from | opt_into select_from - | select_from opt_into - ; + | select_from opt_into; select_from: - FROM join_table_list where_clause group_clause having_clause opt_order_clause limit_clause procedure_clause - ; + FROM join_table_list where_clause group_clause having_clause opt_order_clause limit_clause procedure_clause; + select_options: /* empty*/ - | select_option_list - ; + | select_option_list; select_option_list: select_option_list select_option - | select_option - ; + | select_option; select_option: - STRAIGHT_JOIN { Lex->options|= SELECT_STRAIGHT_JOIN; } - | HIGH_PRIORITY { Lex->lock_option= TL_READ_HIGH_PRIORITY; } - | DISTINCT { Lex->options|= SELECT_DISTINCT; } - | SQL_SMALL_RESULT { Lex->options|= SELECT_SMALL_RESULT; } - | SQL_BIG_RESULT { Lex->options|= SELECT_BIG_RESULT; } - | SQL_BUFFER_RESULT { Lex->options|= OPTION_BUFFER_RESULT; } + STRAIGHT_JOIN { Select->options|= SELECT_STRAIGHT_JOIN; } + | HIGH_PRIORITY + { + if (check_simple_select()) + YYABORT; + Lex->lock_option= TL_READ_HIGH_PRIORITY; + } + | DISTINCT { Select->options|= SELECT_DISTINCT; } + | SQL_SMALL_RESULT { Select->options|= SELECT_SMALL_RESULT; } + | SQL_BIG_RESULT { Select->options|= SELECT_BIG_RESULT; } + | SQL_BUFFER_RESULT + { + if (check_simple_select()) + YYABORT; + Select->options|= OPTION_BUFFER_RESULT; + } + | SQL_CALC_FOUND_ROWS + { + if (check_simple_select()) + YYABORT; + Select->options|= OPTION_FOUND_ROWS; + } + | SQL_NO_CACHE_SYM { current_thd->safe_to_cache_query=0; } + | SQL_CACHE_SYM { Select->options|= OPTION_TO_QUERY_CACHE; } | ALL {} ; select_lock_type: /* empty */ | FOR_SYM UPDATE_SYM - { Lex->lock_option= TL_WRITE; } + { + LEX *lex=Lex; + if (check_simple_select()) + YYABORT; + lex->lock_option= TL_WRITE; + lex->thd->safe_to_cache_query=0; + } | LOCK_SYM IN_SYM SHARE_SYM MODE_SYM - { Lex->lock_option= TL_READ_WITH_SHARED_LOCKS; } + { + LEX *lex=Lex; + if (check_simple_select()) + YYABORT; + lex->lock_option= TL_READ_WITH_SHARED_LOCKS; + lex->thd->safe_to_cache_query=0; + } ; select_item_list: @@ -1380,8 +1561,8 @@ select_item_list: { if (add_item_to_list(new Item_field(NULL,NULL,"*"))) YYABORT; - } - ; + }; + select_item: remember_name select_item2 remember_end select_alias @@ -1392,39 +1573,32 @@ select_item: $2->set_name($4.str); else if (!$2->name) $2->set_name($1,(uint) ($3 - $1)); - } - ; + }; remember_name: - { $$=(char*) Lex->tok_start; } - ; + { $$=(char*) Lex->tok_start; }; remember_end: - { $$=(char*) Lex->tok_end; } - ; + { $$=(char*) Lex->tok_end; }; select_item2: table_wild { $$=$1; } /* table.* */ - | expr { $$=$1; } - ; + | expr { $$=$1; }; select_alias: { $$.str=0;} | AS ident { $$=$2; } | AS TEXT_STRING { $$=$2; } | ident { $$=$1; } - | TEXT_STRING { $$=$1; } - ; + | TEXT_STRING { $$=$1; }; optional_braces: /* empty */ {} - | '(' ')' {} - ; + | '(' ')' {}; /* all possible expressions */ expr: expr_expr {$$ = $1; } - | simple_expr {$$ = $1; } - ; + | simple_expr {$$ = $1; }; /* expressions that begin with 'expr' */ expr_expr: @@ -1438,6 +1612,7 @@ expr_expr: { $$= new Item_func_not(new Item_func_between($1,$4,$6)); } | expr OR_OR_CONCAT expr { $$= or_or_concat($1,$3); } | expr OR expr { $$= new Item_cond_or($1,$3); } + | expr XOR expr { $$= new Item_cond_xor($1,$3); } | expr AND expr { $$= new Item_cond_and($1,$3); } | expr LIKE simple_expr opt_escape { $$= new Item_func_like($1,$3,$4); } | expr NOT LIKE simple_expr opt_escape { $$= new Item_func_not(new Item_func_like($1,$4,$5));} @@ -1459,13 +1634,13 @@ expr_expr: | expr '*' expr { $$= new Item_func_mul($1,$3); } | expr '/' expr { $$= new Item_func_div($1,$3); } | expr '|' expr { $$= new Item_func_bit_or($1,$3); } + | expr '^' expr { $$= new Item_func_bit_xor($1,$3); } | expr '&' expr { $$= new Item_func_bit_and($1,$3); } | expr '%' expr { $$= new Item_func_mod($1,$3); } | expr '+' INTERVAL_SYM expr interval { $$= new Item_date_add_interval($1,$4,$5,0); } | expr '-' INTERVAL_SYM expr interval - { $$= new Item_date_add_interval($1,$4,$5,1); } - ; + { $$= new Item_date_add_interval($1,$4,$5,1); }; /* expressions that begin with 'expr' that do NOT follow IN_SYM */ no_in_expr: @@ -1475,6 +1650,7 @@ no_in_expr: { $$= new Item_func_not(new Item_func_between($1,$4,$6)); } | no_in_expr OR_OR_CONCAT expr { $$= or_or_concat($1,$3); } | no_in_expr OR expr { $$= new Item_cond_or($1,$3); } + | no_in_expr XOR expr { $$= new Item_cond_xor($1,$3); } | no_in_expr AND expr { $$= new Item_cond_and($1,$3); } | no_in_expr LIKE simple_expr opt_escape { $$= new Item_func_like($1,$3,$4); } | no_in_expr NOT LIKE simple_expr opt_escape { $$= new Item_func_not(new Item_func_like($1,$4,$5)); } @@ -1496,14 +1672,14 @@ no_in_expr: | no_in_expr '*' expr { $$= new Item_func_mul($1,$3); } | no_in_expr '/' expr { $$= new Item_func_div($1,$3); } | no_in_expr '|' expr { $$= new Item_func_bit_or($1,$3); } + | no_in_expr '^' expr { $$= new Item_func_bit_xor($1,$3); } | no_in_expr '&' expr { $$= new Item_func_bit_and($1,$3); } | no_in_expr '%' expr { $$= new Item_func_mod($1,$3); } | no_in_expr '+' INTERVAL_SYM expr interval { $$= new Item_date_add_interval($1,$4,$5,0); } | no_in_expr '-' INTERVAL_SYM expr interval { $$= new Item_date_add_interval($1,$4,$5,1); } - | simple_expr - ; + | simple_expr; /* expressions that begin with 'expr' that does NOT follow AND */ no_and_expr: @@ -1517,6 +1693,7 @@ no_and_expr: { $$= new Item_func_not(new Item_func_between($1,$4,$6)); } | no_and_expr OR_OR_CONCAT expr { $$= or_or_concat($1,$3); } | no_and_expr OR expr { $$= new Item_cond_or($1,$3); } + | no_and_expr XOR expr { $$= new Item_cond_xor($1,$3); } | no_and_expr LIKE simple_expr opt_escape { $$= new Item_func_like($1,$3,$4); } | no_and_expr NOT LIKE simple_expr opt_escape { $$= new Item_func_not(new Item_func_like($1,$4,$5)); } | no_and_expr REGEXP expr { $$= new Item_func_regex($1,$3); } @@ -1537,21 +1714,33 @@ no_and_expr: | no_and_expr '*' expr { $$= new Item_func_mul($1,$3); } | no_and_expr '/' expr { $$= new Item_func_div($1,$3); } | no_and_expr '|' expr { $$= new Item_func_bit_or($1,$3); } + | no_and_expr '^' expr { $$= new Item_func_bit_xor($1,$3); } | no_and_expr '&' expr { $$= new Item_func_bit_and($1,$3); } | no_and_expr '%' expr { $$= new Item_func_mod($1,$3); } | no_and_expr '+' INTERVAL_SYM expr interval { $$= new Item_date_add_interval($1,$4,$5,0); } | no_and_expr '-' INTERVAL_SYM expr interval { $$= new Item_date_add_interval($1,$4,$5,1); } - | simple_expr - ; + | simple_expr; simple_expr: simple_ident | literal - | '@' ident_or_text SET_VAR expr { $$= new Item_func_set_user_var($2,$4); } - | '@' ident_or_text { $$= new Item_func_get_user_var($2); } - | '@' '@' ident_or_text { if (!($$= get_system_var($3))) YYABORT; } + | '@' ident_or_text SET_VAR expr + { + $$= new Item_func_set_user_var($2,$4); + current_thd->safe_to_cache_query=0; + } + | '@' ident_or_text + { + $$= new Item_func_get_user_var($2); + current_thd->safe_to_cache_query=0; + } + | '@' '@' opt_var_ident_type ident_or_text + { + if (!($$= get_system_var((enum_var_type) $3, $4))) + YYABORT; + } | sum_expr | '-' expr %prec NEG { $$= new Item_func_neg($2); } | '~' expr %prec NEG { $$= new Item_func_bit_neg($2); } @@ -1559,15 +1748,17 @@ simple_expr: | '!' expr %prec NEG { $$= new Item_func_not($2); } | '(' expr ')' { $$= $2; } | '{' ident expr '}' { $$= $3; } - | MATCH '(' ident_list ')' AGAINST '(' expr ')' - { Lex->ftfunc_list.push_back( - (Item_func_match *)($$=new Item_func_match(*$3,$7))); } - | MATCH ident_list AGAINST '(' expr ')' - { Lex->ftfunc_list.push_back( - (Item_func_match *)($$=new Item_func_match(*$2,$5))); } + | MATCH ident_list_arg AGAINST '(' expr ')' + { Select->ftfunc_list.push_back((Item_func_match *) + ($$=new Item_func_match_nl(*$2,$5))); } + | MATCH ident_list_arg AGAINST '(' expr IN_SYM BOOLEAN_SYM MODE_SYM ')' + { Select->ftfunc_list.push_back((Item_func_match *) + ($$=new Item_func_match_bool(*$2,$5))); } | BINARY expr %prec NEG { $$= new Item_func_binary($2); } + | CAST_SYM '(' expr AS cast_type ')' { $$= create_func_cast($3, $5); } | CASE_SYM opt_expr WHEN_SYM when_list opt_else END { $$= new Item_func_case(* $4, $2, $5 ); } + | CONVERT_SYM '(' expr ',' cast_type ')' { $$= create_func_cast($3, $5); } | FUNC_ARG0 '(' ')' { $$= ((Item*(*)(void))($1.symbol->create_func))();} | FUNC_ARG1 '(' expr ')' @@ -1589,27 +1780,45 @@ simple_expr: | CONCAT_WS '(' expr ',' expr_list ')' { $$= new Item_func_concat_ws($3, *$5); } | CURDATE optional_braces - { $$= new Item_func_curdate(); } + { $$= new Item_func_curdate(); current_thd->safe_to_cache_query=0; } | CURTIME optional_braces - { $$= new Item_func_curtime(); } + { $$= new Item_func_curtime(); current_thd->safe_to_cache_query=0; } | CURTIME '(' expr ')' - { $$= new Item_func_curtime($3); } + { + $$= new Item_func_curtime($3); + current_thd->safe_to_cache_query=0; + } | DATE_ADD_INTERVAL '(' expr ',' INTERVAL_SYM expr interval ')' { $$= new Item_date_add_interval($3,$6,$7,0); } | DATE_SUB_INTERVAL '(' expr ',' INTERVAL_SYM expr interval ')' { $$= new Item_date_add_interval($3,$6,$7,1); } | DATABASE '(' ')' - { $$= new Item_func_database(); } + { + $$= new Item_func_database(); + current_thd->safe_to_cache_query=0; + } | ELT_FUNC '(' expr ',' expr_list ')' { $$= new Item_func_elt($3, *$5); } | MAKE_SET_SYM '(' expr ',' expr_list ')' { $$= new Item_func_make_set($3, *$5); } - | ENCRYPT '(' expr ')' { $$= new Item_func_encrypt($3); } + | ENCRYPT '(' expr ')' + { + $$= new Item_func_encrypt($3); + current_thd->safe_to_cache_query=0; + } | ENCRYPT '(' expr ',' expr ')' { $$= new Item_func_encrypt($3,$5); } | DECODE_SYM '(' expr ',' TEXT_STRING ')' { $$= new Item_func_decode($3,$5.str); } | ENCODE_SYM '(' expr ',' TEXT_STRING ')' { $$= new Item_func_encode($3,$5.str); } + | DES_DECRYPT_SYM '(' expr ')' + { $$= new Item_func_des_decrypt($3); } + | DES_DECRYPT_SYM '(' expr ',' expr ')' + { $$= new Item_func_des_decrypt($3,$5); } + | DES_ENCRYPT_SYM '(' expr ')' + { $$= new Item_func_des_encrypt($3); } + | DES_ENCRYPT_SYM '(' expr ',' expr ')' + { $$= new Item_func_des_encrypt($3,$5); } | EXPORT_SET '(' expr ',' expr ',' expr ')' { $$= new Item_func_export_set($3, $5, $7); } | EXPORT_SET '(' expr ',' expr ',' expr ',' expr ')' @@ -1622,7 +1831,7 @@ simple_expr: { $$= new Item_func_from_unixtime($3); } | FROM_UNIXTIME '(' expr ',' expr ')' { - $$= new Item_func_date_format(new Item_func_from_unixtime($3),$5,0); + $$= new Item_func_date_format (new Item_func_from_unixtime($3),$5,0); } | FIELD_FUNC '(' expr ',' expr_list ')' { $$= new Item_func_field($3, *$5); } @@ -1641,10 +1850,12 @@ simple_expr: { $$= new Item_int((char*) "last_insert_id()", current_thd->insert_id(),21); + current_thd->safe_to_cache_query=0; } | LAST_INSERT_ID '(' expr ')' { $$= new Item_func_set_last_insert_id($3); + current_thd->safe_to_cache_query=0; } | LEFT '(' expr ',' expr ')' { $$= new Item_func_left($3,$5); } @@ -1656,19 +1867,38 @@ simple_expr: { $5->push_front($3); $$= new Item_func_max(*$5); } | LEAST_SYM '(' expr ',' expr_list ')' { $5->push_front($3); $$= new Item_func_min(*$5); } + | LOG_SYM '(' expr ')' + { $$= new Item_func_log($3); } + | LOG_SYM '(' expr ',' expr ')' + { $$= new Item_func_log($3, $5); } + | MASTER_POS_WAIT '(' expr ',' expr ')' + { + $$= new Item_master_pos_wait($3, $5); + current_thd->safe_to_cache_query=0; + } + | MASTER_POS_WAIT '(' expr ',' expr ',' expr ')' + { + $$= new Item_master_pos_wait($3, $5, $7); + current_thd->safe_to_cache_query=0; + } | MINUTE_SYM '(' expr ')' { $$= new Item_func_minute($3); } | MONTH_SYM '(' expr ')' { $$= new Item_func_month($3); } | NOW_SYM optional_braces - { $$= new Item_func_now(); } + { $$= new Item_func_now(); current_thd->safe_to_cache_query=0;} | NOW_SYM '(' expr ')' - { $$= new Item_func_now($3); } - | PASSWORD '(' expr ')' { $$= new Item_func_password($3); } + { $$= new Item_func_now($3); current_thd->safe_to_cache_query=0;} + | PASSWORD '(' expr ')' + { + $$= new Item_func_password($3); + } | POSITION_SYM '(' no_in_expr IN_SYM expr ')' { $$ = new Item_func_locate($5,$3); } - | RAND '(' expr ')' { $$= new Item_func_rand($3); } - | RAND '(' ')' { $$= new Item_func_rand(); } + | RAND '(' expr ')' + { $$= new Item_func_rand($3); current_thd->safe_to_cache_query=0;} + | RAND '(' ')' + { $$= new Item_func_rand(); current_thd->safe_to_cache_query=0;} | REPLACE '(' expr ',' expr ',' expr ')' { $$= new Item_func_replace($3,$5,$7); } | RIGHT '(' expr ',' expr ')' @@ -1743,13 +1973,18 @@ simple_expr: $$ = new Item_func_udf_int($1); } | UNIQUE_USERS '(' text_literal ',' NUM ',' NUM ',' expr_list ')' - { $$= new Item_func_unique_users($3,atoi($5.str),atoi($7.str), * $9); } + { + $$= new Item_func_unique_users($3,atoi($5.str),atoi($7.str), * $9); + } | UNIX_TIMESTAMP '(' ')' - { $$= new Item_func_unix_timestamp(); } + { + $$= new Item_func_unix_timestamp(); + current_thd->safe_to_cache_query=0; + } | UNIX_TIMESTAMP '(' expr ')' { $$= new Item_func_unix_timestamp($3); } | USER '(' ')' - { $$= new Item_func_user(); } + { $$= new Item_func_user(); current_thd->safe_to_cache_query=0; } | WEEK_SYM '(' expr ')' { $$= new Item_func_week($3,new Item_int((char*) "0",0,1)); } | WEEK_SYM '(' expr ',' expr ')' @@ -1761,15 +1996,16 @@ simple_expr: | YEARWEEK '(' expr ',' expr ')' { $$= new Item_func_yearweek($3, $5); } | BENCHMARK_SYM '(' ULONG_NUM ',' expr ')' - { $$=new Item_func_benchmark($3,$5); } + { + $$=new Item_func_benchmark($3,$5); + current_thd->safe_to_cache_query=0; + } | EXTRACT_SYM '(' interval FROM expr ')' - { $$=new Item_extract( $3, $5); } - ; + { $$=new Item_extract( $3, $5); }; udf_expr_list: /* empty */ { $$= NULL; } - | expr_list { $$= $1;} - ; + | expr_list { $$= $1;}; sum_expr: AVG_SYM '(' in_sum_expr ')' @@ -1778,7 +2014,7 @@ sum_expr: { $$=new Item_sum_and($3); } | BIT_OR '(' in_sum_expr ')' { $$=new Item_sum_or($3); } - | COUNT_SYM '(' '*' ')' + | COUNT_SYM '(' opt_all '*' ')' { $$=new Item_sum_count(new Item_int((int32) 0L,1)); } | COUNT_SYM '(' in_sum_expr ')' { $$=new Item_sum_count($3); } @@ -1793,161 +2029,203 @@ sum_expr: | STD_SYM '(' in_sum_expr ')' { $$=new Item_sum_std($3); } | SUM_SYM '(' in_sum_expr ')' - { $$=new Item_sum_sum($3); } - ; + { $$=new Item_sum_sum($3); }; in_sum_expr: - { Lex->in_sum_expr++; } + opt_all + { Select->in_sum_expr++; } expr { - Lex->in_sum_expr--; - $$=$2; - } + Select->in_sum_expr--; + $$=$3; + }; + +cast_type: + BINARY { $$=ITEM_CAST_BINARY; } + | CHAR_SYM { $$=ITEM_CAST_CHAR; } + | SIGNED_SYM { $$=ITEM_CAST_SIGNED_INT; } + | SIGNED_SYM INT_SYM { $$=ITEM_CAST_SIGNED_INT; } + | UNSIGNED { $$=ITEM_CAST_UNSIGNED_INT; } + | UNSIGNED INT_SYM { $$=ITEM_CAST_UNSIGNED_INT; } + | DATE_SYM { $$=ITEM_CAST_DATE; } + | TIME_SYM { $$=ITEM_CAST_TIME; } + | DATETIME { $$=ITEM_CAST_DATETIME; } ; expr_list: - { Lex->expr_list.push_front(new List<Item>); } + { Select->expr_list.push_front(new List<Item>); } expr_list2 - { $$= Lex->expr_list.pop(); } - ; + { $$= Select->expr_list.pop(); }; expr_list2: - expr { Lex->expr_list.head()->push_back($1); } - | expr_list2 ',' expr { Lex->expr_list.head()->push_back($3); } - ; + expr { Select->expr_list.head()->push_back($1); } + | expr_list2 ',' expr { Select->expr_list.head()->push_back($3); }; + +ident_list_arg: + ident_list { $$= $1; } + | '(' ident_list ')' { $$= $2; }; ident_list: - { Lex->expr_list.push_front(new List<Item>); } + { Select->expr_list.push_front(new List<Item>); } ident_list2 - { $$= Lex->expr_list.pop(); } - ; + { $$= Select->expr_list.pop(); }; ident_list2: - simple_ident { Lex->expr_list.head()->push_back($1); } - | ident_list2 ',' simple_ident { Lex->expr_list.head()->push_back($3); } - ; + simple_ident { Select->expr_list.head()->push_back($1); } + | ident_list2 ',' simple_ident { Select->expr_list.head()->push_back($3); }; opt_expr: /* empty */ { $$= NULL; } - | expr { $$= $1; } - ; + | expr { $$= $1; }; opt_else: /* empty */ { $$= NULL; } - | ELSE expr { $$= $2; } - ; + | ELSE expr { $$= $2; }; when_list: - { Lex->when_list.push_front(new List<Item>); } + { Select->when_list.push_front(new List<Item>); } when_list2 - { $$= Lex->when_list.pop(); } - ; + { $$= Select->when_list.pop(); }; when_list2: expr THEN_SYM expr { - Lex->when_list.head()->push_back($1); - Lex->when_list.head()->push_back($3); + SELECT_LEX *sel=Select; + sel->when_list.head()->push_back($1); + sel->when_list.head()->push_back($3); } | when_list2 WHEN_SYM expr THEN_SYM expr { - Lex->when_list.head()->push_back($3); - Lex->when_list.head()->push_back($5); - } - ; + SELECT_LEX *sel=Select; + sel->when_list.head()->push_back($3); + sel->when_list.head()->push_back($5); + }; opt_pad: /* empty */ { $$=new Item_string(" ",1); } - | expr { $$=$1; } - ; + | expr { $$=$1; }; join_table_list: '(' join_table_list ')' { $$=$2; } | join_table { $$=$1; } - | join_table_list normal_join join_table { $$=$3; } - | join_table_list STRAIGHT_JOIN join_table { $$=$3 ; $$->straight=1; } - | join_table_list INNER_SYM JOIN_SYM join_table ON expr - { add_join_on($4,$6); $$=$4; } - | join_table_list INNER_SYM JOIN_SYM join_table - { Lex->db1=$1->db; Lex->table1=$1->alias; - Lex->db2=$4->db; Lex->table2=$4->alias; } - USING '(' using_list ')' - { add_join_on($4,$8); $$=$4; } - | join_table_list LEFT opt_outer JOIN_SYM join_table ON expr + | join_table_list ',' join_table_list { $$=$3; } + | join_table_list normal_join join_table_list { $$=$3; } + | join_table_list STRAIGHT_JOIN join_table_list + { $$=$3 ; $$->straight=1; } + | join_table_list normal_join join_table_list ON expr + { add_join_on($3,$5); $$=$3; } + | join_table_list normal_join join_table_list + USING + { + SELECT_LEX *sel=Select; + sel->db1=$1->db; sel->table1=$1->alias; + sel->db2=$3->db; sel->table2=$3->alias; + } + '(' using_list ')' + { add_join_on($3,$7); $$=$3; } + + | join_table_list LEFT opt_outer JOIN_SYM join_table_list ON expr { add_join_on($5,$7); $5->outer_join|=JOIN_TYPE_LEFT; $$=$5; } - | join_table_list LEFT opt_outer JOIN_SYM join_table - { Lex->db1=$1->db; Lex->table1=$1->alias; - Lex->db2=$5->db; Lex->table2=$5->alias; } + | join_table_list LEFT opt_outer JOIN_SYM join_table_list + { + SELECT_LEX *sel=Select; + sel->db1=$1->db; sel->table1=$1->alias; + sel->db2=$5->db; sel->table2=$5->alias; + } USING '(' using_list ')' { add_join_on($5,$9); $5->outer_join|=JOIN_TYPE_LEFT; $$=$5; } - | join_table_list NATURAL LEFT opt_outer JOIN_SYM join_table + | join_table_list NATURAL LEFT opt_outer JOIN_SYM join_table_list { add_join_natural($1,$6); $6->outer_join|=JOIN_TYPE_LEFT; $$=$6; } - | join_table_list RIGHT opt_outer JOIN_SYM join_table ON expr + | join_table_list RIGHT opt_outer JOIN_SYM join_table_list ON expr { add_join_on($1,$7); $1->outer_join|=JOIN_TYPE_RIGHT; $$=$1; } - | join_table_list RIGHT opt_outer JOIN_SYM join_table - { Lex->db1=$1->db; Lex->table1=$1->alias; - Lex->db2=$5->db; Lex->table2=$5->alias; } + | join_table_list RIGHT opt_outer JOIN_SYM join_table_list + { + SELECT_LEX *sel=Select; + sel->db1=$1->db; sel->table1=$1->alias; + sel->db2=$5->db; sel->table2=$5->alias; + } USING '(' using_list ')' { add_join_on($1,$9); $1->outer_join|=JOIN_TYPE_RIGHT; $$=$1; } - | join_table_list NATURAL RIGHT opt_outer JOIN_SYM join_table + | join_table_list NATURAL RIGHT opt_outer JOIN_SYM join_table_list { add_join_natural($6,$1); $1->outer_join|=JOIN_TYPE_RIGHT; $$=$1; } - | join_table_list NATURAL JOIN_SYM join_table - { add_join_natural($1,$4); $$=$4; } - ; + | join_table_list NATURAL JOIN_SYM join_table_list + { add_join_natural($1,$4); $$=$4; }; normal_join: - ',' {} - | JOIN_SYM {} - | CROSS JOIN_SYM {} + JOIN_SYM {} + | INNER_SYM JOIN_SYM {} + | CROSS JOIN_SYM {} ; join_table: - { Lex->use_index_ptr=Lex->ignore_index_ptr=0; } + { + SELECT_LEX *sel=Select; + sel->use_index_ptr=sel->ignore_index_ptr=0; + sel->table_join_options= 0; + } table_ident opt_table_alias opt_key_definition - { if (!($$=add_table_to_list($2,$3,0,TL_UNLOCK, Lex->use_index_ptr, - Lex->ignore_index_ptr))) YYABORT; } + { + SELECT_LEX *sel=Select; + if (!($$=add_table_to_list($2, $3, sel->table_join_options, + TL_UNLOCK, sel->use_index_ptr, + sel->ignore_index_ptr))) + YYABORT; + } | '{' ident join_table LEFT OUTER JOIN_SYM join_table ON expr '}' - { add_join_on($7,$9); $7->outer_join|=JOIN_TYPE_LEFT; $$=$7; } - ; + { add_join_on($7,$9); $7->outer_join|=JOIN_TYPE_LEFT; $$=$7; }; opt_outer: /* empty */ {} - | OUTER {} - ; + | OUTER {}; opt_key_definition: /* empty */ {} | USE_SYM key_usage_list - { Lex->use_index= *$2; Lex->use_index_ptr= &Lex->use_index; } + { + SELECT_LEX *sel=Select; + sel->use_index= *$2; + sel->use_index_ptr= &sel->use_index; + } + | FORCE_SYM key_usage_list + { + SELECT_LEX *sel=Select; + sel->use_index= *$2; + sel->use_index_ptr= &sel->use_index; + sel->table_join_options|= TL_OPTION_FORCE_INDEX; + } | IGNORE_SYM key_usage_list - { Lex->ignore_index= *$2; Lex->ignore_index_ptr= &Lex->ignore_index;} + { + SELECT_LEX *sel=Select; + sel->ignore_index= *$2; + sel->ignore_index_ptr= &sel->ignore_index; + } ; key_usage_list: - key_or_index { Lex->interval_list.empty(); } '(' key_usage_list2 ')' - { $$= &Lex->interval_list; } - ; + key_or_index { Select->interval_list.empty(); } '(' key_usage_list2 ')' + { $$= &Select->interval_list; }; key_usage_list2: key_usage_list2 ',' ident - { Lex->interval_list.push_back(new String((const char*) $3.str,$3.length)); } + { Select->interval_list.push_back(new String((const char*) $3.str,$3.length)); } | ident - { Lex->interval_list.push_back(new String((const char*) $1.str,$1.length)); } + { Select->interval_list.push_back(new String((const char*) $1.str,$1.length)); } | PRIMARY_SYM - { Lex->interval_list.push_back(new String("PRIMARY",7)); } - ; + { Select->interval_list.push_back(new String("PRIMARY",7)); }; using_list: ident - { if (!($$= new Item_func_eq(new Item_field(Lex->db1,Lex->table1, $1.str), new Item_field(Lex->db2,Lex->table2,$1.str)))) + { + SELECT_LEX *sel=Select; + if (!($$= new Item_func_eq(new Item_field(sel->db1,sel->table1, $1.str), new Item_field(sel->db2,sel->table2,$1.str)))) YYABORT; } | using_list ',' ident { - if (!($$= new Item_cond_and(new Item_func_eq(new Item_field(Lex->db1,Lex->table1,$3.str), new Item_field(Lex->db2,Lex->table2,$3.str)), $1))) + SELECT_LEX *sel=Select; + if (!($$= new Item_cond_and(new Item_func_eq(new Item_field(sel->db1,sel->table1,$3.str), new Item_field(sel->db2,sel->table2,$3.str)), $1))) YYABORT; - } - ; + }; interval: DAY_HOUR_SYM { $$=INTERVAL_DAY_HOUR; } @@ -1962,36 +2240,49 @@ interval: | MONTH_SYM { $$=INTERVAL_MONTH; } | SECOND_SYM { $$=INTERVAL_SECOND; } | YEAR_MONTH_SYM { $$=INTERVAL_YEAR_MONTH; } - | YEAR_SYM { $$=INTERVAL_YEAR; } - ; + | YEAR_SYM { $$=INTERVAL_YEAR; }; table_alias: /* empty */ | AS - | EQ - ; + | EQ; opt_table_alias: /* empty */ { $$=0; } | table_alias ident - { $$= (LEX_STRING*) sql_memdup(&$2,sizeof(LEX_STRING)); } + { $$= (LEX_STRING*) sql_memdup(&$2,sizeof(LEX_STRING)); }; + +opt_all: + /* empty */ + | ALL ; where_clause: - /* empty */ { Lex->where= 0; } - | WHERE expr { Lex->where= $2; } - ; + /* empty */ { Select->where= 0; } + | WHERE expr + { + Select->where= $2; + if ($2) + $2->top_level_item(); + } + ; having_clause: /* empty */ - | HAVING { Lex->create_refs=1; } expr - { Lex->having= $3; Lex->create_refs=0; } + | HAVING { Select->create_refs=1; } expr + { + SELECT_LEX *sel=Select; + sel->having= $3; + sel->create_refs=0; + if ($3) + $3->top_level_item(); + } ; opt_escape: ESCAPE_SYM TEXT_STRING { $$= $2.str; } - | /* empty */ { $$= (char*) "\\"; } - ; + | /* empty */ { $$= (char*) "\\"; }; + /* group by statement in select @@ -1999,14 +2290,32 @@ opt_escape: group_clause: /* empty */ - | GROUP BY group_list - ; + | GROUP BY group_list olap_opt; group_list: group_list ',' order_ident order_dir { if (add_group_to_list($3,(bool) $4)) YYABORT; } | order_ident order_dir - { if (add_group_to_list($1,(bool) $2)) YYABORT; } + { if (add_group_to_list($1,(bool) $2)) YYABORT; }; + +olap_opt: + /* empty */ {} + | WITH CUBE_SYM + { + LEX *lex=Lex; + lex->olap = true; + lex->select->olap= CUBE_TYPE; + net_printf(&lex->thd->net, ER_NOT_SUPPORTED_YET, "CUBE"); + YYABORT; /* To be deleted in 4.1 */ + } + | WITH ROLLUP_SYM + { + LEX *lex=Lex; + lex->olap = true; + lex->select->olap= ROLLUP_TYPE; + net_printf(&lex->thd->net, ER_NOT_SUPPORTED_YET, "ROLLUP"); + YYABORT; /* To be deleted in 4.1 */ + } ; /* @@ -2015,59 +2324,93 @@ group_list: opt_order_clause: /* empty */ - | order_clause - ; + | order_clause; order_clause: - ORDER_SYM BY order_list - ; + ORDER_SYM BY + { + LEX *lex=Lex; + if (lex->select->olap != UNSPECIFIED_OLAP_TYPE) + { + net_printf(&lex->thd->net, ER_WRONG_USAGE, + "CUBE/ROLLUP", + "ORDER BY"); + YYABORT; + } + lex->select->sort_default=1; + } order_list; order_list: order_list ',' order_ident order_dir { if (add_order_to_list($3,(bool) $4)) YYABORT; } | order_ident order_dir - { if (add_order_to_list($1,(bool) $2)) YYABORT; } - ; + { if (add_order_to_list($1,(bool) $2)) YYABORT; }; order_dir: /* empty */ { $$ = 1; } | ASC { $$ =1; } - | DESC { $$ =0; } - ; + | DESC { $$ =0; }; + limit_clause: - /* empty */ - { - Lex->select_limit= current_thd->default_select_limit; - Lex->offset_limit= 0L; - } - | LIMIT ULONG_NUM - { Lex->select_limit= $2; Lex->offset_limit=0L; } - | LIMIT ULONG_NUM ',' ULONG_NUM - { Lex->select_limit= $4; Lex->offset_limit=$2; } + /* empty */ {} + | LIMIT + { + LEX *lex=Lex; + if (lex->select->olap != UNSPECIFIED_OLAP_TYPE) + { + net_printf(&lex->thd->net, ER_WRONG_USAGE, "CUBE/ROLLUP", + "LIMIT"); + YYABORT; + } + } + limit_options + {} + ; + +limit_options: + ULONG_NUM + { + SELECT_LEX *sel= Select; + sel->select_limit= $1; + sel->offset_limit= 0L; + } + | ULONG_NUM ',' ULONG_NUM + { + SELECT_LEX *sel= Select; + sel->select_limit= $3; + sel->offset_limit= $1; + } + | ULONG_NUM OFFSET_SYM ULONG_NUM + { + SELECT_LEX *sel= Select; + sel->select_limit= $1; + sel->offset_limit= $3; + } ; delete_limit_clause: /* empty */ { - Lex->select_limit= HA_POS_ERROR; + LEX *lex=Lex; + lex->select->select_limit= HA_POS_ERROR; } - | LIMIT ULONGLONG_NUM - { Lex->select_limit= (ha_rows) $2; } - ; + | LIMIT ulonglong_num + { Select->select_limit= (ha_rows) $2; }; ULONG_NUM: - NUM { $$= strtoul($1.str,NULL,10); } - | REAL_NUM { $$= strtoul($1.str,NULL,10); } - | FLOAT_NUM { $$= strtoul($1.str,NULL,10); } - ; - -ULONGLONG_NUM: - NUM { $$= (ulonglong) strtoul($1.str,NULL,10); } - | LONG_NUM { $$= strtoull($1.str,NULL,10); } - | REAL_NUM { $$= strtoull($1.str,NULL,10); } - | FLOAT_NUM { $$= strtoull($1.str,NULL,10); } - ; + NUM { $$= strtoul($1.str,NULL,10); } + | LONG_NUM { $$= (ulonglong) strtoll($1.str,NULL,10); } + | ULONGLONG_NUM { $$= (ulong) strtoull($1.str,NULL,10); } + | REAL_NUM { $$= strtoul($1.str,NULL,10); } + | FLOAT_NUM { $$= strtoul($1.str,NULL,10); }; + +ulonglong_num: + NUM { $$= (ulonglong) strtoul($1.str,NULL,10); } + | ULONGLONG_NUM { $$= strtoull($1.str,NULL,10); } + | LONG_NUM { $$= (ulonglong) strtoll($1.str,NULL,10); } + | REAL_NUM { $$= strtoull($1.str,NULL,10); } + | FLOAT_NUM { $$= strtoull($1.str,NULL,10); }; procedure_clause: /* empty */ @@ -2077,31 +2420,30 @@ procedure_clause: lex->proc_list.elements=0; lex->proc_list.first=0; lex->proc_list.next= (byte**) &lex->proc_list.first; - if (add_proc_to_list(new Item_field(NULL,NULL,$2.str))) + if (add_proc_to_list(lex->thd, new Item_field(NULL,NULL,$2.str))) YYABORT; + current_thd->safe_to_cache_query=0; } - '(' procedure_list ')' - ; + '(' procedure_list ')'; + procedure_list: /* empty */ {} - | procedure_list2 {} - ; + | procedure_list2 {}; procedure_list2: procedure_list2 ',' procedure_item - | procedure_item - ; + | procedure_item; procedure_item: remember_name expr { - if (add_proc_to_list($2)) + LEX *lex= Lex; + if (add_proc_to_list(lex->thd, $2)) YYABORT; if (!$2->name) - $2->set_name($1,(uint) ((char*) Lex->tok_end - $1)); - } - ; + $2->set_name($1,(uint) ((char*) lex->tok_end - $1)); + }; opt_into: INTO OUTFILE TEXT_STRING @@ -2114,8 +2456,7 @@ opt_into: { if (!(Lex->exchange= new sql_exchange($3.str,1))) YYABORT; - } - ; + }; /* DO statement @@ -2137,109 +2478,127 @@ do: DO_SYM */ drop: - DROP TABLE_SYM if_exists table_list opt_restrict + DROP opt_temporary TABLE_SYM if_exists table_list opt_restrict { - Lex->sql_command = SQLCOM_DROP_TABLE; - Lex->drop_if_exists = $3; + LEX *lex=Lex; + lex->sql_command = SQLCOM_DROP_TABLE; + lex->drop_temporary= $2; + lex->drop_if_exists= $4; } | DROP INDEX ident ON table_ident {} { - Lex->sql_command= SQLCOM_DROP_INDEX; - Lex->drop_list.empty(); - Lex->drop_list.push_back(new Alter_drop(Alter_drop::KEY, + LEX *lex=Lex; + lex->sql_command= SQLCOM_DROP_INDEX; + lex->drop_list.empty(); + lex->drop_list.push_back(new Alter_drop(Alter_drop::KEY, $3.str)); - if (!add_table_to_list($5,NULL, 1)) + if (!add_table_to_list($5, NULL, TL_OPTION_UPDATING)) YYABORT; } | DROP DATABASE if_exists ident { - Lex->sql_command= SQLCOM_DROP_DB; - Lex->drop_if_exists=$3; - Lex->name=$4.str; + LEX *lex=Lex; + lex->sql_command= SQLCOM_DROP_DB; + lex->drop_if_exists=$3; + lex->name=$4.str; } | DROP UDF_SYM ident { - Lex->sql_command = SQLCOM_DROP_FUNCTION; - Lex->udf.name=$3.str; - } - ; + LEX *lex=Lex; + lex->sql_command = SQLCOM_DROP_FUNCTION; + lex->udf.name=$3.str; + }; + table_list: - table - | table_list ',' table - ; + table_name + | table_list ',' table_name; -table: +table_name: table_ident - { if (!add_table_to_list($1,NULL,1)) YYABORT; } - ; + { if (!add_table_to_list($1,NULL,TL_OPTION_UPDATING)) YYABORT; }; if_exists: - /* empty */ { $$=0; } + /* empty */ { $$= 0; } | IF EXISTS { $$= 1; } ; +opt_temporary: + /* empty */ { $$= 0; } + | TEMPORARY { $$= 1; } + ; /* ** Insert : add new data to table */ insert: - INSERT { Lex->sql_command = SQLCOM_INSERT; } insert_lock_option opt_ignore insert2 insert_field_spec + INSERT { Lex->sql_command = SQLCOM_INSERT; } insert_lock_option + opt_ignore insert2 + { + set_lock_for_tables($3); + } + insert_field_spec {} ; replace: - REPLACE { Lex->sql_command = SQLCOM_REPLACE; } replace_lock_option insert2 insert_field_spec + REPLACE + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_REPLACE; + lex->duplicates= DUP_REPLACE; + } + replace_lock_option insert2 + { + set_lock_for_tables($3); + } + insert_field_spec {} ; insert_lock_option: - /* empty */ { Lex->lock_option= TL_WRITE_CONCURRENT_INSERT; } - | LOW_PRIORITY { Lex->lock_option= TL_WRITE_LOW_PRIORITY; } - | DELAYED_SYM { Lex->lock_option= TL_WRITE_DELAYED; } - | HIGH_PRIORITY { Lex->lock_option= TL_WRITE; } - ; + /* empty */ { $$= TL_WRITE_CONCURRENT_INSERT; } + | LOW_PRIORITY { $$= TL_WRITE_LOW_PRIORITY; } + | DELAYED_SYM { $$= TL_WRITE_DELAYED; } + | HIGH_PRIORITY { $$= TL_WRITE; } + ; replace_lock_option: - opt_low_priority {} - | DELAYED_SYM { Lex->lock_option= TL_WRITE_DELAYED; } - ; + opt_low_priority { $$= $1; } + | DELAYED_SYM { $$= TL_WRITE_DELAYED; }; insert2: INTO insert_table {} - | insert_table {} - ; + | insert_table {}; insert_table: - table + table_name { - Lex->field_list.empty(); - Lex->many_values.empty(); - Lex->insert_list=0; - } - ; + LEX *lex=Lex; + lex->field_list.empty(); + lex->many_values.empty(); + lex->insert_list=0; + }; insert_field_spec: opt_field_spec insert_values {} | SET { - if (!(Lex->insert_list = new List_item) || - Lex->many_values.push_back(Lex->insert_list)) + LEX *lex=Lex; + if (!(lex->insert_list = new List_item) || + lex->many_values.push_back(lex->insert_list)) YYABORT; } - ident_eq_list - ; + ident_eq_list; opt_field_spec: /* empty */ { } | '(' fields ')' { } - | '(' ')' { } - ; + | '(' ')' { }; fields: fields ',' insert_ident { Lex->field_list.push_back($3); } - | insert_ident { Lex->field_list.push_back($1); } - ; + | insert_ident { Lex->field_list.push_back($1); }; insert_values: VALUES values_list {} @@ -2251,33 +2610,36 @@ insert_values: lex->lock_option= (using_update_log) ? TL_READ_NO_INSERT : TL_READ; mysql_init_select(lex); } - select_options select_item_list select_from select_lock_type {} - ; + select_options select_item_list select_from select_lock_type + opt_union {}; values_list: values_list ',' no_braces - | no_braces - ; + | no_braces; ident_eq_list: ident_eq_list ',' ident_eq_value | - ident_eq_value - ; + ident_eq_value; ident_eq_value: - simple_ident equal expr + simple_ident equal expr_or_default { - if (Lex->field_list.push_back($1) || - Lex->insert_list->push_back($3)) + LEX *lex=Lex; + if (lex->field_list.push_back($1) || + lex->insert_list->push_back($3)) YYABORT; - } - ; + }; equal: EQ {} | SET_VAR {} ; +opt_equal: + /* empty */ {} + | equal {} + ; + no_braces: '(' { @@ -2286,34 +2648,49 @@ no_braces: } opt_values ')' { - if (Lex->many_values.push_back(Lex->insert_list)) + LEX *lex=Lex; + if (lex->many_values.push_back(lex->insert_list)) YYABORT; - } - ; + }; opt_values: /* empty */ {} - | values - ; + | values; values: - values ',' expr + values ',' expr_or_default { if (Lex->insert_list->push_back($3)) YYABORT; } - | expr - { - if (Lex->insert_list->push_back($1)) - YYABORT; - } + | expr_or_default + { + if (Lex->insert_list->push_back($1)) + YYABORT; + } + ; + +expr_or_default: + expr { $$= $1;} + | DEFAULT {$$= new Item_default(); } ; /* Update rows in a table */ update: - UPDATE_SYM opt_low_priority opt_ignore table SET update_list where_clause delete_limit_clause - { Lex->sql_command = SQLCOM_UPDATE; } + UPDATE_SYM + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_UPDATE; + lex->select->order_list.elements=0; + lex->select->order_list.first=0; + lex->select->order_list.next= (byte**) &lex->select->order_list.first; + } + opt_low_priority opt_ignore join_table_list + SET update_list where_clause opt_order_clause delete_limit_clause + { + set_lock_for_tables($3); + } ; update_list: @@ -2326,48 +2703,94 @@ update_list: { if (add_item_to_list($1) || add_value_to_list($3)) YYABORT; - } - ; + }; opt_low_priority: - /* empty */ { Lex->lock_option= current_thd->update_lock_default; } - | LOW_PRIORITY { Lex->lock_option= TL_WRITE_LOW_PRIORITY; } - ; + /* empty */ { $$= current_thd->update_lock_default; } + | LOW_PRIORITY { $$= TL_WRITE_LOW_PRIORITY; }; /* Delete rows from a table */ delete: DELETE_SYM + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_DELETE; lex->select->options=0; + lex->lock_option= lex->thd->update_lock_default; + lex->select->order_list.elements=0; + lex->select->order_list.first=0; + lex->select->order_list.next= (byte**) &lex->select->order_list.first; + } + opt_delete_options single_multi {} + ; + +single_multi: + FROM table_ident { - Lex->sql_command= SQLCOM_DELETE; Lex->options=0; - Lex->lock_option= current_thd->update_lock_default; + if (!add_table_to_list($2, NULL, TL_OPTION_UPDATING, + Lex->lock_option)) + YYABORT; } - opt_delete_options FROM table - where_clause delete_limit_clause - {} + where_clause opt_order_clause + delete_limit_clause {} + | table_wild_list + { mysql_init_multi_delete(Lex); } + FROM join_table_list where_clause + | FROM table_wild_list + { mysql_init_multi_delete(Lex); } + USING join_table_list where_clause + {} + ; + +table_wild_list: + table_wild_one {} + | table_wild_list ',' table_wild_one {}; + +table_wild_one: + ident opt_wild + { + if (!add_table_to_list(new Table_ident($1), NULL, + TL_OPTION_UPDATING, Lex->lock_option)) + YYABORT; + } + | ident '.' ident opt_wild + { + if (!add_table_to_list(new Table_ident($1,$3,0), NULL, + TL_OPTION_UPDATING, + Lex->lock_option)) + YYABORT; + } ; +opt_wild: + /* empty */ {} + | '.' '*' {}; + + opt_delete_options: - /* empty */ {} - | opt_delete_option opt_delete_options {} - ; + /* empty */ {} + | opt_delete_option opt_delete_options {}; opt_delete_option: - QUICK { Lex->options|= OPTION_QUICK; } - | LOW_PRIORITY { Lex->lock_option= TL_WRITE_LOW_PRIORITY; } - ; + QUICK { Select->options|= OPTION_QUICK; } + | LOW_PRIORITY { Lex->lock_option= TL_WRITE_LOW_PRIORITY; }; truncate: - TRUNCATE_SYM opt_table_sym table - { Lex->sql_command= SQLCOM_TRUNCATE; Lex->options=0; - Lex->lock_option= current_thd->update_lock_default; } + TRUNCATE_SYM opt_table_sym table_name + { + LEX* lex = Lex; + lex->sql_command= SQLCOM_TRUNCATE; + lex->select->options=0; + lex->select->order_list.elements=0; + lex->select->order_list.first=0; + lex->select->order_list.next= (byte**) &lex->select->order_list.first; + } ; opt_table_sym: /* empty */ - | TABLE_SYM - ; - + | TABLE_SYM; + /* Show things */ show: SHOW { Lex->wild=0;} show_param @@ -2378,35 +2801,64 @@ show_param: DATABASES wild { Lex->sql_command= SQLCOM_SHOW_DATABASES; } | TABLES opt_db wild - { Lex->sql_command= SQLCOM_SHOW_TABLES; Lex->db= $2; Lex->options=0;} + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_SHOW_TABLES; + lex->select->db= $2; lex->select->options=0; + } | TABLE_SYM STATUS_SYM opt_db wild - { Lex->sql_command= SQLCOM_SHOW_TABLES; - Lex->options|= SELECT_DESCRIBE; - Lex->db= $3; + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_SHOW_TABLES; + lex->select->options|= SELECT_DESCRIBE; + lex->select->db= $3; } | OPEN_SYM TABLES opt_db wild - { Lex->sql_command= SQLCOM_SHOW_OPEN_TABLES; - Lex->db= $3; - Lex->options=0; + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_SHOW_OPEN_TABLES; + lex->select->db= $3; + lex->select->options=0; } - | opt_full COLUMNS FROM table_ident opt_db wild + | opt_full COLUMNS from_or_in table_ident opt_db wild { Lex->sql_command= SQLCOM_SHOW_FIELDS; if ($5) $4->change_db($5); - if (!add_table_to_list($4,NULL,0)) + if (!add_table_to_list($4, NULL, 0)) YYABORT; } + | NEW_SYM MASTER_SYM FOR_SYM SLAVE WITH MASTER_LOG_FILE_SYM EQ + TEXT_STRING AND MASTER_LOG_POS_SYM EQ ulonglong_num + AND MASTER_SERVER_ID_SYM EQ + ULONG_NUM + { + Lex->sql_command = SQLCOM_SHOW_NEW_MASTER; + Lex->mi.log_file_name = $8.str; + Lex->mi.pos = $12; + Lex->mi.server_id = $16; + } | MASTER_SYM LOGS_SYM { Lex->sql_command = SQLCOM_SHOW_BINLOGS; - } + } + | SLAVE HOSTS_SYM + { + Lex->sql_command = SQLCOM_SHOW_SLAVE_HOSTS; + } + | BINLOG_SYM EVENTS_SYM binlog_in binlog_from + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_SHOW_BINLOG_EVENTS; + lex->select->select_limit= lex->thd->variables.select_limit; + lex->select->offset_limit= 0L; + } limit_clause | keys_or_index FROM table_ident opt_db { Lex->sql_command= SQLCOM_SHOW_KEYS; if ($4) $3->change_db($4); - if (!add_table_to_list($3,NULL,0)) + if (!add_table_to_list($3, NULL, 0)) YYABORT; } | STATUS_SYM wild @@ -2415,17 +2867,25 @@ show_param: { Lex->sql_command = SQLCOM_SHOW_INNODB_STATUS;} | opt_full PROCESSLIST_SYM { Lex->sql_command= SQLCOM_SHOW_PROCESSLIST;} - | VARIABLES wild - { Lex->sql_command= SQLCOM_SHOW_VARIABLES; } + | opt_var_type VARIABLES wild + { + THD *thd= current_thd; + thd->lex.sql_command= SQLCOM_SHOW_VARIABLES; + thd->lex.option_type= (enum_var_type) $1; + } | LOGS_SYM { Lex->sql_command= SQLCOM_SHOW_LOGS; } | GRANTS FOR_SYM user - { Lex->sql_command= SQLCOM_SHOW_GRANTS; - Lex->grant_user=$3; Lex->grant_user->password.str=NullS; } + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_SHOW_GRANTS; + lex->grant_user=$3; + lex->grant_user->password.str=NullS; + } | CREATE TABLE_SYM table_ident { Lex->sql_command = SQLCOM_SHOW_CREATE; - if(!add_table_to_list($3, NULL,0)) + if(!add_table_to_list($3, NULL, 0)) YYABORT; } | MASTER_SYM STATUS_SYM @@ -2435,211 +2895,235 @@ show_param: | SLAVE STATUS_SYM { Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT; - } - ; + }; opt_db: /* empty */ { $$= 0; } - | FROM ident { $$= $2.str; } - ; + | from_or_in ident { $$= $2.str; }; wild: /* empty */ - | LIKE text_string { Lex->wild= $2; } - ; + | LIKE text_string { Lex->wild= $2; }; opt_full: /* empty */ { Lex->verbose=0; } - | FULL { Lex->verbose=1; } - ; + | FULL { Lex->verbose=1; }; + +from_or_in: + FROM + | IN_SYM; + +binlog_in: + /* empty */ { Lex->mi.log_file_name = 0; } + | IN_SYM TEXT_STRING { Lex->mi.log_file_name = $2.str; }; + +binlog_from: + /* empty */ { Lex->mi.pos = 4; /* skip magic number */ } + | FROM ulonglong_num { Lex->mi.pos = $2; }; + /* A Oracle compatible synonym for show */ describe: describe_command table_ident { - Lex->wild=0; - Lex->verbose=0; - Lex->sql_command=SQLCOM_SHOW_FIELDS; - if (!add_table_to_list($2, NULL,0)) + LEX *lex=Lex; + lex->wild=0; + lex->verbose=0; + lex->sql_command=SQLCOM_SHOW_FIELDS; + if (!add_table_to_list($2, NULL, 0)) YYABORT; } opt_describe_column {} - | describe_command select { Lex->options|= SELECT_DESCRIBE; } - ; + | describe_command select + { Lex->select_lex.options|= SELECT_DESCRIBE; }; + describe_command: DESC - | DESCRIBE - ; + | DESCRIBE; opt_describe_column: /* empty */ {} | text_string { Lex->wild= $1; } - | ident { Lex->wild= new String((const char*) $1.str,$1.length); } - ; + | ident + { Lex->wild= new String((const char*) $1.str,$1.length); }; + /* flush things */ flush: - FLUSH_SYM {Lex->sql_command= SQLCOM_FLUSH; Lex->type=0; } flush_options + FLUSH_SYM + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_FLUSH; lex->type=0; + } + flush_options {} ; flush_options: flush_options ',' flush_option - | flush_option - ; + | flush_option; flush_option: table_or_tables { Lex->type|= REFRESH_TABLES; } opt_table_list {} | TABLES WITH READ_SYM LOCK_SYM { Lex->type|= REFRESH_TABLES | REFRESH_READ_LOCK; } + | QUERY_SYM CACHE_SYM { Lex->type|= REFRESH_QUERY_CACHE_FREE; } | HOSTS_SYM { Lex->type|= REFRESH_HOSTS; } | PRIVILEGES { Lex->type|= REFRESH_GRANT; } | LOGS_SYM { Lex->type|= REFRESH_LOG; } | STATUS_SYM { Lex->type|= REFRESH_STATUS; } | SLAVE { Lex->type|= REFRESH_SLAVE; } | MASTER_SYM { Lex->type|= REFRESH_MASTER; } - ; + | DES_KEY_FILE { Lex->type|= REFRESH_DES_KEY_FILE; } + | RESOURCES { Lex->type|= REFRESH_USER_RESOURCES; }; opt_table_list: - /* empty */ {} - | table_list {} - ; + /* empty */ {;} + | table_list {;}; reset: - RESET_SYM {Lex->sql_command= SQLCOM_RESET; Lex->type=0; } reset_options + RESET_SYM + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_RESET; lex->type=0; + } reset_options {} ; reset_options: reset_options ',' reset_option - | reset_option - ; + | reset_option; reset_option: - SLAVE { Lex->type|= REFRESH_SLAVE; } - | MASTER_SYM { Lex->type|= REFRESH_MASTER; } - ; + SLAVE { Lex->type|= REFRESH_SLAVE; } + | MASTER_SYM { Lex->type|= REFRESH_MASTER; } + | QUERY_SYM CACHE_SYM { Lex->type|= REFRESH_QUERY_CACHE;}; purge: - PURGE { Lex->sql_command = SQLCOM_PURGE; Lex->type=0;} + PURGE + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_PURGE; + lex->type=0; + } MASTER_SYM LOGS_SYM TO_SYM TEXT_STRING { Lex->to_log = $6.str; - } - ; + } ; /* kill threads */ kill: KILL_SYM expr { - if ($2->fix_fields(current_thd,0)) - { - send_error(¤t_thd->net, ER_SET_CONSTANTS_ONLY); - YYABORT; - } - Lex->sql_command=SQLCOM_KILL; - Lex->thread_id= (ulong) $2->val_int(); - } - ; + LEX *lex=Lex; + if ($2->fix_fields(lex->thd,0)) + { + send_error(&lex->thd->net, ER_SET_CONSTANTS_ONLY); + YYABORT; + } + lex->sql_command=SQLCOM_KILL; + lex->thread_id= (ulong) $2->val_int(); + }; /* change database */ use: USE_SYM ident - { Lex->sql_command=SQLCOM_CHANGE_DB; Lex->db= $2.str; } - ; + { + LEX *lex=Lex; + lex->sql_command=SQLCOM_CHANGE_DB; lex->select->db= $2.str; + }; /* import, export of files */ load: LOAD DATA_SYM load_data_lock opt_local INFILE TEXT_STRING { - LEX *lex= Lex; + LEX *lex=Lex; lex->sql_command= SQLCOM_LOAD; - lex->local_file= $4; - if (!(Lex->exchange= new sql_exchange($6.str,0))) + lex->lock_option= $3; + lex->local_file= $4; + if (!(lex->exchange= new sql_exchange($6.str,0))) YYABORT; - Lex->field_list.empty(); + lex->field_list.empty(); } opt_duplicate INTO TABLE_SYM table_ident opt_field_term opt_line_term opt_ignore_lines opt_field_spec { - if (!add_table_to_list($11,NULL,1)) + if (!add_table_to_list($11, NULL, TL_OPTION_UPDATING)) YYABORT; } | LOAD TABLE_SYM table_ident FROM MASTER_SYM { Lex->sql_command = SQLCOM_LOAD_MASTER_TABLE; - if (!add_table_to_list($3,NULL,1)) + if (!add_table_to_list($3, NULL, TL_OPTION_UPDATING)) YYABORT; } - ; + | + LOAD DATA_SYM FROM MASTER_SYM + { + Lex->sql_command = SQLCOM_LOAD_MASTER_DATA; + }; opt_local: /* empty */ { $$=0;} - | LOCAL_SYM { $$=1;} - ; + | LOCAL_SYM { $$=1;}; load_data_lock: - /* empty */ { Lex->lock_option= current_thd->update_lock_default; } - | CONCURRENT { Lex->lock_option= TL_WRITE_CONCURRENT_INSERT ; } - | LOW_PRIORITY { Lex->lock_option= TL_WRITE_LOW_PRIORITY; } - ; + /* empty */ { $$= current_thd->update_lock_default; } + | CONCURRENT { $$= TL_WRITE_CONCURRENT_INSERT ; } + | LOW_PRIORITY { $$= TL_WRITE_LOW_PRIORITY; }; + opt_duplicate: /* empty */ { Lex->duplicates=DUP_ERROR; } | REPLACE { Lex->duplicates=DUP_REPLACE; } - | IGNORE_SYM { Lex->duplicates=DUP_IGNORE; } - ; + | IGNORE_SYM { Lex->duplicates=DUP_IGNORE; }; opt_field_term: /* empty */ - | COLUMNS field_term_list - ; + | COLUMNS field_term_list; field_term_list: field_term_list field_term - | field_term - ; + | field_term; field_term: TERMINATED BY text_string { Lex->exchange->field_term= $3;} | OPTIONALLY ENCLOSED BY text_string - { Lex->exchange->enclosed= $4; Lex->exchange->opt_enclosed=1;} + { + LEX *lex=Lex; + lex->exchange->enclosed= $4; + lex->exchange->opt_enclosed=1; + } | ENCLOSED BY text_string { Lex->exchange->enclosed= $3;} - | ESCAPED BY text_string { Lex->exchange->escaped= $3;} - ; + | ESCAPED BY text_string { Lex->exchange->escaped= $3;}; opt_line_term: /* empty */ - | LINES line_term_list - ; + | LINES line_term_list; line_term_list: line_term_list line_term - | line_term - ; + | line_term; line_term: TERMINATED BY text_string { Lex->exchange->line_term= $3;} - | STARTING BY text_string { Lex->exchange->line_start= $3;} - ; + | STARTING BY text_string { Lex->exchange->line_start= $3;}; opt_ignore_lines: /* empty */ | IGNORE_SYM NUM LINES - { Lex->exchange->skip_lines=atol($2.str); } - ; + { Lex->exchange->skip_lines=atol($2.str); }; /* Common definitions */ text_literal: TEXT_STRING { $$ = new Item_string($1.str,$1.length); } | text_literal TEXT_STRING - { ((Item_string*) $1)->append($2.str,$2.length); } - ; + { ((Item_string*) $1)->append($2.str,$2.length); }; text_string: TEXT_STRING { $$= new String($1.str,$1.length); } @@ -2647,22 +3131,21 @@ text_string: { Item *tmp = new Item_varbinary($1.str,$1.length); $$= tmp ? tmp->val_str((String*) 0) : (String*) 0; - } - ; + }; literal: text_literal { $$ = $1; } - | NUM { $$ = new Item_int($1.str, (longlong) atol($1.str),$1.length); } - | LONG_NUM { $$ = new Item_int($1.str); } + | NUM { $$ = new Item_int($1.str, (longlong) strtol($1.str, NULL, 10),$1.length); } + | LONG_NUM { $$ = new Item_int($1.str, (longlong) strtoll($1.str,NULL,10), $1.length); } + | ULONGLONG_NUM { $$ = new Item_uint($1.str, $1.length); } | REAL_NUM { $$ = new Item_real($1.str, $1.length); } | FLOAT_NUM { $$ = new Item_float($1.str, $1.length); } | NULL_SYM { $$ = new Item_null(); Lex->next_state=STATE_OPERATOR_OR_IDENT;} - | HEX_NUM { $$ = new Item_varbinary($1.str,$1.length); } + | HEX_NUM { $$ = new Item_varbinary($1.str,$1.length);} | DATE_SYM text_literal { $$ = $2; } | TIME_SYM text_literal { $$ = $2; } - | TIMESTAMP text_literal { $$ = $2; } - ; + | TIMESTAMP text_literal { $$ = $2; }; /********************************************************************** ** Createing different items. @@ -2670,58 +3153,67 @@ literal: insert_ident: simple_ident { $$=$1; } - | table_wild { $$=$1; } - ; + | table_wild { $$=$1; }; table_wild: ident '.' '*' { $$ = new Item_field(NullS,$1.str,"*"); } | ident '.' ident '.' '*' - { $$ = new Item_field((current_thd->client_capabilities & CLIENT_NO_SCHEMA ? NullS : $1.str),$3.str,"*"); } - ; + { $$ = new Item_field((current_thd->client_capabilities & + CLIENT_NO_SCHEMA ? NullS : $1.str),$3.str,"*"); }; order_ident: - expr { $$=$1; } - ; + expr { $$=$1; }; simple_ident: ident - { $$ = !Lex->create_refs || Lex->in_sum_expr > 0 ? (Item*) new Item_field(NullS,NullS,$1.str) : (Item*) new Item_ref(NullS,NullS,$1.str); } + { + SELECT_LEX *sel=Select; + $$ = !sel->create_refs || sel->in_sum_expr > 0 ? (Item*) new Item_field(NullS,NullS,$1.str) : (Item*) new Item_ref(NullS,NullS,$1.str); + } | ident '.' ident - { $$ = !Lex->create_refs || Lex->in_sum_expr > 0 ? (Item*) new Item_field(NullS,$1.str,$3.str) : (Item*) new Item_ref(NullS,$1.str,$3.str); } + { + SELECT_LEX *sel=Select; + $$ = !sel->create_refs || sel->in_sum_expr > 0 ? (Item*) new Item_field(NullS,$1.str,$3.str) : (Item*) new Item_ref(NullS,$1.str,$3.str); + } | '.' ident '.' ident - { $$ = !Lex->create_refs || Lex->in_sum_expr > 0 ? (Item*) new Item_field(NullS,$2.str,$4.str) : (Item*) new Item_ref(NullS,$2.str,$4.str); } + { + SELECT_LEX *sel=Select; + $$ = !sel->create_refs || sel->in_sum_expr > 0 ? (Item*) new Item_field(NullS,$2.str,$4.str) : (Item*) new Item_ref(NullS,$2.str,$4.str); + } | ident '.' ident '.' ident - { $$ = !Lex->create_refs || Lex->in_sum_expr > 0 ? (Item*) new Item_field((current_thd->client_capabilities & CLIENT_NO_SCHEMA ? NullS :$1.str),$3.str,$5.str) : (Item*) new Item_ref((current_thd->client_capabilities & CLIENT_NO_SCHEMA ? NullS :$1.str),$3.str,$5.str); } - ; + { + SELECT_LEX *sel=Select; + $$ = !sel->create_refs || sel->in_sum_expr > 0 ? (Item*) new Item_field((current_thd->client_capabilities & CLIENT_NO_SCHEMA ? NullS :$1.str),$3.str,$5.str) : (Item*) new Item_ref((current_thd->client_capabilities & CLIENT_NO_SCHEMA ? NullS :$1.str),$3.str,$5.str); + }; + field_ident: ident { $$=$1;} | ident '.' ident { $$=$3;} /* Skipp schema name in create*/ - | '.' ident { $$=$2;} /* For Delphi */ - ; + | '.' ident { $$=$2;} /* For Delphi */; table_ident: ident { $$=new Table_ident($1); } | ident '.' ident { $$=new Table_ident($1,$3,0);} - | '.' ident { $$=new Table_ident($2);} /* For Delphi */ - ; + | '.' ident { $$=new Table_ident($2);} + /* For Delphi */; ident: IDENT { $$=$1; } | keyword { - $$.str=sql_strmake($1.str,$1.length); + LEX *lex= Lex; + $$.str= lex->thd->strmake($1.str,$1.length); $$.length=$1.length; - if (Lex->next_state != STATE_END) - Lex->next_state=STATE_OPERATOR_OR_IDENT; + if (lex->next_state != STATE_END) + lex->next_state=STATE_OPERATOR_OR_IDENT; } ; ident_or_text: ident { $$=$1;} | TEXT_STRING { $$=$1;} - | LEX_HOSTNAME { $$=$1;} - ; + | LEX_HOSTNAME { $$=$1;}; user: ident_or_text @@ -2735,8 +3227,7 @@ user: if (!($$=(LEX_USER*) sql_alloc(sizeof(st_lex_user)))) YYABORT; $$->user = $1; $$->host=$3; - } - ; + }; /* Keyword that we allow for identifiers */ @@ -2745,51 +3236,68 @@ keyword: | AFTER_SYM {} | AGAINST {} | AGGREGATE_SYM {} - | AUTOCOMMIT {} | AUTO_INC {} | AVG_ROW_LENGTH {} | AVG_SYM {} | BACKUP_SYM {} | BEGIN_SYM {} | BERKELEY_DB_SYM {} + | BINLOG_SYM {} | BIT_SYM {} | BOOL_SYM {} + | BOOLEAN_SYM {} + | CACHE_SYM {} | CHANGED {} + | CHARSET {} | CHECKSUM_SYM {} - | CHECK_SYM {} + | CIPHER_SYM {} + | CLIENT_SYM {} + | CLOSE_SYM {} | COMMENT_SYM {} - | COMMIT_SYM {} | COMMITTED_SYM {} + | COMMIT_SYM {} | COMPRESSED_SYM {} | CONCURRENT {} + | CUBE_SYM {} | DATA_SYM {} | DATETIME {} | DATE_SYM {} | DAY_SYM {} | DELAY_KEY_WRITE_SYM {} + | DES_KEY_FILE {} + | DIRECTORY_SYM {} | DO_SYM {} | DUMPFILE {} | DYNAMIC_SYM {} | END {} | ENUM {} | ESCAPE_SYM {} + | EVENTS_SYM {} + | EXECUTE_SYM {} | EXTENDED_SYM {} | FAST_SYM {} + | DISABLE_SYM {} + | ENABLE_SYM {} | FULL {} | FILE_SYM {} | FIRST_SYM {} | FIXED_SYM {} | FLUSH_SYM {} | GRANTS {} - | GEMINI_SYM {} | GLOBAL_SYM {} | HEAP_SYM {} + | HANDLER_SYM {} | HOSTS_SYM {} | HOUR_SYM {} | IDENTIFIED_SYM {} + | INDEXES {} | ISOLATION {} | ISAM_SYM {} + | ISSUER_SYM {} | INNOBASE_SYM {} + | INSERT_METHOD {} + | IO_THREAD {} + | LAST_SYM {} | LEVEL_SYM {} | LOCAL_SYM {} | LOCKS_SYM {} @@ -2803,6 +3311,9 @@ keyword: | MASTER_USER_SYM {} | MASTER_PASSWORD_SYM {} | MASTER_CONNECT_RETRY_SYM {} + | MAX_CONNECTIONS_PER_HOUR {} + | MAX_QUERIES_PER_HOUR {} + | MAX_UPDATES_PER_HOUR {} | MEDIUM_SYM {} | MERGE_SYM {} | MINUTE_SYM {} @@ -2813,38 +3324,55 @@ keyword: | MYISAM_SYM {} | NATIONAL_SYM {} | NCHAR_SYM {} - | FOREIGN_KEY_CHECKS {} + | NEXT_SYM {} + | NEW_SYM {} | NO_SYM {} + | NONE_SYM {} + | OFFSET_SYM {} | OPEN_SYM {} | PACK_KEYS_SYM {} | PASSWORD {} + | PREV_SYM {} | PROCESS {} | PROCESSLIST_SYM {} + | QUERY_SYM {} | QUICK {} | RAID_0_SYM {} | RAID_CHUNKS {} | RAID_CHUNKSIZE {} | RAID_STRIPED_SYM {} | RAID_TYPE {} - | UNIQUE_CHECKS {} + | RELAY_LOG_FILE_SYM {} + | RELAY_LOG_POS_SYM {} | RELOAD {} | REPAIR {} | REPEATABLE_SYM {} + | REPLICATION {} | RESET_SYM {} + | RESOURCES {} | RESTORE_SYM {} | ROLLBACK_SYM {} + | ROLLUP_SYM {} | ROWS_SYM {} | ROW_FORMAT_SYM {} | ROW_SYM {} | SECOND_SYM {} | SERIALIZABLE_SYM {} | SESSION_SYM {} + | SIGNED_SYM {} | SHARE_SYM {} | SHUTDOWN {} + | SLAVE {} + | SQL_CACHE_SYM {} + | SQL_BUFFER_RESULT {} + | SQL_NO_CACHE_SYM {} + | SQL_THREAD {} | START_SYM {} | STATUS_SYM {} | STOP_SYM {} | STRING_SYM {} + | SUBJECT_SYM {} + | SUPER_SYM {} | TEMPORARY {} | TEXT_SYM {} | TRANSACTION_SYM {} @@ -2854,24 +3382,20 @@ keyword: | TYPE_SYM {} | UDF_SYM {} | UNCOMMITTED_SYM {} + | USE_FRM {} | VARIABLES {} | WORK_SYM {} - | YEAR_SYM {} - | SLAVE {} - ; + | YEAR_SYM {}; /* Option functions */ set: SET opt_option { - THD *thd=current_thd; - LEX *lex= &thd->lex; + LEX *lex=Lex; lex->sql_command= SQLCOM_SET_OPTION; - lex->options=thd->options; - lex->select_limit=thd->default_select_limit; - lex->gemini_spin_retries=thd->gemini_spin_retries; - lex->tx_isolation=thd->tx_isolation; + lex->option_type=OPT_DEFAULT; + lex->var_list.empty(); } option_value_list {} @@ -2879,132 +3403,95 @@ set: opt_option: /* empty */ {} - | OPTION {} - ; + | OPTION {}; option_value_list: - option_value - | option_value_list ',' option_value + option_type option_value + | option_value_list ',' option_type option_value; + +option_type: + /* empty */ {} + | GLOBAL_SYM { Lex->option_type= OPT_GLOBAL; } + | LOCAL_SYM { Lex->option_type= OPT_SESSION; } + | SESSION_SYM { Lex->option_type= OPT_SESSION; } + ; + +opt_var_type: + /* empty */ { $$=OPT_SESSION; } + | LOCAL_SYM { $$=OPT_SESSION; } + | SESSION_SYM { $$=OPT_SESSION; } + | GLOBAL_SYM { $$=OPT_GLOBAL; } + ; + +opt_var_ident_type: + /* empty */ { $$=OPT_DEFAULT; } + | LOCAL_SYM '.' { $$=OPT_SESSION; } + | SESSION_SYM '.' { $$=OPT_SESSION; } + | GLOBAL_SYM '.' { $$=OPT_GLOBAL; } ; option_value: - set_option equal NUM - { - if (atoi($3.str) == 0) - Lex->options&= ~$1; - else - Lex->options|= $1; - } - | set_isolation - | AUTOCOMMIT equal NUM - { - if (atoi($3.str) != 0) /* Test NOT AUTOCOMMIT */ - Lex->options&= ~(OPTION_NOT_AUTO_COMMIT); - else - Lex->options|= OPTION_NOT_AUTO_COMMIT; - } - | SQL_SELECT_LIMIT equal ULONG_NUM - { - Lex->select_limit= $3; - } - | SQL_SELECT_LIMIT equal DEFAULT - { - Lex->select_limit= HA_POS_ERROR; - } - | SQL_MAX_JOIN_SIZE equal ULONG_NUM - { - current_thd->max_join_size= $3; - Lex->options&= ~OPTION_BIG_SELECTS; - } - | SQL_MAX_JOIN_SIZE equal DEFAULT - { - current_thd->max_join_size= HA_POS_ERROR; - } - | TIMESTAMP equal ULONG_NUM - { - current_thd->set_time((time_t) $3); - } - | TIMESTAMP equal DEFAULT - { - current_thd->user_time=0; - } - | LAST_INSERT_ID equal ULONGLONG_NUM - { - current_thd->insert_id($3); - } - | INSERT_ID equal ULONGLONG_NUM + '@' ident_or_text equal expr { - current_thd->next_insert_id=$3; + Lex->var_list.push_back(new set_var_user(new Item_func_set_user_var($2,$4))); } - | GEMINI_SPIN_RETRIES equal ULONG_NUM - { - Lex->gemini_spin_retries= $3; - } - | GEMINI_SPIN_RETRIES equal DEFAULT - { - Lex->gemini_spin_retries= 1; - } - | CHAR_SYM SET IDENT - { - CONVERT *tmp; - if (!(tmp=get_convert_set($3.str))) + | internal_variable_name equal set_expr_or_default { - net_printf(¤t_thd->net,ER_UNKNOWN_CHARACTER_SET,$3); - YYABORT; + LEX *lex=Lex; + lex->var_list.push_back(new set_var(lex->option_type, $1, $3)); } - current_thd->convert_set=tmp; - } - | CHAR_SYM SET DEFAULT + | '@' '@' opt_var_ident_type internal_variable_name equal set_expr_or_default + { + LEX *lex=Lex; + lex->var_list.push_back(new set_var((enum_var_type) $3, $4, $6)); + } + | TRANSACTION_SYM ISOLATION LEVEL_SYM isolation_types + { + LEX *lex=Lex; + lex->var_list.push_back(new set_var(lex->option_type, + find_sys_var("tx_isolation"), + new Item_int((int32) $4))); + } + | CHAR_SYM SET opt_equal set_expr_or_default { - current_thd->convert_set=0; + LEX *lex=Lex; + lex->var_list.push_back(new set_var(lex->option_type, + find_sys_var("convert_character_set"), + $4)); } | PASSWORD equal text_or_password - { - if (change_password(current_thd,current_thd->host, - current_thd->priv_user,$3)) - YYABORT; - } - | PASSWORD FOR_SYM user equal text_or_password - { - if (change_password(current_thd, - $3->host.str ? $3->host.str : current_thd->host, - $3->user.str,$5)) - YYABORT; - } - | '@' ident_or_text equal expr { - Item_func_set_user_var *item = new Item_func_set_user_var($2,$4); - if (item->fix_fields(current_thd,0) || item->update()) - { - send_error(¤t_thd->net, ER_SET_CONSTANTS_ONLY); - YYABORT; - } - } - | SQL_SLAVE_SKIP_COUNTER equal ULONG_NUM - { - pthread_mutex_lock(&LOCK_slave); - if(slave_running) - send_error(¤t_thd->net, ER_SLAVE_MUST_STOP); - else - slave_skip_counter = $3; - pthread_mutex_unlock(&LOCK_slave); - } - | FOREIGN_KEY_CHECKS equal NUM - { - if (atoi($3.str) == 0) - Lex->options|= OPTION_NO_FOREIGN_KEY_CHECKS; - else - Lex->options&= ~(OPTION_NO_FOREIGN_KEY_CHECKS); + THD *thd=current_thd; + LEX_USER *user; + if (!(user=(LEX_USER*) sql_alloc(sizeof(LEX_USER)))) + YYABORT; + user->host.str=0; + user->user.str=thd->priv_user; + thd->lex.var_list.push_back(new set_var_password(user, $3)); } - | UNIQUE_CHECKS equal NUM + | PASSWORD FOR_SYM user equal text_or_password { - if (atoi($3.str) == 0) - Lex->options|= OPTION_RELAXED_UNIQUE_CHECKS; - else - Lex->options&= ~(OPTION_RELAXED_UNIQUE_CHECKS); + Lex->var_list.push_back(new set_var_password($3,$5)); } ; +internal_variable_name: + ident + { + sys_var *tmp=find_sys_var($1.str, $1.length); + if (!tmp) + YYABORT; + $$=tmp; + } + ; + +isolation_types: + READ_SYM UNCOMMITTED_SYM { $$= ISO_READ_UNCOMMITTED; } + | READ_SYM COMMITTED_SYM { $$= ISO_READ_COMMITTED; } + | REPEATABLE_SYM READ_SYM { $$= ISO_REPEATABLE_READ; } + | SERIALIZABLE_SYM { $$= ISO_SERIALIZABLE; } + ; + text_or_password: TEXT_STRING { $$=$1.str;} | PASSWORD '(' TEXT_STRING ')' @@ -3017,57 +3504,16 @@ text_or_password: make_scrambled_password(buff,$3.str); $$=buff; } - } - ; + }; -set_option: - SQL_BIG_TABLES { $$= OPTION_BIG_TABLES; } - | SQL_BIG_SELECTS { $$= OPTION_BIG_SELECTS; } - | SQL_LOG_OFF { $$= OPTION_LOG_OFF; } - | SQL_LOG_UPDATE - { - $$= (opt_sql_bin_update)? - OPTION_UPDATE_LOG|OPTION_BIN_LOG: - OPTION_UPDATE_LOG ; - } - | SQL_LOG_BIN - { - $$= (opt_sql_bin_update)? - OPTION_UPDATE_LOG|OPTION_BIN_LOG: - OPTION_BIN_LOG ; - } - | SQL_WARNINGS { $$= OPTION_WARNINGS; } - | SQL_LOW_PRIORITY_UPDATES { $$= OPTION_LOW_PRIORITY_UPDATES; } - | SQL_AUTO_IS_NULL { $$= OPTION_AUTO_IS_NULL; } - | SQL_SAFE_UPDATES { $$= OPTION_SAFE_UPDATES; } - | SQL_BUFFER_RESULT { $$= OPTION_BUFFER_RESULT; } - | SQL_QUOTE_SHOW_CREATE { $$= OPTION_QUOTE_SHOW_CREATE; } - ; - -set_isolation: - GLOBAL_SYM tx_isolation - { - if (check_process_priv()) - YYABORT; - default_tx_isolation= $2; - default_tx_isolation_name=tx_isolation_typelib.type_names[default_tx_isolation]; - } - | SESSION_SYM tx_isolation - { current_thd->session_tx_isolation= Lex->tx_isolation= $2; } - | tx_isolation - { Lex->tx_isolation= $1; } - ; -tx_isolation: - TRANSACTION_SYM ISOLATION LEVEL_SYM isolation_types { $$=$4; } +set_expr_or_default: + expr { $$=$1; } + | DEFAULT { $$=0; } + | ON { $$=new Item_string("ON",2); } + | ALL { $$=new Item_string("ALL",3); } ; -isolation_types: - READ_SYM UNCOMMITTED_SYM { $$= ISO_READ_UNCOMMITTED; } - | READ_SYM COMMITTED_SYM { $$= ISO_READ_COMMITTED; } - | REPEATABLE_SYM READ_SYM { $$= ISO_REPEATABLE_READ; } - | SERIALIZABLE_SYM { $$= ISO_SERIALIZABLE; } - ; /* Lock function */ @@ -3082,40 +3528,98 @@ lock: table_or_tables: TABLE_SYM - | TABLES - ; + | TABLES; table_lock_list: table_lock - | table_lock_list ',' table_lock - ; + | table_lock_list ',' table_lock; table_lock: table_ident opt_table_alias lock_option - { if (!add_table_to_list($1,$2,0,(thr_lock_type) $3)) YYABORT; } - ; + { if (!add_table_to_list($1,$2,0,(thr_lock_type) $3)) YYABORT; }; lock_option: READ_SYM { $$=TL_READ_NO_INSERT; } | WRITE_SYM { $$=current_thd->update_lock_default; } | LOW_PRIORITY WRITE_SYM { $$=TL_WRITE_LOW_PRIORITY; } - | READ_SYM LOCAL_SYM { $$= TL_READ; } - ; + | READ_SYM LOCAL_SYM { $$= TL_READ; }; unlock: - UNLOCK_SYM table_or_tables { Lex->sql_command=SQLCOM_UNLOCK_TABLES; } - ; + UNLOCK_SYM table_or_tables { Lex->sql_command=SQLCOM_UNLOCK_TABLES; }; + + +/* +** Handler: direct access to ISAM functions +*/ + +handler: + HANDLER_SYM table_ident OPEN_SYM opt_table_alias + { + Lex->sql_command = SQLCOM_HA_OPEN; + if (!add_table_to_list($2,$4,0)) + YYABORT; + } + | HANDLER_SYM table_ident CLOSE_SYM + { + Lex->sql_command = SQLCOM_HA_CLOSE; + if (!add_table_to_list($2,0,0)) + YYABORT; + } + | HANDLER_SYM table_ident READ_SYM + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_HA_READ; + lex->ha_rkey_mode= HA_READ_KEY_EXACT; /* Avoid purify warnings */ + lex->select->select_limit= 1; + lex->select->offset_limit= 0L; + if (!add_table_to_list($2,0,0)) + YYABORT; + } + handler_read_or_scan where_clause limit_clause { }; + +handler_read_or_scan: + handler_scan_function { Lex->backup_dir= 0; } + | ident handler_rkey_function { Lex->backup_dir= $1.str; }; + +handler_scan_function: + FIRST_SYM { Lex->ha_read_mode = RFIRST; } + | NEXT_SYM { Lex->ha_read_mode = RNEXT; }; + +handler_rkey_function: + FIRST_SYM { Lex->ha_read_mode = RFIRST; } + | NEXT_SYM { Lex->ha_read_mode = RNEXT; } + | PREV_SYM { Lex->ha_read_mode = RPREV; } + | LAST_SYM { Lex->ha_read_mode = RLAST; } + | handler_rkey_mode + { + LEX *lex=Lex; + lex->ha_read_mode = RKEY; + lex->ha_rkey_mode=$1; + if (!(lex->insert_list = new List_item)) + YYABORT; + } '(' values ')' { }; + +handler_rkey_mode: + EQ { $$=HA_READ_KEY_EXACT; } + | GE { $$=HA_READ_KEY_OR_NEXT; } + | LE { $$=HA_READ_KEY_OR_PREV; } + | GT_SYM { $$=HA_READ_AFTER_KEY; } + | LT { $$=HA_READ_BEFORE_KEY; }; /* GRANT / REVOKE */ revoke: REVOKE { - Lex->sql_command = SQLCOM_REVOKE; - Lex->users_list.empty(); - Lex->columns.empty(); - Lex->grant= Lex->grant_tot_col=0; - Lex->db=0; + LEX *lex=Lex; + lex->sql_command = SQLCOM_REVOKE; + lex->users_list.empty(); + lex->columns.empty(); + lex->grant= lex->grant_tot_col=0; + lex->select->db=0; + lex->ssl_type= SSL_TYPE_NOT_SPECIFIED; + lex->ssl_cipher= lex->x509_subject= lex->x509_issuer= 0; + bzero((char*) &lex->mqh, sizeof(lex->mqh)); } grant_privileges ON opt_table FROM user_list {} @@ -3124,101 +3628,156 @@ revoke: grant: GRANT { - Lex->sql_command = SQLCOM_GRANT; - Lex->users_list.empty(); - Lex->columns.empty(); - Lex->grant= Lex->grant_tot_col=0; - Lex->db=0; + LEX *lex=Lex; + lex->users_list.empty(); + lex->columns.empty(); + lex->sql_command = SQLCOM_GRANT; + lex->grant= lex->grant_tot_col= 0; + lex->select->db= 0; + lex->ssl_type= SSL_TYPE_NOT_SPECIFIED; + lex->ssl_cipher= lex->x509_subject= lex->x509_issuer= 0; + bzero(&(lex->mqh),sizeof(lex->mqh)); } grant_privileges ON opt_table TO_SYM user_list - grant_option + require_clause grant_options {} ; grant_privileges: grant_privilege_list {} - | ALL PRIVILEGES { Lex->grant = UINT_MAX;} - | ALL { Lex->grant = UINT_MAX;} - ; + | ALL PRIVILEGES { Lex->grant = GLOBAL_ACLS;} + | ALL { Lex->grant = GLOBAL_ACLS;}; grant_privilege_list: grant_privilege - | grant_privilege_list ',' grant_privilege - {} - ; + | grant_privilege_list ',' grant_privilege; grant_privilege: - SELECT_SYM - { Lex->which_columns = SELECT_ACL;} - opt_column_list {} - | INSERT - { Lex->which_columns = INSERT_ACL; } - opt_column_list {} - | UPDATE_SYM - { Lex->which_columns = UPDATE_ACL; } - opt_column_list {} - | DELETE_SYM { Lex->grant |= DELETE_ACL;} - | REFERENCES { Lex->which_columns = REFERENCES_ACL;} opt_column_list {} - | USAGE {} + SELECT_SYM { Lex->which_columns = SELECT_ACL;} opt_column_list {} + | INSERT { Lex->which_columns = INSERT_ACL;} opt_column_list {} + | UPDATE_SYM { Lex->which_columns = UPDATE_ACL; } opt_column_list {} + | REFERENCES { Lex->which_columns = REFERENCES_ACL;} opt_column_list {} + | DELETE_SYM { Lex->grant |= DELETE_ACL;} + | USAGE {} | INDEX { Lex->grant |= INDEX_ACL;} | ALTER { Lex->grant |= ALTER_ACL;} | CREATE { Lex->grant |= CREATE_ACL;} | DROP { Lex->grant |= DROP_ACL;} + | EXECUTE_SYM { Lex->grant |= EXECUTE_ACL;} | RELOAD { Lex->grant |= RELOAD_ACL;} | SHUTDOWN { Lex->grant |= SHUTDOWN_ACL;} | PROCESS { Lex->grant |= PROCESS_ACL;} | FILE_SYM { Lex->grant |= FILE_ACL;} | GRANT OPTION { Lex->grant |= GRANT_ACL;} + | SHOW DATABASES { Lex->grant |= SHOW_DB_ACL;} + | SUPER_SYM { Lex->grant |= SUPER_ACL;} + | CREATE TEMPORARY TABLES { Lex->grant |= CREATE_TMP_ACL;} + | LOCK_SYM TABLES { Lex->grant |= LOCK_TABLES_ACL; } + | REPLICATION SLAVE { Lex->grant |= REPL_SLAVE_ACL;} + | REPLICATION CLIENT_SYM { Lex->grant |= REPL_CLIENT_ACL;} ; + +opt_and: + /* empty */ {} + | AND {} + ; + +require_list: + require_list_element opt_and require_list + | require_list_element + ; + +require_list_element: + SUBJECT_SYM TEXT_STRING + { + LEX *lex=Lex; + if (lex->x509_subject) + { + net_printf(&lex->thd->net,ER_DUP_ARGUMENT, "SUBJECT"); + YYABORT; + } + lex->x509_subject=$2.str; + } + | ISSUER_SYM TEXT_STRING + { + LEX *lex=Lex; + if (lex->x509_issuer) + { + net_printf(&lex->thd->net,ER_DUP_ARGUMENT, "ISSUER"); + YYABORT; + } + lex->x509_issuer=$2.str; + } + | CIPHER_SYM TEXT_STRING + { + LEX *lex=Lex; + if (lex->ssl_cipher) + { + net_printf(&lex->thd->net,ER_DUP_ARGUMENT, "CIPHER"); + YYABORT; + } + lex->ssl_cipher=$2.str; + } + ; + opt_table: '*' { - Lex->db=current_thd->db; - if (Lex->grant == UINT_MAX) - Lex->grant = DB_ACLS & ~GRANT_ACL; - else if (Lex->columns.elements) + LEX *lex=Lex; + lex->select->db=lex->thd->db; + if (lex->grant == GLOBAL_ACLS) + lex->grant = DB_ACLS & ~GRANT_ACL; + else if (lex->columns.elements) { - net_printf(¤t_thd->net,ER_ILLEGAL_GRANT_FOR_TABLE); - YYABORT; - } + send_error(&lex->thd->net,ER_ILLEGAL_GRANT_FOR_TABLE); + YYABORT; + } } | ident '.' '*' { - Lex->db = $1.str; - if (Lex->grant == UINT_MAX) - Lex->grant = DB_ACLS & ~GRANT_ACL; - else if (Lex->columns.elements) + LEX *lex=Lex; + lex->select->db = $1.str; + if (lex->grant == GLOBAL_ACLS) + lex->grant = DB_ACLS & ~GRANT_ACL; + else if (lex->columns.elements) { - net_printf(¤t_thd->net,ER_ILLEGAL_GRANT_FOR_TABLE); + send_error(&lex->thd->net,ER_ILLEGAL_GRANT_FOR_TABLE); YYABORT; } } | '*' '.' '*' { - Lex->db = NULL; - if (Lex->grant == UINT_MAX) - Lex->grant = GLOBAL_ACLS & ~GRANT_ACL; - else if (Lex->columns.elements) + LEX *lex=Lex; + lex->select->db = NULL; + if (lex->grant == GLOBAL_ACLS) + lex->grant= GLOBAL_ACLS & ~GRANT_ACL; + else if (lex->columns.elements) { - net_printf(¤t_thd->net,ER_ILLEGAL_GRANT_FOR_TABLE); + send_error(&lex->thd->net,ER_ILLEGAL_GRANT_FOR_TABLE); YYABORT; } } | table_ident { + LEX *lex=Lex; if (!add_table_to_list($1,NULL,0)) YYABORT; - if (Lex->grant == UINT_MAX) - Lex->grant = TABLE_ACLS & ~GRANT_ACL; - } - ; + if (lex->grant == GLOBAL_ACLS) + lex->grant = TABLE_ACLS & ~GRANT_ACL; + }; + user_list: - grant_user { if (Lex->users_list.push_back($1)) YYABORT;} - | user_list ',' grant_user { if (Lex->users_list.push_back($3)) YYABORT;} + grant_user { if (Lex->users_list.push_back($1)) YYABORT;} + | user_list ',' grant_user + { + if (Lex->users_list.push_back($3)) + YYABORT; + } ; + grant_user: user IDENTIFIED_SYM BY TEXT_STRING { @@ -3237,18 +3796,20 @@ grant_user: | user IDENTIFIED_SYM BY PASSWORD TEXT_STRING { $$=$1; $1->password=$5 ; } | user - { $$=$1; $1->password.str=NullS; } - ; + { $$=$1; $1->password.str=NullS; }; + opt_column_list: - /* empty */ { Lex->grant |= Lex->which_columns; } - | '(' column_list ')' - ; + /* empty */ + { + LEX *lex=Lex; + lex->grant |= lex->which_columns; + } + | '(' column_list ')'; column_list: column_list ',' column_list_id - | column_list_id - ; + | column_list_id; column_list_id: ident @@ -3256,23 +3817,64 @@ column_list_id: String *new_str = new String((const char*) $1.str,$1.length); List_iterator <LEX_COLUMN> iter(Lex->columns); class LEX_COLUMN *point; + LEX *lex=Lex; while ((point=iter++)) { if (!my_strcasecmp(point->column.ptr(),new_str->ptr())) break; } - Lex->grant_tot_col|= Lex->which_columns; + lex->grant_tot_col|= lex->which_columns; if (point) - point->rights |= Lex->which_columns; + point->rights |= lex->which_columns; else - Lex->columns.push_back(new LEX_COLUMN (*new_str,Lex->which_columns)); - } + lex->columns.push_back(new LEX_COLUMN (*new_str,lex->which_columns)); + }; + + +require_clause: /* empty */ + | REQUIRE_SYM require_list + { + Lex->ssl_type=SSL_TYPE_SPECIFIED; + } + | REQUIRE_SYM SSL_SYM + { + Lex->ssl_type=SSL_TYPE_ANY; + } + | REQUIRE_SYM X509_SYM + { + Lex->ssl_type=SSL_TYPE_X509; + } + | REQUIRE_SYM NONE_SYM + { + Lex->ssl_type=SSL_TYPE_NONE; + } ; -grant_option: +grant_options: /* empty */ {} - | WITH GRANT OPTION { Lex->grant |= GRANT_ACL;} - ; + | WITH grant_option_list; + +grant_option_list: + grant_option_list grant_option {} + | grant_option {}; + +grant_option: + GRANT OPTION { Lex->grant |= GRANT_ACL;} + | MAX_QUERIES_PER_HOUR ULONG_NUM + { + Lex->mqh.questions=$2; + Lex->mqh.bits |= 1; + } + | MAX_UPDATES_PER_HOUR ULONG_NUM + { + Lex->mqh.updates=$2; + Lex->mqh.bits |= 2; + } + | MAX_CONNECTIONS_PER_HOUR ULONG_NUM + { + Lex->mqh.connections=$2; + Lex->mqh.bits |= 4; + }; begin: BEGIN_SYM { Lex->sql_command = SQLCOM_BEGIN;} opt_work {} @@ -3280,13 +3882,73 @@ begin: opt_work: /* empty */ {} - | WORK_SYM {} - ; + | WORK_SYM {;}; commit: - COMMIT_SYM { Lex->sql_command = SQLCOM_COMMIT;} - ; + COMMIT_SYM { Lex->sql_command = SQLCOM_COMMIT;}; rollback: - ROLLBACK_SYM { Lex->sql_command = SQLCOM_ROLLBACK;} + ROLLBACK_SYM { Lex->sql_command = SQLCOM_ROLLBACK;}; + + +/* +** UNIONS : glue selects together +*/ + + +opt_union: + /* empty */ {} + | union_list; + +union_list: + UNION_SYM union_option + { + LEX *lex=Lex; + if (lex->exchange) + { + /* Only the last SELECT can have INTO...... */ + net_printf(&lex->thd->net, ER_WRONG_USAGE,"UNION","INTO"); + YYABORT; + } + if (lex->select->linkage == NOT_A_SELECT) + { + send_error(&lex->thd->net, ER_SYNTAX_ERROR); + YYABORT; + } + if (mysql_new_select(lex)) + YYABORT; + lex->select->linkage=UNION_TYPE; + } + select_init {} + ; + +union_opt: + union_list {} + | optional_order_or_limit {}; + +optional_order_or_limit: + /* empty + intentional reduce/reduce conflict here !!! + { code } below should not be executed + when neither ORDER BY nor LIMIT are used */ {} + | + { + LEX *lex=Lex; + if (!lex->select->braces) + { + send_error(&lex->thd->net, ER_SYNTAX_ERROR); + YYABORT; + } + if (mysql_new_select(lex)) + YYABORT; + mysql_init_select(lex); + lex->select->linkage=NOT_A_SELECT; + lex->select->select_limit=lex->thd->variables.select_limit; + } + opt_order_clause limit_clause + ; + +union_option: + /* empty */ {} + | ALL { Lex->union_option=1; } ; diff --git a/sql/stacktrace.c b/sql/stacktrace.c index d5711bcd78e..f5c0a59b572 100644 --- a/sql/stacktrace.c +++ b/sql/stacktrace.c @@ -14,7 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <global.h> +#include <my_global.h> #include "stacktrace.h" #include <signal.h> #include <my_pthread.h> @@ -122,8 +122,8 @@ terribly wrong...\n"); return; } #endif /* __alpha__ */ - - if (!stack_bottom) + + if (!stack_bottom || (gptr) stack_bottom > (gptr) &fp) { ulong tmp= min(0x10000,thread_stack); /* Assume that the stack starts at the previous even 65K */ @@ -145,12 +145,12 @@ terribly wrong...\n"); fprintf(stderr, "Warning: Alpha stacks are difficult -\ will be taking some wild guesses, stack trace may be incorrect or \ terminate abruptly\n"); - // On Alpha, we need to get pc + /* On Alpha, we need to get pc */ __asm __volatile__ ("bsr %0, do_next; do_next: " :"=r"(pc) :"r"(pc)); #endif /* __alpha__ */ - + while (fp < (uchar**) stack_bottom) { #ifdef __i386__ @@ -165,7 +165,7 @@ terribly wrong...\n"); { new_fp += 90; } - + if (fp && pc) { pc = find_prev_pc(pc, fp); @@ -195,7 +195,7 @@ terribly wrong...\n"); } fprintf(stderr, "Stack trace seems successful - bottom reached\n"); - + end: fprintf(stderr, "Please read http://www.mysql.com/doc/U/s/Using_stack_trace.html and follow instructions on how to resolve the stack trace. Resolved\n\ stack trace is much more helpful in diagnosing the problem, so please do \n\ @@ -210,8 +210,8 @@ resolve it\n"); void write_core(int sig) { signal(sig, SIG_DFL); - if (fork() != 0) exit(1); // Abort main program - // Core will be written at exit + if (fork() != 0) exit(1); /* Abort main program */ + /* Core will be written at exit */ } #else void write_core(int sig) diff --git a/sql/structs.h b/sql/structs.h index 90924b842d6..77fed422d21 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -50,14 +50,14 @@ typedef struct st_keyfile_info { /* used with ha_info() */ typedef struct st_key_part_info { /* Info about a key part */ Field *field; uint offset; /* offset in record (from 0) */ - uint null_offset; // Offset to null_bit in record + uint null_offset; /* Offset to null_bit in record */ uint16 length; /* Length of key_part */ uint16 store_length; uint16 key_type; uint16 fieldnr; /* Fieldnum in UNIREG */ uint8 key_part_flag; /* 0 or HA_REVERSE_SORT */ uint8 type; - uint8 null_bit; // Position to null_bit + uint8 null_bit; /* Position to null_bit */ } KEY_PART_INFO ; @@ -67,6 +67,7 @@ typedef struct st_key { uint key_parts; /* How many key_parts */ uint extra_length; uint usable_key_parts; /* Should normally be = key_parts */ + enum ha_key_alg algorithm; KEY_PART_INFO *key_part; char *name; /* Name of key */ ulong *rec_per_key; /* Key part distribution */ @@ -105,7 +106,7 @@ typedef struct st_read_record { /* Parameter to read_record */ byte *record; byte *cache,*cache_pos,*cache_end,*read_positions; IO_CACHE *io_cache; - bool print_error; + bool print_error, ignore_not_found_rows; } READ_RECORD; enum timestamp_type { TIMESTAMP_NONE, TIMESTAMP_DATE, TIMESTAMP_FULL, @@ -123,17 +124,37 @@ typedef struct { } INTERVAL; -enum SHOW_TYPE { SHOW_LONG,SHOW_CHAR,SHOW_INT,SHOW_CHAR_PTR,SHOW_BOOL, - SHOW_MY_BOOL,SHOW_OPENTABLES,SHOW_STARTTIME,SHOW_QUESTION, - SHOW_LONG_CONST, SHOW_INT_CONST, SHOW_HAVE, SHOW_LONG_AS_LONGLONG}; +enum SHOW_TYPE +{ + SHOW_UNDEF, + SHOW_LONG, SHOW_LONGLONG, SHOW_INT, SHOW_CHAR, SHOW_CHAR_PTR, SHOW_BOOL, + SHOW_MY_BOOL, SHOW_OPENTABLES, SHOW_STARTTIME, SHOW_QUESTION, + SHOW_LONG_CONST, SHOW_INT_CONST, SHOW_HAVE, SHOW_SYS, SHOW_HA_ROWS, +#ifdef HAVE_OPENSSL + SHOW_SSL_CTX_SESS_ACCEPT, SHOW_SSL_CTX_SESS_ACCEPT_GOOD, + SHOW_SSL_GET_VERSION, SHOW_SSL_CTX_GET_SESSION_CACHE_MODE, + SHOW_SSL_CTX_SESS_CB_HITS, SHOW_SSL_CTX_SESS_ACCEPT_RENEGOTIATE, + SHOW_SSL_CTX_SESS_NUMBER, SHOW_SSL_SESSION_REUSED, + SHOW_SSL_CTX_SESS_GET_CACHE_SIZE, SHOW_SSL_GET_CIPHER, + SHOW_SSL_GET_DEFAULT_TIMEOUT, SHOW_SSL_GET_VERIFY_MODE, + SHOW_SSL_CTX_GET_VERIFY_MODE, SHOW_SSL_GET_VERIFY_DEPTH, + SHOW_SSL_CTX_GET_VERIFY_DEPTH, SHOW_SSL_CTX_SESS_CONNECT, + SHOW_SSL_CTX_SESS_CONNECT_RENEGOTIATE, SHOW_SSL_CTX_SESS_CONNECT_GOOD, + SHOW_SSL_CTX_SESS_HITS, SHOW_SSL_CTX_SESS_MISSES, + SHOW_SSL_CTX_SESS_TIMEOUTS, SHOW_SSL_CTX_SESS_CACHE_FULL, + SHOW_SSL_GET_CIPHER_LIST, +#endif /* HAVE_OPENSSL */ + SHOW_RPL_STATUS, SHOW_SLAVE_RUNNING +}; enum SHOW_COMP_OPTION { SHOW_OPTION_YES, SHOW_OPTION_NO, SHOW_OPTION_DISABLED}; +typedef int *(*update_var)(THD *, struct show_var_st *); -struct show_var_st { +typedef struct show_var_st { const char *name; char *value; SHOW_TYPE type; -}; +} SHOW_VAR; typedef struct lex_string { char *str; @@ -144,6 +165,17 @@ typedef struct st_lex_user { LEX_STRING user, host, password; } LEX_USER; + +typedef struct user_resources { + uint questions, updates, connections, bits; +} USER_RESOURCES; + +typedef struct user_conn { + char *user, *host; + uint len, connections, conn_per_hour, updates, questions, user_len; + USER_RESOURCES user_resources; + time_t intime; +} USER_CONN; /* Bits in form->update */ #define REG_MAKE_DUPP 1 /* Make a copy of record when read */ #define REG_NEW_RECORD 2 /* Write a new record if not found */ @@ -154,13 +186,14 @@ typedef struct st_lex_user { #define REG_MAY_BE_UPDATED 64 #define REG_AUTO_UPDATE 64 /* Used in D-forms for scroll-tables */ #define REG_OVERWRITE 128 -#define REG_SKIPP_DUPP 256 +#define REG_SKIP_DUP 256 /* Bits in form->status */ #define STATUS_NO_RECORD (1+2) /* Record isn't usably */ #define STATUS_GARBAGE 1 -#define STATUS_NOT_FOUND 2 /* No record in database when neaded */ +#define STATUS_NOT_FOUND 2 /* No record in database when needed */ #define STATUS_NO_PARENT 4 /* Parent record wasn't found */ #define STATUS_NOT_READ 8 /* Record isn't read */ #define STATUS_UPDATED 16 /* Record is updated by formula */ #define STATUS_NULL_ROW 32 /* table->null_row is set */ +#define STATUS_DELETED 64 diff --git a/sql/table.cc b/sql/table.cc index 3afadec3801..1635c85eca8 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -46,19 +46,20 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, reg2 uchar *strpos; int j,error; uint rec_buff_length,n_length,int_length,records,key_parts,keys, - interval_count,interval_parts,read_length,db_create_options; + interval_count,interval_parts,read_length,db_create_options; + uint key_info_length, com_length; ulong pos; - char index_file[FN_REFLEN], *names,*keynames; + char index_file[FN_REFLEN], *names, *keynames; uchar head[288],*disk_buff,new_field_pack_flag; my_string record; const char **int_array; - bool new_frm_ver,use_hash, null_field_first; + bool use_hash, null_field_first; File file; Field **field_ptr,*reg_field; KEY *keyinfo; KEY_PART_INFO *key_part; uchar *null_pos; - uint null_bit; + uint null_bit, new_frm_ver, field_pack_length; SQL_CRYPT *crypted=0; DBUG_ENTER("openfrm"); DBUG_PRINT("enter",("name: '%s' form: %lx",name,outparam)); @@ -94,14 +95,15 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, if (my_read(file,(byte*) head,64,MYF(MY_NABP))) goto err_not_open; if (head[0] != (uchar) 254 || head[1] != 1 || - (head[2] != FRM_VER && head[2] != FRM_VER+1)) - goto err_not_open; /* purecov: inspected */ + (head[2] != FRM_VER && head[2] != FRM_VER+1 && head[2] != FRM_VER+3)) + goto err_not_open; /* purecov: inspected */ new_field_pack_flag=head[27]; - new_frm_ver= (head[2] == FRM_VER+1); + new_frm_ver= (head[2] - FRM_VER); + field_pack_length= new_frm_ver < 2 ? 11 : 17; error=3; if (!(pos=get_form_pos(file,head,(TYPELIB*) 0))) - goto err_not_open; /* purecov: inspected */ + goto err_not_open; /* purecov: inspected */ *fn_ext(index_file)='\0'; // Remove .frm extension outparam->db_type=ha_checktype((enum db_type) (uint) *(head+3)); @@ -127,11 +129,12 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, outparam->min_rows=uint4korr(head+22); /* Read keyinformation */ + key_info_length= (uint) uint2korr(head+28); VOID(my_seek(file,(ulong) uint2korr(head+6),MY_SEEK_SET,MYF(0))); - if (read_string(file,(gptr*) &disk_buff,(uint) uint2korr(head+28))) + if (read_string(file,(gptr*) &disk_buff,key_info_length)) goto err_not_open; /* purecov: inspected */ outparam->keys=keys= disk_buff[0]; - outparam->keys_in_use= set_bits(key_map, keys); + outparam->keys_for_keyread= outparam->keys_in_use= set_bits(key_map, keys); outparam->key_parts=key_parts=disk_buff[1]; n_length=keys*sizeof(KEY)+key_parts*sizeof(KEY_PART_INFO); @@ -140,8 +143,8 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, goto err_not_open; /* purecov: inspected */ bzero((char*) keyinfo,n_length); outparam->key_info=keyinfo; - outparam->max_key_length=0; - key_part= (KEY_PART_INFO*) (keyinfo+keys); + outparam->max_key_length= outparam->total_key_length= 0; + key_part= my_reinterpret_cast(KEY_PART_INFO*) (keyinfo+keys); strpos=disk_buff+6; ulong *rec_per_key; @@ -151,9 +154,23 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, for (i=0 ; i < keys ; i++, keyinfo++) { - keyinfo->flags= ((uint) strpos[0]) ^ HA_NOSAME; - keyinfo->key_length= (uint) uint2korr(strpos+1); - keyinfo->key_parts= (uint) strpos[3]; strpos+=4; + if (new_frm_ver == 3) + { + keyinfo->flags= (uint) uint2korr(strpos) ^ HA_NOSAME; + keyinfo->key_length= (uint) uint2korr(strpos+2); + keyinfo->key_parts= (uint) strpos[4]; + keyinfo->algorithm= (enum ha_key_alg) strpos[5]; + strpos+=8; + } + else + { + keyinfo->flags= ((uint) strpos[0]) ^ HA_NOSAME; + keyinfo->key_length= (uint) uint2korr(strpos+1); + keyinfo->key_parts= (uint) strpos[3]; + keyinfo->algorithm= HA_KEY_ALG_UNDEF; + strpos+=4; + } + keyinfo->key_part= key_part; keyinfo->rec_per_key= rec_per_key; for (j=keyinfo->key_parts ; j-- ; key_part++) @@ -163,7 +180,7 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, key_part->offset= (uint) uint2korr(strpos+2)-1; key_part->key_type= (uint) uint2korr(strpos+5); // key_part->field= (Field*) 0; // Will be fixed later - if (new_frm_ver) + if (new_frm_ver >= 1) { key_part->key_part_flag= *(strpos+4); key_part->length= (uint) uint2korr(strpos+7); @@ -184,11 +201,13 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, } set_if_bigger(outparam->max_key_length,keyinfo->key_length+ keyinfo->key_parts); + outparam->total_key_length+= keyinfo->key_length; if (keyinfo->flags & HA_NOSAME) set_if_bigger(outparam->max_unique_length,keyinfo->key_length); } + keynames=(char*) key_part; + strpos+= (strmov(keynames, (char *) strpos) - keynames)+1; - (void) strmov(keynames= (char *) key_part,(char *) strpos); outparam->reclength = uint2korr((head+16)); if (*(head+26) == 1) outparam->system=1; /* one-record-database */ @@ -256,10 +275,11 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, interval_parts=uint2korr(head+272); int_length=uint2korr(head+274); outparam->null_fields=uint2korr(head+282); + com_length=uint2korr(head+284); outparam->comment=strdup_root(&outparam->mem_root, (char*) head+47); - DBUG_PRINT("form",("i_count: %d i_parts: %d index: %d n_length: %d int_length: %d", interval_count,interval_parts, outparam->keys,n_length,int_length)); + DBUG_PRINT("info",("i_count: %d i_parts: %d index: %d n_length: %d int_length: %d", interval_count,interval_parts, outparam->keys,n_length,int_length)); if (!(field_ptr = (Field **) alloc_root(&outparam->mem_root, @@ -267,12 +287,12 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, interval_count*sizeof(TYPELIB)+ (outparam->fields+interval_parts+ keys+3)*sizeof(my_string)+ - (n_length+int_length))))) + (n_length+int_length+com_length))))) goto err_not_open; /* purecov: inspected */ outparam->field=field_ptr; - read_length=((uint) (outparam->fields*11)+pos+ - (uint) (n_length+int_length)); + read_length=(uint) (outparam->fields * field_pack_length + + pos+ (uint) (n_length+int_length+com_length)); if (read_string(file,(gptr*) &disk_buff,read_length)) goto err_not_open; /* purecov: inspected */ if (crypted) @@ -288,7 +308,7 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, names= (char*) (int_array+outparam->fields+interval_parts+keys+3); if (!interval_count) outparam->intervals=0; // For better debugging - memcpy((char*) names, strpos+(outparam->fields*11), + memcpy((char*) names, strpos+(outparam->fields*field_pack_length), (uint) (n_length+int_length)); fix_type_pointers(&int_array,&outparam->fieldnames,1,&names); @@ -321,22 +341,49 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, (hash_get_key) get_field_name,0, HASH_CASE_INSENSITIVE); - for (i=0 ; i < outparam->fields; i++, strpos+= 11, field_ptr++) + for (i=0 ; i < outparam->fields; i++, strpos+=field_pack_length, field_ptr++) { - uint pack_flag= uint2korr(strpos+6); - uint interval_nr= (uint) strpos[10]; + uint pack_flag, interval_nr, unireg_type, recpos, field_length; + enum_field_types field_type; + + if (new_frm_ver == 3) + { + /* new frm file in 4.1 */ + field_length= uint2korr(strpos+3); + recpos= uint3korr(strpos+5); + pack_flag= uint2korr(strpos+8); + unireg_type= (uint) strpos[10]; + interval_nr= (uint) strpos[12]; + field_type= (enum_field_types) (uint) strpos[13]; + } + else + { + /* old frm file */ + field_length= (uint) strpos[3]; + recpos= uint2korr(strpos+4), + pack_flag= uint2korr(strpos+6); + unireg_type= (uint) strpos[8]; + interval_nr= (uint) strpos[10]; + field_type= (enum_field_types) f_packtype(pack_flag); + } *field_ptr=reg_field= - make_field(record+uint2korr(strpos+4), - (uint32) strpos[3], // field_length + make_field(record+recpos, + (uint32) field_length, null_pos,null_bit, pack_flag, - (Field::utype) MTYP_TYPENR((uint) strpos[8]), + field_type, + (Field::utype) MTYP_TYPENR(unireg_type), (interval_nr ? outparam->intervals+interval_nr-1 : (TYPELIB*) 0), outparam->fieldnames.type_names[i], outparam); + if (!*field_ptr) // Field in 4.1 + { + error= 4; + goto err_not_open; /* purecov: inspected */ + } if (!(reg_field->flags & NOT_NULL_FLAG)) { if ((null_bit<<=1) == 256) @@ -359,14 +406,24 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, { uint primary_key=(uint) (find_type((char*) "PRIMARY",&outparam->keynames, 3)-1); - uint ha_option=outparam->file->option_flag(); + uint ha_option=outparam->file->table_flags(); keyinfo=outparam->key_info; key_part=keyinfo->key_part; for (uint key=0 ; key < outparam->keys ; key++,keyinfo++) { uint usable_parts=0; + ulong index_flags; keyinfo->name=(char*) outparam->keynames.type_names[key]; + /* Fix fulltext keys for old .frm files */ + if (outparam->key_info[key].flags & HA_FULLTEXT) + outparam->key_info[key].algorithm= HA_KEY_ALG_FULLTEXT; + + /* This has to be done after the above fulltext correction */ + index_flags=outparam->file->index_flags(key); + if (!(index_flags & HA_KEY_READ_ONLY)) + outparam->keys_for_keyread&= ~((key_map) 1 << key); + if (primary_key >= MAX_KEY && (keyinfo->flags & HA_NOSAME)) { /* @@ -424,7 +481,7 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, keyinfo->key_length ? UNIQUE_KEY_FLAG : MULTIPLE_KEY_FLAG); if (i == 0) field->key_start|= ((key_map) 1 << key); - if ((ha_option & HA_HAVE_KEY_READ_ONLY) && + if ((index_flags & HA_KEY_READ_ONLY) && field->key_length() == key_part->length && field->type() != FIELD_TYPE_BLOB) { @@ -432,8 +489,9 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, (!(ha_option & HA_KEY_READ_WRONG_STR) && !(keyinfo->flags & HA_FULLTEXT))) field->part_of_key|= ((key_map) 1 << key); - if (field->key_type() != HA_KEYTYPE_TEXT || - !(keyinfo->flags & HA_FULLTEXT)) + if ((field->key_type() != HA_KEYTYPE_TEXT || + !(keyinfo->flags & HA_FULLTEXT)) && + !(index_flags & HA_WRONG_ASCII_ORDER)) field->part_of_sortkey|= ((key_map) 1 << key); } if (!(key_part->key_part_flag & HA_REVERSE_SORT) && @@ -443,15 +501,20 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, if (key == primary_key) { field->flags|= PRI_KEY_FLAG; + /* + If this field is part of the primary key and all keys contains + the primary key, then we can use any key to find this column + */ if (ha_option & HA_PRIMARY_KEY_IN_READ_INDEX) - field->part_of_key|= ((key_map) 1 << primary_key); + field->part_of_key= outparam->keys_in_use; } if (field->key_length() != key_part->length) { key_part->key_part_flag|= HA_PART_KEY; if (field->type() != FIELD_TYPE_BLOB) { // Create a new field - field=key_part->field=field->new_field(outparam); + field=key_part->field=field->new_field(&outparam->mem_root, + outparam); field->field_length=key_part->length; } } @@ -475,8 +538,6 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, (outparam->keys_in_use & ((key_map) 1 << primary_key))) { outparam->primary_key=primary_key; - if (outparam->file->option_flag() & HA_PRIMARY_KEY_IN_READ_INDEX) - outparam->ref_primary_key= (key_map) 1 << primary_key; /* If we are using an integer as the primary key then allow the user to refer to it as '_rowid' @@ -504,6 +565,7 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag, bfill(outparam->null_flags+outparam->rec_buff_length*2,null_length,255); } + if ((reg_field=outparam->found_next_number_field)) { if ((int) (outparam->next_number_index= (uint) @@ -757,7 +819,7 @@ ulong make_new_entry(File file, uchar *fileinfo, TYPELIB *formnames, int2store(fileinfo+8,names+1); int2store(fileinfo+4,n_length+length); - VOID(my_chsize(file,newpos,MYF(MY_WME))); /* Append file with '\0' */ + VOID(my_chsize(file, newpos, 0, MYF(MY_WME)));/* Append file with '\0' */ DBUG_RETURN(newpos); } /* make_new_entry */ @@ -831,7 +893,7 @@ fix_type_pointers(const char ***array, TYPELIB *point_to_type, uint types, *type_name= '\0'; /* End string */ ptr=type_name; } - ptr+=2; /* Skipp end mark and last 0 */ + ptr+=2; /* Skip end mark and last 0 */ } else ptr++; @@ -917,7 +979,7 @@ ulong next_io_size(register ulong pos) void append_unescaped(String *res,const char *pos) { - for ( ; *pos ; pos++) + for (; *pos ; pos++) { switch (*pos) { case 0: /* Must be escaped for 'mysql' */ @@ -1005,6 +1067,7 @@ File create_frm(register my_string name, uint reclength, uchar *fileinfo, void update_create_info_from_table(HA_CREATE_INFO *create_info, TABLE *table) { + DBUG_ENTER("update_create_info_from_table"); create_info->max_rows=table->max_rows; create_info->min_rows=table->min_rows; create_info->table_options=table->db_create_options; @@ -1013,7 +1076,8 @@ void update_create_info_from_table(HA_CREATE_INFO *create_info, TABLE *table) create_info->raid_type=table->raid_type; create_info->raid_chunks=table->raid_chunks; create_info->raid_chunksize=table->raid_chunksize; -} + DBUG_VOID_RETURN; +} int rename_file_ext(const char * from,const char * to,const char * ext) @@ -1081,7 +1145,8 @@ bool check_db_name(char *name) } } #endif - if (*name == '/' || *name == FN_LIBCHAR || *name == FN_EXTCHAR) + if (*name == '/' || *name == '\\' || *name == FN_LIBCHAR || + *name == FN_EXTCHAR) return 1; name++; } @@ -1159,7 +1224,7 @@ db_type get_table_type(const char *name) error=my_read(file,(byte*) head,4,MYF(MY_NABP)); my_close(file,MYF(0)); if (error || head[0] != (uchar) 254 || head[1] != 1 || - (head[2] != FRM_VER && head[2] != FRM_VER+1)) + (head[2] != FRM_VER && head[2] != FRM_VER+1 && head[2] != FRM_VER+3)) DBUG_RETURN(DB_TYPE_UNKNOWN); DBUG_RETURN(ha_checktype((enum db_type) (uint) *(head+3))); } diff --git a/sql/table.h b/sql/table.h index 4302e3a3f27..b6935ea6a32 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -37,8 +37,8 @@ typedef struct st_grant_info { GRANT_TABLE *grant_table; uint version; - uint privilege; - uint want_privilege; + ulong privilege; + ulong want_privilege; } GRANT_INFO; enum tmp_table_type {NO_TMP_TABLE=0, TMP_TABLE=1, TRANSACTIONAL_TMP_TABLE=2}; @@ -58,10 +58,12 @@ struct st_table { uint reclength; /* Recordlength */ uint rec_buff_length; uint keys,key_parts,primary_key,max_key_length,max_unique_length; + uint total_key_length; uint uniques; uint null_fields; /* number of null fields */ uint blob_fields; /* number of blob fields */ - key_map keys_in_use, keys_in_use_for_query; + key_map keys_in_use, keys_for_keyread; + key_map quick_keys, used_keys, keys_in_use_for_query; KEY *key_info; /* data of keys in database */ TYPELIB keynames; /* Pointers to keynames */ ha_rows max_rows; /* create information */ @@ -89,8 +91,9 @@ struct st_table { my_bool copy_blobs; /* copy_blobs when storing */ my_bool null_row; /* All columns are null */ my_bool maybe_null,outer_join; /* Used with OUTER JOIN */ - my_bool distinct,const_table; - my_bool key_read; + my_bool force_index; + my_bool distinct,const_table,no_rows; + my_bool key_read, bulk_insert; my_bool crypted; my_bool db_low_byte_first; /* Portable row format */ my_bool locked_by_flush; @@ -98,6 +101,7 @@ struct st_table { my_bool fulltext_searched; my_bool crashed; my_bool is_view; + my_bool no_keyread; Field *next_number_field, /* Set if next_number is activated */ *found_next_number_field, /* Set on open */ *rowid_field; @@ -111,22 +115,25 @@ struct st_table { char *table_name,*real_name,*path; uint key_length; /* Length of key */ uint tablenr,used_fields,null_bytes; - table_map map; + table_map map; /* ID bit of table (1,2,4,8,16...) */ ulong version,flush_version; uchar *null_flags; - IO_CACHE *io_cache; /* If sorted trough file*/ - byte *record_pointers; /* If sorted in memory */ - ha_rows found_records; /* How many records in sort */ + IO_CACHE *io_cache; /* If sorted trough file*/ + byte *record_pointers; /* If sorted in memory */ + ha_rows found_records; /* How many records in sort */ ORDER *group; - key_map quick_keys, used_keys, ref_primary_key; ha_rows quick_rows[MAX_KEY]; uint quick_key_parts[MAX_KEY]; key_part_map const_key_parts[MAX_KEY]; ulong query_id; - uint temp_pool_slot; + union /* Temporary variables */ + { + uint temp_pool_slot; /* Used by intern temp tables */ + struct st_table_list *pos_in_table_list; + }; - THD *in_use; /* Which thread uses this */ + THD *in_use; /* Which thread uses this */ struct st_table *next,*prev; }; @@ -134,16 +141,37 @@ struct st_table { #define JOIN_TYPE_LEFT 1 #define JOIN_TYPE_RIGHT 2 -typedef struct st_table_list { +typedef struct st_table_list +{ struct st_table_list *next; - char *db,*alias,*real_name; - Item *on_expr; /* Used with outer join */ - struct st_table_list *natural_join; /* natural join on this table*/ - List<String> *use_index,*ignore_index; + char *db, *alias, *real_name; + Item *on_expr; /* Used with outer join */ + struct st_table_list *natural_join; /* natural join on this table*/ + /* ... join ... USE INDEX ... IGNORE INDEX */ + List<String> *use_index,*ignore_index; TABLE *table; GRANT_INFO grant; thr_lock_type lock_type; - uint outer_join; /* Which join type */ - bool straight; /* optimize with prev table */ - bool updating; /* for replicate-do/ignore table */ + uint outer_join; /* Which join type */ + uint shared; /* Used in union or in multi-upd */ + uint32 db_length, real_name_length; + bool straight; /* optimize with prev table */ + bool updating; /* for replicate-do/ignore table */ + bool force_index; /* Prefer index over table scan */ } TABLE_LIST; + + +typedef struct st_changed_table_list +{ + struct st_changed_table_list *next; + char *key; + uint32 key_length; +} CHANGED_TABLE_LIST; + + +typedef struct st_open_table_list +{ + struct st_open_table_list *next; + char *db,*table; + uint32 in_use,locked; +} OPEN_TABLE_LIST; diff --git a/sql/thr_malloc.cc b/sql/thr_malloc.cc index deb304443df..8b9baa6f045 100644 --- a/sql/thr_malloc.cc +++ b/sql/thr_malloc.cc @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -22,7 +22,9 @@ extern "C" { void sql_alloc_error_handler(void) { - current_thd->fatal_error=1; /* purecov: inspected */ + THD *thd=current_thd; + if (thd) // QQ; To be removed + thd->fatal_error=1; /* purecov: inspected */ sql_print_error(ER(ER_OUT_OF_RESOURCES)); } } diff --git a/sql/time.cc b/sql/time.cc index 1d7e055f682..4fe79966404 100644 --- a/sql/time.cc +++ b/sql/time.cc @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -24,7 +24,7 @@ static ulong const days_at_timestart=719528; /* daynr at 1970.01.01 */ uchar *days_in_month= (uchar*) "\037\034\037\036\037\036\037\037\036\037\036\037"; - /* Init some variabels neaded when using my_local_time */ + /* Init some variabels needed when using my_local_time */ /* Currently only my_time_zone is inited */ static long my_time_zone=0; @@ -54,7 +54,7 @@ void init_time(void) This code handles also day light saving time. The idea is to cache the time zone (including daylight saving time) for the next call to make things faster. - + */ long my_gmt_sec(TIME *t) @@ -128,7 +128,7 @@ long calc_daynr(uint year,uint month,uint day) DBUG_ENTER("calc_daynr"); if (year == 0 && month == 0 && day == 0) - DBUG_RETURN(0); /* Skipp errors */ + DBUG_RETURN(0); /* Skip errors */ if (year < 200) { if ((year=year+1900) < 1900+YY_PART_YEAR) @@ -176,7 +176,9 @@ uint calc_week(TIME *l_time, bool with_year, bool sunday_first_day_of_week, ulong first_daynr=calc_daynr(l_time->year,1,1); uint weekday=calc_weekday(first_daynr,sunday_first_day_of_week); *year=l_time->year; - if (l_time->month == 1 && weekday >= 4 && l_time->day <= 7-weekday) + if (l_time->month == 1 && l_time->day <= 7-weekday && + ((!sunday_first_day_of_week && weekday >= 4) || + (sunday_first_day_of_week && weekday != 0))) { /* Last week of the previous year */ if (!with_year) @@ -186,7 +188,8 @@ uint calc_week(TIME *l_time, bool with_year, bool sunday_first_day_of_week, first_daynr-= (days=calc_days_in_year(*year)); weekday= (weekday + 53*7- days) % 7; } - if (weekday >= 4) + if ((sunday_first_day_of_week && weekday != 0) || + (!sunday_first_day_of_week && weekday >= 4)) days= daynr - (first_daynr+ (7-weekday)); else days= daynr - (first_daynr - weekday); @@ -426,12 +429,13 @@ timestamp_type str_to_TIME(const char *str, uint length, TIME *l_time,bool fuzzy_date) { uint field_length,year_length,digits,i,number_of_fields,date[7]; + uint not_zero_date; const char *pos; const char *end=str+length; DBUG_ENTER("str_to_TIME"); DBUG_PRINT("enter",("str: %.*s",length,str)); - for (; str != end && !isdigit(*str) ; str++) ; // Skipp garbage + for (; str != end && !isdigit(*str) ; str++) ; // Skip garbage if (str == end) DBUG_RETURN(TIMESTAMP_NONE); /* @@ -443,6 +447,7 @@ str_to_TIME(const char *str, uint length, TIME *l_time,bool fuzzy_date) digits= (uint) (pos-str); year_length= (digits == 4 || digits == 8 || digits >= 14) ? 4 : 2; field_length=year_length-1; + not_zero_date= 0; for (i=0 ; i < 6 && str != end && isdigit(*str) ; i++) { uint tmp_value=(uint) (uchar) (*str++ - '0'); @@ -452,6 +457,7 @@ str_to_TIME(const char *str, uint length, TIME *l_time,bool fuzzy_date) str++; } date[i]=tmp_value; + not_zero_date|= tmp_value; if (i == 2 && str != end && *str == 'T') str++; // ISO8601: CCYYMMDDThhmmss else if ( i != 5 ) // Skip inter-field delimiters @@ -475,6 +481,7 @@ str_to_TIME(const char *str, uint length, TIME *l_time,bool fuzzy_date) while (str++ != end && isdigit(str[0]) && field_length--) tmp_value=tmp_value*10 + (uint) (uchar) (*str - '0'); date[6]=tmp_value; + not_zero_date|= tmp_value; } else date[6]=0; @@ -488,12 +495,25 @@ str_to_TIME(const char *str, uint length, TIME *l_time,bool fuzzy_date) date[2] > 31 || date[3] > 23 || date[4] > 59 || date[5] > 59 || !fuzzy_date && (date[1] == 0 || date[2] == 0)) { - current_thd->cuted_fields++; + /* Only give warning for a zero date if there is some garbage after */ + if (!not_zero_date) // If zero date + { + for (; str != end ; str++) + { + if (!isspace(*str)) + { + not_zero_date= 1; // Give warning + break; + } + } + } + if (not_zero_date) + current_thd->cuted_fields++; DBUG_RETURN(TIMESTAMP_NONE); } if (str != end && current_thd->count_cuted_fields) { - for ( ; str != end ; str++) + for (; str != end ; str++) { if (!isspace(*str)) { @@ -591,7 +611,7 @@ bool str_to_time(const char *str,uint length,TIME *l_time) date[0]=value; state=1; // Assume next is hours found_days=1; - str++; // Skipp space; + str++; // Skip space; } else if ((end-str) > 1 && *str == ':' && isdigit(str[1])) { @@ -599,7 +619,7 @@ bool str_to_time(const char *str,uint length,TIME *l_time) date[1]=value; state=2; found_hours=1; - str++; // skipp ':' + str++; // skip ':' } else { @@ -620,7 +640,7 @@ bool str_to_time(const char *str,uint length,TIME *l_time) date[state++]=value; if (state == 4 || (end-str) < 2 || *str != ':' || !isdigit(str[1])) break; - str++; // Skipp ':' + str++; // Skip ':' } if (state != 4) diff --git a/sql/udf_example.cc b/sql/udf_example.cc index a91db5ee1cc..176ddeb10a3 100644 --- a/sql/udf_example.cc +++ b/sql/udf_example.cc @@ -1,15 +1,15 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - +/* Copyright (C) 2002 MySQL AB + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -61,7 +61,7 @@ ** On the end is a couple of functions that converts hostnames to ip and ** vice versa. ** -** A dynamicly loadable file should be compiled sharable +** A dynamicly loadable file should be compiled shared. ** (something like: gcc -shared -o my_func.so myfunc.cc). ** You can easily get all switches right by doing: ** cd sql ; make udf_example.o @@ -69,6 +69,8 @@ ** the line and add -shared -o udf_example.so to the end of the compile line. ** The resulting library (udf_example.so) should be copied to some dir ** searched by ld. (/usr/lib ?) +** If you are using gcc, then you should be able to create the udf_example.so +** by simply doing 'make udf_example.so'. ** ** After the library is made one must notify mysqld about the new ** functions with the commands: @@ -109,8 +111,15 @@ #ifdef STANDARD #include <stdio.h> #include <string.h> +#ifdef __WIN__ +typedef unsigned __int64 ulonglong; /* Microsofts 64 bit types */ +typedef __int64 longlong; #else -#include <global.h> +typedef unsigned long long ulonglong; +typedef long long longlong; +#endif /*__WIN__*/ +#else +#include <my_global.h> #include <my_sys.h> #endif #include <mysql.h> @@ -133,7 +142,7 @@ longlong myfunc_int(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error); my_bool sequence_init(UDF_INIT *initid, UDF_ARGS *args, char *message); void sequence_deinit(UDF_INIT *initid); -long long sequence(UDF_INIT *initid, UDF_ARGS *args, char *is_null, +longlong sequence(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error); my_bool avgcost_init( UDF_INIT* initid, UDF_ARGS* args, char* message ); void avgcost_deinit( UDF_INIT* initid ); @@ -271,7 +280,7 @@ char *metaphon(UDF_INIT *initid, UDF_ARGS *args, char *result, * characters and converting to uppercase. *-------------------------------------------------------*/ - for ( n = ntrans + 1, n_end = ntrans + sizeof(ntrans)-2; + for (n = ntrans + 1, n_end = ntrans + sizeof(ntrans)-2; word != w_end && n < n_end; word++ ) if ( isalpha ( *word )) *n++ = toupper ( *word ); @@ -324,7 +333,7 @@ char *metaphon(UDF_INIT *initid, UDF_ARGS *args, char *result, KSflag = 0; /* state flag for KS translation */ - for ( metaph_end = result + MAXMETAPH, n_start = n; + for (metaph_end = result + MAXMETAPH, n_start = n; n <= n_end && result < metaph_end; n++ ) { @@ -402,7 +411,7 @@ char *metaphon(UDF_INIT *initid, UDF_ARGS *args, char *result, n[2] != 'G' ) ? (char)'J' : (char)'K'; else - if( n[1] == 'H' && + if ( n[1] == 'H' && !NOGHTOF( *( n - 3 )) && *( n - 4 ) != 'H') *result++ = 'F'; @@ -440,7 +449,7 @@ char *metaphon(UDF_INIT *initid, UDF_ARGS *args, char *result, case 'T': /* TIO, TIA = X ("sh" sound) */ /* TH = 0, ("th" sound ) */ - if( *( n + 1 ) == 'I' && ( n[2] == 'O' + if ( *( n + 1 ) == 'I' && ( n[2] == 'O' || n[2] == 'A') ) *result++ = 'X'; else @@ -556,10 +565,10 @@ double myfunc_double(UDF_INIT *initid, UDF_ARGS *args, char *is_null, /* This function returns the sum of all arguments */ -long long myfunc_int(UDF_INIT *initid, UDF_ARGS *args, char *is_null, +longlong myfunc_int(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error) { - long long val = 0; + longlong val = 0; for (uint i = 0; i < args->arg_count; i++) { if (args->args[i] == NULL) @@ -569,10 +578,10 @@ long long myfunc_int(UDF_INIT *initid, UDF_ARGS *args, char *is_null, val += args->lengths[i]; break; case INT_RESULT: // Add numbers - val += *((long long*) args->args[i]); + val += *((longlong*) args->args[i]); break; - case REAL_RESULT: // Add numers as long long - val += (long long) *((double*) args->args[i]); + case REAL_RESULT: // Add numers as longlong + val += (longlong) *((double*) args->args[i]); break; } } @@ -615,12 +624,12 @@ void sequence_deinit(UDF_INIT *initid) free(initid->ptr); } -long long sequence(UDF_INIT *initid, UDF_ARGS *args, char *is_null, +longlong sequence(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error) { ulonglong val=0; if (args->arg_count) - val= *((long long*) args->args[0]); + val= *((longlong*) args->args[0]); return ++ *((longlong*) initid->ptr) + val; } @@ -741,10 +750,10 @@ char *reverse_lookup(UDF_INIT *initid, UDF_ARGS *args, char *result, return 0; } sprintf(result,"%d.%d.%d.%d", - (int) *((long long*) args->args[0]), - (int) *((long long*) args->args[1]), - (int) *((long long*) args->args[2]), - (int) *((long long*) args->args[3])); + (int) *((longlong*) args->args[0]), + (int) *((longlong*) args->args[1]), + (int) *((longlong*) args->args[2]), + (int) *((longlong*) args->args[3])); } else { // string argument @@ -793,9 +802,9 @@ char *reverse_lookup(UDF_INIT *initid, UDF_ARGS *args, char *result, struct avgcost_data { - unsigned long long count; - long long totalquantity; - double totalprice; + ulonglong count; + longlong totalquantity; + double totalprice; }; @@ -869,8 +878,8 @@ avgcost_add( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char* message ) if (args->args[0] && args->args[1]) { struct avgcost_data* data = (struct avgcost_data*)initid->ptr; - long long quantity = *((long long*)args->args[0]); - long long newquantity = data->totalquantity + quantity; + longlong quantity = *((longlong*)args->args[0]); + longlong newquantity = data->totalquantity + quantity; double price = *((double*)args->args[1]); data->count++; diff --git a/sql/uniques.cc b/sql/uniques.cc new file mode 100644 index 00000000000..ed256a4b791 --- /dev/null +++ b/sql/uniques.cc @@ -0,0 +1,167 @@ +/* Copyright (C) 2001 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* + Function to handle quick removal of duplicates + This code is used when doing multi-table deletes to find the rows in + reference tables that needs to be deleted. + + The basic idea is as follows: + + Store first all strings in a binary tree, ignoring duplicates. + When the tree uses more memory than 'max_heap_table_size', + write the tree (in sorted order) out to disk and start with a new tree. + When all data has been generated, merge the trees (removing any found + duplicates). + + The unique entries will be returned in sort order, to ensure that we do the + deletes in disk order. +*/ + +#include "mysql_priv.h" +#include "sql_sort.h" + + +int unique_write_to_file(gptr key, element_count count, Unique *unique) +{ + return my_b_write(&unique->file, (byte*) key, + unique->tree.size_of_element) ? 1 : 0; +} + +int unique_write_to_ptrs(gptr key, element_count count, Unique *unique) +{ + memcpy(unique->record_pointers, key, unique->tree.size_of_element); + unique->record_pointers+=unique->tree.size_of_element; + return 0; +} + +Unique::Unique(qsort_cmp2 comp_func, void * comp_func_fixed_arg, + uint size, ulong max_in_memory_size_arg) + :max_in_memory_size(max_in_memory_size_arg),elements(0) +{ + my_b_clear(&file); + init_tree(&tree, max_in_memory_size / 16, 0, size, comp_func, 0, NULL, + comp_func_fixed_arg); + /* If the following fail's the next add will also fail */ + my_init_dynamic_array(&file_ptrs, sizeof(BUFFPEK), 16, 16); + max_elements= max_in_memory_size / ALIGN_SIZE(sizeof(TREE_ELEMENT)+size); + open_cached_file(&file, mysql_tmpdir,TEMP_PREFIX, DISK_BUFFER_SIZE, + MYF(MY_WME)); +} + + +Unique::~Unique() +{ + close_cached_file(&file); + delete_tree(&tree); + delete_dynamic(&file_ptrs); +} + + + /* Write tree to disk; clear tree */ +bool Unique::flush() +{ + BUFFPEK file_ptr; + elements+= tree.elements_in_tree; + file_ptr.count=tree.elements_in_tree; + file_ptr.file_pos=my_b_tell(&file); + if (tree_walk(&tree, (tree_walk_action) unique_write_to_file, + (void*) this, left_root_right) || + insert_dynamic(&file_ptrs, (gptr) &file_ptr)) + return 1; + delete_tree(&tree); + return 0; +} + + +/* + Modify the TABLE element so that when one calls init_records() + the rows will be read in priority order. +*/ + +bool Unique::get(TABLE *table) +{ + SORTPARAM sort_param; + table->found_records=elements+tree.elements_in_tree; + + if (my_b_tell(&file) == 0) + { + /* Whole tree is in memory; Don't use disk if you don't need to */ + if ((record_pointers=table->record_pointers= (byte*) + my_malloc(tree.size_of_element * tree.elements_in_tree, MYF(0)))) + { + (void) tree_walk(&tree, (tree_walk_action) unique_write_to_ptrs, + this, left_root_right); + return 0; + } + } + /* Not enough memory; Save the result to file */ + if (flush()) + return 1; + + IO_CACHE *outfile=table->io_cache; + BUFFPEK *file_ptr= (BUFFPEK*) file_ptrs.buffer; + uint maxbuffer= file_ptrs.elements - 1; + uchar *sort_buffer; + my_off_t save_pos; + bool error=1; + + /* Open cached file if it isn't open */ + outfile=table->io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE), + MYF(MY_ZEROFILL)); + + if (!outfile || ! my_b_inited(outfile) && + open_cached_file(outfile,mysql_tmpdir,TEMP_PREFIX,READ_RECORD_BUFFER, + MYF(MY_WME))) + return 1; + reinit_io_cache(outfile,WRITE_CACHE,0L,0,0); + + bzero((char*) &sort_param,sizeof(sort_param)); + sort_param.max_rows= elements; + sort_param.sort_form=table; + sort_param.sort_length=sort_param.ref_length=tree.size_of_element; + sort_param.keys= max_in_memory_size / sort_param.sort_length; + sort_param.not_killable=1; + + if (!(sort_buffer=(uchar*) my_malloc((sort_param.keys+1) * + sort_param.sort_length, + MYF(0)))) + return 1; + sort_param.unique_buff= sort_buffer+(sort_param.keys* + sort_param.sort_length); + + /* Merge the buffers to one file, removing duplicates */ + if (merge_many_buff(&sort_param,sort_buffer,file_ptr,&maxbuffer,&file)) + goto err; + if (flush_io_cache(&file) || + reinit_io_cache(&file,READ_CACHE,0L,0,0)) + goto err; + if (merge_buffers(&sort_param, &file, outfile, sort_buffer, file_ptr, + file_ptr, file_ptr+maxbuffer,0)) + goto err; + error=0; +err: + x_free((gptr) sort_buffer); + if (flush_io_cache(outfile)) + error=1; + + /* Setup io_cache for reading */ + save_pos=outfile->pos_in_file; + if (reinit_io_cache(outfile,READ_CACHE,0L,0,0)) + error=1; + outfile->end_of_file=save_pos; + return error; +} diff --git a/sql/unireg.cc b/sql/unireg.cc index f7b040adebe..5183f471fa2 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -20,7 +20,7 @@ struct. In the following functions FIELD * is an ordinary field-structure with the following exeptions: - sc_length,typepos,row,kol,dtype,regnr and field nead not to be set. + sc_length,typepos,row,kol,dtype,regnr and field need not to be set. str is a (long) to record position where 0 is the first position. */ @@ -105,7 +105,7 @@ int rea_create_table(my_string file_name, fileinfo[26]= (uchar) test((create_info->max_rows == 1) && (create_info->min_rows == 1) && (keys == 0)); int2store(fileinfo+28,key_info_length); - strnmov((char*) forminfo+47,create_info->comment ? create_info->comment : "", + strmake((char*) forminfo+47,create_info->comment ? create_info->comment : "", 60); forminfo[46]=(uchar) strlen((char*)forminfo+47); // Length of comment @@ -246,7 +246,7 @@ static uchar * pack_screens(List<create_field> &create_fields, static uint pack_keys(uchar *keybuff,uint key_count,KEY *keyinfo) { uint key_parts,length; - uchar *pos,*keyname_pos; + uchar *pos, *keyname_pos, *key_alg_pos; KEY *key,*end; KEY_PART_INFO *key_part,*key_part_end; DBUG_ENTER("pack_keys"); @@ -290,11 +290,18 @@ static uint pack_keys(uchar *keybuff,uint key_count,KEY *keyinfo) } *(pos++)=0; + /* For MySQL 4.0; Store key algoritms last */ + key_alg_pos= pos; + for (key=keyinfo ; key != end ; key++) + { + *(pos++)= (uchar) key->algorithm; + } + keybuff[0]=(uchar) key_count; keybuff[1]=(uchar) key_parts; length=(uint) (keyname_pos-keybuff); int2store(keybuff+2,length); - length=(uint) (pos-keyname_pos); + length=(uint) (key_alg_pos-keyname_pos); int2store(keybuff+4,length); DBUG_RETURN((uint) (pos-keybuff)); } /* pack_keys */ @@ -391,8 +398,8 @@ static bool pack_header(uchar *forminfo, enum db_type table_type, int2store(forminfo+272,int_parts); int2store(forminfo+274,int_length); int2store(forminfo+276,time_stamp_pos); - int2store(forminfo+278,80); /* Columns neaded */ - int2store(forminfo+280,22); /* Rows neaded */ + int2store(forminfo+278,80); /* Columns needed */ + int2store(forminfo+280,22); /* Rows needed */ int2store(forminfo+282,null_fields); DBUG_RETURN(0); } /* pack_header */ @@ -550,6 +557,7 @@ static bool make_empty_rec(File file,enum db_type table_type, null_pos+null_count/8, 1 << (null_count & 7), field->pack_flag, + field->sql_type, field->unireg_check, field->interval, field->field_name, @@ -566,7 +574,7 @@ static bool make_empty_rec(File file,enum db_type table_type, if (field->def && (regfield->real_type() != FIELD_TYPE_YEAR || field->def->val_int() != 0)) - field->def->save_in_field(regfield); + field->def->save_in_field(regfield, 1); else if (regfield->real_type() == FIELD_TYPE_ENUM && (field->flags & NOT_NULL_FLAG)) { diff --git a/sql/unireg.h b/sql/unireg.h index f8f5edd5156..eec89fcee0f 100644 --- a/sql/unireg.h +++ b/sql/unireg.h @@ -1,15 +1,15 @@ /* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -46,9 +46,10 @@ #define MAX_DBKEY_LENGTH (FN_LEN*2+6) /* extra 4 bytes for slave tmp * tables */ #define MAX_FIELD_NAME 34 /* Max colum name length +2 */ +#define MAX_SYS_VAR_LENGTH 32 #define MAX_KEY 32 /* Max used keys */ #define MAX_REF_PARTS 16 /* Max parts used as ref */ -#define MAX_KEY_LENGTH 500 /* max possible key */ +#define MAX_KEY_LENGTH 1024 /* max possible key */ #if SIZEOF_OFF_T > 4 #define MAX_REFLENGTH 8 /* Max length for record ref */ #else @@ -80,7 +81,7 @@ #define MYF_RW MYF(MY_WME+MY_NABP) /* Vid my_read & my_write */ #define SPECIAL_USE_LOCKS 1 /* Lock used databases */ -#define SPECIAL_NO_NEW_FUNC 2 /* Skipp new functions */ +#define SPECIAL_NO_NEW_FUNC 2 /* Skip new functions */ #define SPECIAL_NEW_FUNC 4 /* New nonstandard functions */ #define SPECIAL_WAIT_IF_LOCKED 8 /* Wait if locked database */ #define SPECIAL_SAME_DB_NAME 16 /* form name = file name */ @@ -122,6 +123,17 @@ bfill((A)->null_flags,(A)->null_bytes,255);\ #define TE_INFO_LENGTH 3 #define MTYP_NOEMPTY_BIT 128 +/* + * Minimum length pattern before Turbo Boyer-Moore is used + * for SELECT "text" LIKE "%pattern%", excluding the two + * wildcards in class Item_func_like. + */ +#define MIN_TURBOBM_PATTERN_LEN 3 + +/* Defines for binary logging */ + +#define BIN_LOG_HEADER_SIZE 4 + /* Include prototypes for unireg */ #include "mysqld_error.h" diff --git a/sql/violite.c b/sql/violite.c deleted file mode 100644 index 37fee6fad3d..00000000000 --- a/sql/violite.c +++ /dev/null @@ -1,443 +0,0 @@ -/* 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 */ - -/* - Note that we can't have assertion on file descriptors; The reason for - this is that during mysql shutdown, another thread can close a file - we are working on. In this case we should just return read errors from - the file descriptior. -*/ - -#include <global.h> - -#ifndef HAVE_VIO /* is Vio suppored by the Vio lib ? */ - -#include <errno.h> -#include <assert.h> -#include <violite.h> -#include <my_sys.h> -#include <my_net.h> -#include <m_string.h> -#ifdef HAVE_POLL -#include <sys/poll.h> -#endif -#ifdef HAVE_SYS_IOCTL_H -#include <sys/ioctl.h> -#endif -#ifdef HAVE_FCNTL_H -#include <fcntl.h> -#endif - -#if !defined(MSDOS) && !defined(__WIN__) && !defined(HAVE_BROKEN_NETINET_INCLUDES) && !defined(__BEOS__) && !defined(__FreeBSD__) -#include <netinet/in_systm.h> -#include <netinet/ip.h> -#if !defined(alpha_linux_port) -#include <netinet/tcp.h> -#endif -#endif - -#if defined(__EMX__) || defined(OS2) -#define ioctlsocket ioctl -#endif /* defined(__EMX__) */ - -#if defined(MSDOS) || defined(__WIN__) -#define O_NONBLOCK 1 /* For emulation of fcntl() */ -#endif -#ifndef EWOULDBLOCK -#define SOCKET_EWOULDBLOCK SOCKET_EAGAIN -#endif - -#ifndef __WIN__ -#define HANDLE void * -#endif - -struct st_vio -{ - my_socket sd; /* my_socket - real or imaginary */ - HANDLE hPipe; - my_bool localhost; /* Are we from localhost? */ - int fcntl_mode; /* Buffered fcntl(sd,F_GETFL) */ - struct sockaddr_in local; /* Local internet address */ - struct sockaddr_in remote; /* Remote internet address */ - enum enum_vio_type type; /* Type of connection */ - char desc[30]; /* String description */ -}; - -typedef void *vio_ptr; -typedef char *vio_cstring; - -/* - * Helper to fill most of the Vio* with defaults. - */ - -static void vio_reset(Vio* vio, enum enum_vio_type type, - my_socket sd, HANDLE hPipe, - my_bool localhost) -{ - bzero((char*) vio, sizeof(*vio)); - vio->type = type; - vio->sd = sd; - vio->hPipe = hPipe; - vio->localhost= localhost; -} - -/* Open the socket or TCP/IP connection and read the fnctl() status */ - -Vio *vio_new(my_socket sd, enum enum_vio_type type, my_bool localhost) -{ - Vio *vio; - DBUG_ENTER("vio_new"); - DBUG_PRINT("enter", ("sd=%d", sd)); - if ((vio = (Vio*) my_malloc(sizeof(*vio),MYF(MY_WME)))) - { - vio_reset(vio, type, sd, 0, localhost); - sprintf(vio->desc, - (vio->type == VIO_TYPE_SOCKET ? "socket (%d)" : "TCP/IP (%d)"), - vio->sd); -#if !defined(___WIN__) && !defined(__EMX__) && !defined(OS2) -#if !defined(NO_FCNTL_NONBLOCK) - vio->fcntl_mode = fcntl(sd, F_GETFL); -#elif defined(HAVE_SYS_IOCTL_H) /* hpux */ - /* Non blocking sockets doesn't work good on HPUX 11.0 */ - (void) ioctl(sd,FIOSNBIO,0); -#endif -#else /* !defined(__WIN__) && !defined(__EMX__) */ - { - /* set to blocking mode by default */ - ulong arg=0, r; - r = ioctlsocket(vio->sd,FIONBIO,(void*) &arg, sizeof(arg)); - } -#endif - } - DBUG_RETURN(vio); -} - - -#ifdef __WIN__ - -Vio *vio_new_win32pipe(HANDLE hPipe) -{ - Vio *vio; - DBUG_ENTER("vio_new_handle"); - if ((vio = (Vio*) my_malloc(sizeof(Vio),MYF(MY_WME)))) - { - vio_reset(vio, VIO_TYPE_NAMEDPIPE, 0, hPipe, TRUE); - strmov(vio->desc, "named pipe"); - } - DBUG_RETURN(vio); -} - -#endif - -void vio_delete(Vio * vio) -{ - /* It must be safe to delete null pointers. */ - /* This matches the semantics of C++'s delete operator. */ - if (vio) - { - if (vio->type != VIO_CLOSED) - vio_close(vio); - my_free((gptr) vio,MYF(0)); - } -} - -int vio_errno(Vio *vio __attribute__((unused))) -{ - return socket_errno; /* On Win32 this mapped to WSAGetLastError() */ -} - - -int vio_read(Vio * vio, gptr buf, int size) -{ - int r; - DBUG_ENTER("vio_read"); - DBUG_PRINT("enter", ("sd=%d size=%d", vio->sd, size)); -#if defined( __WIN__) || defined(OS2) - if (vio->type == VIO_TYPE_NAMEDPIPE) - { - DWORD length; -#ifdef OS2 - if (!DosRead((HFILE)vio->hPipe, buf, size, &length)) - DBUG_RETURN(-1); -#else - if (!ReadFile(vio->hPipe, buf, size, &length, NULL)) - DBUG_RETURN(-1); -#endif - DBUG_RETURN(length); - } - r = recv(vio->sd, buf, size,0); -#else - errno=0; /* For linux */ - r = read(vio->sd, buf, size); -#endif /* __WIN__ */ -#ifndef DBUG_OFF - if (r < 0) - { - DBUG_PRINT("vio_error", ("Got error %d during read",socket_errno)); - } -#endif /* DBUG_OFF */ - DBUG_PRINT("exit", ("%d", r)); - DBUG_RETURN(r); -} - - -int vio_write(Vio * vio, const gptr buf, int size) -{ - int r; - DBUG_ENTER("vio_write"); - DBUG_PRINT("enter", ("sd=%d size=%d", vio->sd, size)); -#if defined( __WIN__) || defined(OS2) - if ( vio->type == VIO_TYPE_NAMEDPIPE) - { - DWORD length; -#ifdef OS2 - if (!DosWrite((HFILE)vio->hPipe, (char*) buf, size, &length)) - DBUG_RETURN(-1); -#else - if (!WriteFile(vio->hPipe, (char*) buf, size, &length, NULL)) - DBUG_RETURN(-1); -#endif - DBUG_RETURN(length); - } - r = send(vio->sd, buf, size,0); -#else - r = write(vio->sd, buf, size); -#endif /* __WIN__ */ -#ifndef DBUG_OFF - if (r < 0) - { - DBUG_PRINT("vio_error", ("Got error on write: %d",socket_errno)); - } -#endif /* DBUG_OFF */ - DBUG_PRINT("exit", ("%d", r)); - DBUG_RETURN(r); -} - - -int vio_blocking(Vio * vio, my_bool set_blocking_mode) -{ - int r=0; - DBUG_ENTER("vio_blocking"); - DBUG_PRINT("enter", ("set_blocking_mode: %d", (int) set_blocking_mode)); - -#if !defined(___WIN__) && !defined(__EMX__) && !defined(OS2) -#if !defined(NO_FCNTL_NONBLOCK) - - if (vio->sd >= 0) - { - int old_fcntl=vio->fcntl_mode; - if (set_blocking_mode) - vio->fcntl_mode &= ~O_NONBLOCK; /* clear bit */ - else - vio->fcntl_mode |= O_NONBLOCK; /* set bit */ - if (old_fcntl != vio->fcntl_mode) - r = fcntl(vio->sd, F_SETFL, vio->fcntl_mode); - } -#endif /* !defined(NO_FCNTL_NONBLOCK) */ -#else /* !defined(__WIN__) && !defined(__EMX__) */ -#ifndef __EMX__ - if (vio->type != VIO_TYPE_NAMEDPIPE) -#endif - { - ulong arg; - int old_fcntl=vio->fcntl_mode; - if (set_blocking_mode) - { - arg = 0; - vio->fcntl_mode &= ~O_NONBLOCK; /* clear bit */ - } - else - { - arg = 1; - vio->fcntl_mode |= O_NONBLOCK; /* set bit */ - } - if (old_fcntl != vio->fcntl_mode) - r = ioctlsocket(vio->sd,FIONBIO,(void*) &arg, sizeof(arg)); - } -#endif /* !defined(__WIN__) && !defined(__EMX__) */ - DBUG_RETURN(r); -} - -my_bool -vio_is_blocking(Vio * vio) -{ - my_bool r; - DBUG_ENTER("vio_is_blocking"); - r = !(vio->fcntl_mode & O_NONBLOCK); - DBUG_PRINT("exit", ("%d", (int) r)); - DBUG_RETURN(r); -} - - -int vio_fastsend(Vio * vio __attribute__((unused))) -{ - int r=0; - DBUG_ENTER("vio_fastsend"); - -#ifdef IPTOS_THROUGHPUT - { -#ifndef __EMX__ - int tos = IPTOS_THROUGHPUT; - if (!setsockopt(vio->sd, IPPROTO_IP, IP_TOS, (void *) &tos, sizeof(tos))) -#endif /* !__EMX__ */ - { - int nodelay = 1; - if (setsockopt(vio->sd, IPPROTO_TCP, TCP_NODELAY, (void *) &nodelay, - sizeof(nodelay))) { - DBUG_PRINT("warning", - ("Couldn't set socket option for fast send")); - r= -1; - } - } - } -#endif /* IPTOS_THROUGHPUT */ - DBUG_PRINT("exit", ("%d", r)); - DBUG_RETURN(r); -} - -int vio_keepalive(Vio* vio, my_bool set_keep_alive) -{ - int r=0; - uint opt = 0; - DBUG_ENTER("vio_keepalive"); - DBUG_PRINT("enter", ("sd=%d set_keep_alive=%d", vio->sd, (int) - set_keep_alive)); - if (vio->type != VIO_TYPE_NAMEDPIPE) - { - if (set_keep_alive) - opt = 1; - r = setsockopt(vio->sd, SOL_SOCKET, SO_KEEPALIVE, (char *) &opt, - sizeof(opt)); - } - DBUG_RETURN(r); -} - - -my_bool -vio_should_retry(Vio * vio __attribute__((unused))) -{ - int en = socket_errno; - return en == SOCKET_EAGAIN || en == SOCKET_EINTR || en == SOCKET_EWOULDBLOCK; -} - - -int vio_close(Vio * vio) -{ - int r; - DBUG_ENTER("vio_close"); -#ifdef __WIN__ - if (vio->type == VIO_TYPE_NAMEDPIPE) - { -#if defined(__NT__) && defined(MYSQL_SERVER) - CancelIo(vio->hPipe); - DisconnectNamedPipe(vio->hPipe); -#endif - r=CloseHandle(vio->hPipe); - } - else if (vio->type != VIO_CLOSED) -#endif /* __WIN__ */ - { - r=0; - if (shutdown(vio->sd,2)) - r= -1; - if (closesocket(vio->sd)) - r= -1; - } - if (r) - { - DBUG_PRINT("vio_error", ("close() failed, error: %d",socket_errno)); - /* FIXME: error handling (not critical for MySQL) */ - } - vio->type= VIO_CLOSED; - vio->sd= -1; - DBUG_RETURN(r); -} - - -const char *vio_description(Vio * vio) -{ - return vio->desc; -} - -enum enum_vio_type vio_type(Vio* vio) -{ - return vio->type; -} - -my_socket vio_fd(Vio* vio) -{ - return vio->sd; -} - - -my_bool vio_peer_addr(Vio * vio, char *buf) -{ - DBUG_ENTER("vio_peer_addr"); - DBUG_PRINT("enter", ("sd=%d", vio->sd)); - if (vio->localhost) - { - strmov(buf,"127.0.0.1"); - } - else - { - size_socket addrLen = sizeof(struct sockaddr); - if (getpeername(vio->sd, (struct sockaddr *) (& (vio->remote)), - &addrLen) != 0) - { - DBUG_PRINT("exit", ("getpeername, error: %d", socket_errno)); - DBUG_RETURN(1); - } - my_inet_ntoa(vio->remote.sin_addr,buf); - } - DBUG_PRINT("exit", ("addr=%s", buf)); - DBUG_RETURN(0); -} - - -void vio_in_addr(Vio *vio, struct in_addr *in) -{ - DBUG_ENTER("vio_in_addr"); - if (vio->localhost) - bzero((char*) in, sizeof(*in)); /* This should never be executed */ - else - *in=vio->remote.sin_addr; - DBUG_VOID_RETURN; -} - - -/* Return 0 if there is data to be read */ - -my_bool vio_poll_read(Vio *vio,uint timeout) -{ -#ifndef HAVE_POLL - return 0; -#else - struct pollfd fds; - int res; - DBUG_ENTER("vio_poll"); - fds.fd=vio->sd; - fds.events=POLLIN; - fds.revents=0; - if ((res=poll(&fds,1,(int) timeout*1000)) <= 0) - { - DBUG_RETURN(res < 0 ? 0 : 1); /* Don't return 1 on errors */ - } - DBUG_RETURN(fds.revents & POLLIN ? 0 : 1); -#endif -} - -#endif /* HAVE_VIO */ |