summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
Diffstat (limited to 'sql')
-rw-r--r--sql/.cvsignore12
-rw-r--r--sql/Attic/lex_hash.h416
-rw-r--r--sql/Attic/mini_client.c783
-rw-r--r--sql/Attic/mini_client_errors.c64
-rw-r--r--sql/Attic/mybinlogdump.cc138
-rw-r--r--sql/Attic/net_serv.c616
-rw-r--r--sql/ChangeLog3268
-rw-r--r--sql/Makefile.am126
-rwxr-xr-xsql/add_errmsg17
-rw-r--r--sql/cache_manager.cc150
-rw-r--r--sql/cache_manager.h65
-rw-r--r--sql/convert.cc465
-rw-r--r--sql/custom_conf.h28
-rw-r--r--sql/derror.cc146
-rw-r--r--sql/field.cc4576
-rw-r--r--sql/field.h1082
-rw-r--r--sql/field_conv.cc528
-rw-r--r--sql/filesort.cc957
-rw-r--r--sql/frm_crypt.cc37
-rw-r--r--sql/gen_lex_hash.cc560
-rw-r--r--sql/ha_berkeley.cc1398
-rw-r--r--sql/ha_berkeley.h141
-rw-r--r--sql/ha_hash.h31
-rw-r--r--sql/ha_heap.cc261
-rw-r--r--sql/ha_heap.h83
-rw-r--r--sql/ha_isam.cc390
-rw-r--r--sql/ha_isam.h87
-rw-r--r--sql/ha_isammrg.cc210
-rw-r--r--sql/ha_isammrg.h69
-rw-r--r--sql/ha_myisam.cc873
-rw-r--r--sql/ha_myisam.h96
-rw-r--r--sql/ha_myisammrg.cc209
-rw-r--r--sql/ha_myisammrg.h67
-rw-r--r--sql/handler.cc614
-rw-r--r--sql/handler.h307
-rw-r--r--sql/hash_filo.cc28
-rw-r--r--sql/hash_filo.h131
-rw-r--r--sql/hostname.cc235
-rw-r--r--sql/init.cc69
-rw-r--r--sql/item.cc680
-rw-r--r--sql/item.h432
-rw-r--r--sql/item_buff.cc119
-rw-r--r--sql/item_cmpfunc.cc1298
-rw-r--r--sql/item_cmpfunc.h567
-rw-r--r--sql/item_create.cc383
-rw-r--r--sql/item_create.h88
-rw-r--r--sql/item_func.cc1981
-rw-r--r--sql/item_func.h857
-rw-r--r--sql/item_strfunc.cc1719
-rw-r--r--sql/item_strfunc.h429
-rw-r--r--sql/item_sum.cc941
-rw-r--r--sql/item_sum.h464
-rw-r--r--sql/item_timefunc.cc1117
-rw-r--r--sql/item_timefunc.h370
-rwxr-xr-xsql/item_uniq.cc22
-rwxr-xr-xsql/item_uniq.h46
-rw-r--r--sql/key.cc266
-rw-r--r--sql/lex.h446
-rw-r--r--sql/lex_symbol.h37
-rw-r--r--sql/lock.cc386
-rw-r--r--sql/log.cc733
-rw-r--r--sql/log_event.cc685
-rw-r--r--sql/log_event.h354
-rw-r--r--sql/matherr.c43
-rw-r--r--sql/md5.c351
-rw-r--r--sql/md5.h80
-rw-r--r--sql/mf_iocache.cc641
-rw-r--r--sql/mini_client.cc801
-rw-r--r--sql/mini_client.h45
-rw-r--r--sql/my_lock.c82
-rw-r--r--sql/mysql_priv.h594
-rw-r--r--sql/mysqlbinlog.cc378
-rw-r--r--sql/mysqld.cc3433
-rw-r--r--sql/net_pkg.cc329
-rw-r--r--sql/net_serv.cc680
-rw-r--r--sql/nt_servc.cc400
-rw-r--r--sql/nt_servc.h80
-rw-r--r--sql/opt_range.cc2543
-rw-r--r--sql/opt_range.h106
-rw-r--r--sql/opt_sum.cc359
-rw-r--r--sql/password.c191
-rw-r--r--sql/procedure.cc71
-rw-r--r--sql/procedure.h124
-rw-r--r--sql/records.cc335
-rw-r--r--sql/share/.cvsignore2
-rwxr-xr-xsql/share/Makefile.am22
-rw-r--r--sql/share/charsets/Index37
-rw-r--r--sql/share/charsets/README43
-rw-r--r--sql/share/charsets/cp1251.conf74
-rw-r--r--sql/share/charsets/cp1257.conf74
-rw-r--r--sql/share/charsets/croat.conf74
-rw-r--r--sql/share/charsets/danish.conf74
-rw-r--r--sql/share/charsets/dec8.conf74
-rw-r--r--sql/share/charsets/dos.conf74
-rw-r--r--sql/share/charsets/estonia.conf74
-rw-r--r--sql/share/charsets/german1.conf74
-rw-r--r--sql/share/charsets/greek.conf74
-rw-r--r--sql/share/charsets/hebrew.conf74
-rw-r--r--sql/share/charsets/hp8.conf74
-rw-r--r--sql/share/charsets/hungarian.conf74
-rw-r--r--sql/share/charsets/koi8_ru.conf74
-rw-r--r--sql/share/charsets/koi8_ukr.conf74
-rw-r--r--sql/share/charsets/latin1.conf74
-rw-r--r--sql/share/charsets/latin2.conf74
-rw-r--r--sql/share/charsets/swe7.conf74
-rw-r--r--sql/share/charsets/usa7.conf74
-rw-r--r--sql/share/charsets/win1250.conf74
-rw-r--r--sql/share/charsets/win1251.conf74
-rw-r--r--sql/share/charsets/win1251ukr.conf74
-rw-r--r--sql/share/czech/errmsg.sysbin0 -> 9707 bytes
-rw-r--r--sql/share/czech/errmsg.txt209
-rw-r--r--sql/share/danish/errmsg.sysbin0 -> 10135 bytes
-rw-r--r--sql/share/danish/errmsg.txt198
-rw-r--r--sql/share/dutch/errmsg.sysbin0 -> 10351 bytes
-rw-r--r--sql/share/dutch/errmsg.txt195
-rw-r--r--sql/share/english/errmsg.sysbin0 -> 9516 bytes
-rw-r--r--sql/share/english/errmsg.txt195
-rw-r--r--sql/share/estonia/errmsg.sysbin0 -> 9492 bytes
-rw-r--r--sql/share/estonia/errmsg.txt199
-rw-r--r--sql/share/french/errmsg.sysbin0 -> 9957 bytes
-rw-r--r--sql/share/french/errmsg.txt195
-rw-r--r--sql/share/german/errmsg.sysbin0 -> 10736 bytes
-rw-r--r--sql/share/german/errmsg.txt198
-rw-r--r--sql/share/greek/errmsg.sysbin0 -> 11563 bytes
-rw-r--r--sql/share/greek/errmsg.txt195
-rw-r--r--sql/share/hungarian/errmsg.sysbin0 -> 10472 bytes
-rw-r--r--sql/share/hungarian/errmsg.txt197
-rw-r--r--sql/share/italian/errmsg.sysbin0 -> 11081 bytes
-rw-r--r--sql/share/italian/errmsg.txt195
-rw-r--r--sql/share/japanese/errmsg.sysbin0 -> 10263 bytes
-rw-r--r--sql/share/japanese/errmsg.txt197
-rw-r--r--sql/share/korean/errmsg.sysbin0 -> 10393 bytes
-rw-r--r--sql/share/korean/errmsg.txt195
-rwxr-xr-xsql/share/norwegian-ny/.cvsignore1
-rw-r--r--sql/share/norwegian-ny/errmsg.txt197
-rwxr-xr-xsql/share/norwegian/.cvsignore1
-rw-r--r--sql/share/norwegian/errmsg.txt197
-rw-r--r--sql/share/polish/errmsg.sysbin0 -> 10151 bytes
-rw-r--r--sql/share/polish/errmsg.txt199
-rw-r--r--sql/share/portuguese/errmsg.sysbin0 -> 10076 bytes
-rw-r--r--sql/share/portuguese/errmsg.txt195
-rw-r--r--sql/share/romania/errmsg.sysbin0 -> 11663 bytes
-rw-r--r--sql/share/romania/errmsg.txt199
-rw-r--r--sql/share/russian/errmsg.sysbin0 -> 9970 bytes
-rw-r--r--sql/share/russian/errmsg.txt198
-rw-r--r--sql/share/slovak/errmsg.sysbin0 -> 9736 bytes
-rw-r--r--sql/share/slovak/errmsg.txt203
-rw-r--r--sql/share/spanish/errmsg.sysbin0 -> 10092 bytes
-rw-r--r--sql/share/spanish/errmsg.txt196
-rw-r--r--sql/share/swedish/errmsg.OLD195
-rw-r--r--sql/share/swedish/errmsg.sysbin0 -> 9840 bytes
-rw-r--r--sql/share/swedish/errmsg.txt195
-rw-r--r--sql/slave.cc945
-rw-r--r--sql/sql_acl.cc2547
-rw-r--r--sql/sql_acl.h84
-rw-r--r--sql/sql_analyse.cc981
-rw-r--r--sql/sql_analyse.h292
-rw-r--r--sql/sql_base.cc2174
-rw-r--r--sql/sql_cache.cc98
-rw-r--r--sql/sql_class.cc593
-rw-r--r--sql/sql_class.h472
-rw-r--r--sql/sql_crypt.cc82
-rw-r--r--sql/sql_crypt.h38
-rw-r--r--sql/sql_db.cc300
-rw-r--r--sql/sql_delete.cc213
-rw-r--r--sql/sql_insert.cc1346
-rw-r--r--sql/sql_lex.cc810
-rw-r--r--sql/sql_lex.h149
-rw-r--r--sql/sql_list.cc22
-rw-r--r--sql/sql_list.h310
-rw-r--r--sql/sql_load.cc790
-rw-r--r--sql/sql_map.cc147
-rw-r--r--sql/sql_map.h59
-rw-r--r--sql/sql_parse.cc2627
-rw-r--r--sql/sql_select.cc6408
-rw-r--r--sql/sql_select.h291
-rw-r--r--sql/sql_show.cc1032
-rw-r--r--sql/sql_string.cc741
-rw-r--r--sql/sql_string.h183
-rw-r--r--sql/sql_table.cc1559
-rw-r--r--sql/sql_test.cc246
-rw-r--r--sql/sql_udf.cc471
-rw-r--r--sql/sql_udf.h144
-rw-r--r--sql/sql_update.cc266
-rw-r--r--sql/sql_yacc.yy2767
-rw-r--r--sql/structs.h160
-rw-r--r--sql/table.cc1121
-rw-r--r--sql/table.h136
-rw-r--r--sql/thr_malloc.cc88
-rw-r--r--sql/time.cc690
-rw-r--r--sql/udf_example.cc868
-rw-r--r--sql/unireg.cc581
-rw-r--r--sql/unireg.h136
-rw-r--r--sql/violite.c399
-rwxr-xr-xsql/watchdog_mysqld126
195 files changed, 84913 insertions, 0 deletions
diff --git a/sql/.cvsignore b/sql/.cvsignore
new file mode 100644
index 00000000000..3e2f44f5a10
--- /dev/null
+++ b/sql/.cvsignore
@@ -0,0 +1,12 @@
+.deps
+.libs
+Makefile
+Makefile.in
+deadlock_test.c
+gen_lex_hash
+lex_hash.h
+mysqlbinlog
+mysqlbinlog
+mysqld
+sql_yacc.cc
+sql_yacc.h
diff --git a/sql/Attic/lex_hash.h b/sql/Attic/lex_hash.h
new file mode 100644
index 00000000000..6e89119a3dc
--- /dev/null
+++ b/sql/Attic/lex_hash.h
@@ -0,0 +1,416 @@
+/* This code is generated by program for seeking hash algorithms, copyright TcX Datakonsult AB */
+
+#include "lex.h"
+
+static uint16 char_table[] = {
+258,39835,4883,21845,23130,46003,12850,35980,514,65278,61937,7196,40863,26728,26985,9766,
+5654,23644,21331,17733,3341,25186,30069,1542,31354,39321,4626,52171,46260,57568,21588,14906,
+60652,20560,6939,39578,52428,10794,46517,40606,58596,17990,58339,24672,42148,60138,3598,5911,
+43947,37522,36494,1799,36751,30840,57311,34952,56540,27499,45232,41891,61423,38550,29812,24158,
+771,37779,57054,257,8224,64250,51657,16962,13878,20303,42662,6168,19275,20817,34438,44461,
+12336,48059,49087,28013,3855,16191,32896,26214,57825,38807,29298,35723,51400,12079,6682,25957,
+8738,37779,57054,257,8224,64250,51657,16962,13878,20303,42662,6168,19275,20817,34438,44461,
+12336,48059,49087,28013,3855,16191,32896,26214,57825,38807,29298,32639,11051,43690,2313,33924,
+16705,53970,4112,24415,30583,17476,45746,7967,34695,35466,25700,6425,29555,59881,10537,37008,
+7710,2827,62965,11565,26471,43433,34181,30326,13364,29041,54998,47802,52685,63993,2056,51914,
+31097,44718,48830,50372,23387,62194,19789,19532,53456,62451,16448,54741,63736,18761,10023,3084,
+20046,31868,50629,15420,54484,42405,47288,31611,28270,47031,61680,65535,49344,23901,42919,48316,
+10280,55512,60395,51143,9252,53199,40349,56797,13621,11308,49601,36237,18247,15934,18504,46774,
+43176,11822,56283,28784,59624,39064,63479,19018,58853,65021,9509,8995,58082,5140,5397,15163,
+27242,22102,28527,22873,59367,47545,13107,60909,41120,24929,22616,49858,38036,17219,4369,64507,
+50115,62708,7453,41377,53713,33153,32382,59110,64764,50886,33410,8481,63222,41634,12593,56026
+};
+
+
+static uchar unique_length[] = {
+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,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,3,1,2,0,
+0,5,5,13,9,7,6,6,6,7,1,4,5,7,5,7,
+8,3,12,10,6,7,4,5,0,5,1,0,0,0,0,0,
+0,5,5,13,9,7,6,6,6,7,1,4,5,7,5,7,
+8,3,12,10,6,7,4,5,0,5,1,0,1,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,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,0,0,0,0,0
+};
+
+
+static uint16 my_function_table[] = {
+32767,32767,32767,32767,32767,32767,32767,32767,102,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,282,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,297,32767,32767,202,382,126,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,169,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,306,152,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,354,32767,32767,32767,32767,32767,370,256,32767,
+32767,32767,32767,32767,32767,32767,355,32767,32767,32767,32767,361,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,313,32767,32767,32767,32767,32767,32767,
+14,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,315,
+32767,32767,32767,32767,32767,32767,32767,32767,42,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,189,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,27,32767,32767,32767,32767,32767,32767,32767,374,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,166,32767,32767,32767,32767,32767,32767,349,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+46,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,353,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,223,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+216,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,143,32767,32767,32767,32767,32767,32767,32767,
+32767,20,32767,32767,32767,140,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,125,32767,32767,32767,
+32767,224,32767,32767,32767,32767,267,32767,175,32767,32767,32767,
+246,32767,32767,32767,32767,32767,32767,109,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,324,32767,
+32767,32767,32767,32767,161,32767,21,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,11,32767,32767,32767,32767,32767,32767,32767,
+194,32767,32767,32767,32767,32767,32767,32767,32767,332,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,364,342,32767,
+32767,32767,32767,32767,32767,193,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,373,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,212,32767,32767,32767,32,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,54,
+67,32767,32767,32767,32767,32767,32767,32767,32767,287,32767,32767,
+32767,32767,32767,51,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,273,219,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,261,32767,32767,32767,32767,
+32767,123,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,198,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,233,32767,32767,32767,32767,32767,32767,32767,338,32767,32767,
+32767,32767,32767,32767,32767,236,32767,32767,32767,310,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,232,32767,32767,32767,32767,135,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,65,32767,32767,150,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,2,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,378,32767,32767,32767,32767,32767,107,32767,32767,32767,32767,
+32767,32767,32767,335,32767,358,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,188,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+254,32767,32767,146,32767,32767,32767,32767,32767,153,33,32767,
+32767,32767,32767,32767,32767,32767,242,32767,32767,32767,32767,32767,
+32767,32767,32767,226,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,167,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,104,32767,32767,
+32767,32767,32767,32767,32767,195,37,32767,32767,253,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,88,32767,32767,32767,32767,154,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,300,32767,32767,32767,330,314,326,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,259,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,134,32767,32767,32767,32767,341,
+32767,32767,32767,32767,32767,32767,281,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,316,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,22,32767,32767,32767,32767,32767,
+32767,32767,268,32767,32767,144,32767,32767,32767,32767,32767,32767,
+32767,239,165,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,200,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,351,87,32767,32767,255,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,180,32767,32767,32767,32767,32767,10,24,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,376,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,230,32767,32767,32767,32767,32767,303,44,217,
+32767,32767,32767,32767,32767,32767,32767,32767,317,32767,32767,32767,
+266,32767,58,32767,84,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,181,32767,381,32767,296,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,40,32767,32767,32767,32767,32767,133,196,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,263,32767,
+32767,32767,32767,32767,32767,182,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,28,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,307,32767,32767,
+32767,32767,3,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+174,298,32767,32767,32767,32767,362,32767,32767,32767,151,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,99,32767,112,
+32767,32767,15,32767,32767,32767,32767,32767,32767,32767,32767,129,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,76,32767,32767,32767,32767,93,32767,337,32767,32767,
+356,32767,32767,32767,328,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,91,32767,250,32767,32767,32767,32767,
+32767,32767,32767,32767,205,32767,32767,32767,32767,32767,32767,32767,
+32767,368,32767,1,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,38,32767,203,32767,176,32767,32767,32767,32767,
+32767,32767,208,32767,32767,32767,32767,32767,32767,32767,320,32767,
+156,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,277,32767,32767,32767,32767,32767,32767,
+32767,30,32767,32767,344,32767,32767,32767,32767,383,32767,32767,
+155,32767,32767,119,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,211,43,32767,32767,32767,32767,32767,141,32767,158,32767,
+32767,32767,32767,32767,283,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,280,32767,120,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,35,32767,32767,32767,32767,32767,347,32767,32767,
+32767,32767,318,32767,32767,32767,235,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,262,32767,32767,32767,32767,45,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,305,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,278,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,238,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,7,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,122,32767,19,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,288,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,206,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,139,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,56,32767,32767,32767,32767,106,32767,77,32767,32767,32767,
+32767,101,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,379,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,4,32767,32767,62,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,201,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,163,32767,32767,
+98,32767,32767,32767,32767,32767,32767,162,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,192,82,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,340,32767,32767,32767,145,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,184,32767,32767,32767,32767,32767,32767,173,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,301,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,149,32767,32767,32767,32767,32767,32767,32767,32767,279,32767,
+142,32767,32767,294,32767,32767,32767,32767,32767,32767,32767,333,
+32767,32767,32767,32767,32767,117,32767,32767,69,32767,32767,96,
+32767,32767,32767,286,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,73,32767,32767,32767,32767,
+32767,103,32767,164,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+92,32767,32767,32767,32767,32767,221,272,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,25,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,311,
+225,32767,207,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,295,32767,108,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,290,384,32767,32767,32767,32767,
+32767,32767,32767,32767,80,32767,346,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,366,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,128,32767,32767,
+32767,32767,32767,83,243,32767,32767,32767,270,32767,32767,118,
+32767,32767,249,32767,32767,241,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,229,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,86,32767,32767,260,50,32767,32767,227,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,23,32767,32767,32767,32767,32767,32767,309,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+371,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,114,32767,32767,32767,32767,32767,32767,
+365,13,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,352,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,178,289,32767,32767,32767,32767,32767,32767,
+32767,32767,81,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,60,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,105,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+168,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,350,32767,32767,32767,32767,
+276,32767,32767,32767,32767,32767,32767,32767,199,59,32767,32767,
+32767,32767,32767,32767,32767,322,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,339,32767,32767,32767,32767,
+32767,32767,32767,94,32767,32767,228,32767,32767,32767,32767,293,
+32767,32767,32767,55,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,244,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,66,32767,32767,32767,32767,258,357,32767,32767,32767,
+32767,8,32767,32767,32767,12,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,124,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+111,32767,32767,32767,32767,32767,63,32767,32767,32767,95,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,329,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,159,32767,32767,32767,32767,
+215,32767,32767,274,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,85,32767,6,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,312,113,32767,32767,32767,78,
+32767,323,18,32767,32767,32767,32767,32767,32767,369,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,90,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+325,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+210,32767,32767,32767,32767,32767,9,32767,32767,121,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,319,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,220,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,39,32767,32767,
+271,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,234,
+32767,32767,32767,367,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,363,0,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+204,177,32767,32767,32767,32767,32767,32767,32767,32767,148,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,284,252,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,222,32767,75,32767,32767,32767,32767,292,
+32767,32767,32767,32767,32767,32767,172,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,360,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,138,32767,32767,380,32767,359,32767,
+32767,32767,32767,32767,32767,79,32767,32767,32767,32767,248,32767,
+32767,32767,32767,32767,32767,32767,147,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,327,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,299,32767,32767,32767,32767,72,32767,32767,32767,
+32767,240,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+343,32767,32767,32767,32767,32767,137,32767,32767,32767,32767,32767,
+32767,32767,32767,130,32767,32767,32767,32767,32767,32767,32767,131,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,57,32767,5,32767,32767,32767,
+32767,32767,32767,291,32767,32767,32767,32767,32767,213,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,375,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,68,
+32767,32767,32767,32767,64,32767,32767,32767,32767,32767,32767,171,
+32767,32767,32767,49,32767,32767,32767,32767,32767,48,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,275,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,345,331,32767,32767,32767,32767,32767,157,32767,32767,32767,
+32767,32767,32767,32767,32767,190,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,74,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+334,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,179,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,264,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,53,32767,32767,
+32767,32767,32767,32767,209,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,245,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,61,32767,
+52,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,186,32767,41,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+70,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,247,32767,32767,32767,32767,136,36,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,31,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,336,32767,32767,32767,265,191,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,127,32767,32767,32767,32767,32767,32767,
+321,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,116,32767,32767,32767,32767,32767,32767,26,32767,32767,
+32767,32767,32767,32767,32767,32767,269,32767,32767,32767,32767,32767,
+100,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,183,34,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,372,32767,32767,32767,32767,32767,
+308,32767,32767,285,32767,32767,32767,32767,377,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,257,32767,32767,32767,32767,32767,97,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,89,32767,32767,302,160,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,170,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,110,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,16,32767,214,71,32767,32767,32767,
+32767,32767,32767,197,32767,32767,32767,32767,32767,32767,32767,17,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+237,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,187,32767,218,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,304,32767,32767,32767,32767,32767,32767,32767,32767,185,32767,
+348,32767,32767,32767,231,32767,32767,32767,32767,32767,251,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,32767,
+32767,132,32767,32767,32767,32767,32767,32767,32767,115,32767,32767,
+32767,29,32767,32767,32767,32767,32767,32767,32767,32767,32767,47,
+32767,32767,32767,32767,32767
+};
+
+
+/* t1= 45205 t2=3702303 type= 0 */
+
+inline SYMBOL *get_hash_symbol(const char *s,unsigned int length,bool function)
+{
+ ulong idx = 2003+char_table[(uchar) *s];
+ SYMBOL *sim;
+ const char *start=s;
+ int i=unique_length[(uchar) *s++];
+ if (i > (int) length) i=(int) length;
+ while (--i > 0)
+ idx= (idx ^ (char_table[(uchar) *s++] + (idx << 7)));
+ idx=my_function_table[(idx & 8388607) % 4133];
+ if (idx >= 262)
+ {
+ if (!function || idx >= 32767) return (SYMBOL*) 0;
+ sim=sql_functions + (idx - 262);
+ }
+ else
+ sim=symbols + idx;
+ if ((length != sim->length) || lex_casecmp(start,sim->name,length))
+ return (SYMBOL *)0;
+ return sim;
+}
diff --git a/sql/Attic/mini_client.c b/sql/Attic/mini_client.c
new file mode 100644
index 00000000000..56a6de14742
--- /dev/null
+++ b/sql/Attic/mini_client.c
@@ -0,0 +1,783 @@
+/* Copyright Abandoned 1996 TCX DataKonsult AB & Monty Program KB & Detron HB
+ This file is public domain and comes with NO WARRANTY of any kind */
+
+/*
+ mini MySQL client to be included into the server to do server to server
+ commincation by Sasha Pachev
+
+ 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(__WIN32__) || defined(WIN32)
+#include <winsock.h>
+#include <odbcinst.h>
+#endif
+#include <global.h>
+#include <my_sys.h>
+#include <mysys_err.h>
+#include <m_string.h>
+#include <m_ctype.h>
+#include "mysql.h"
+#include "mini_client.h"
+#include "mysql_version.h"
+#include "mysqld_error.h"
+#include "errmsg.h"
+#include <violite.h>
+#include <sys/stat.h>
+#include <signal.h>
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+#if !defined(MSDOS) && !defined(__WIN32__)
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#ifdef HAVE_SELECT_H
+# include <select.h>
+#endif
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#endif
+#ifdef HAVE_SYS_UN_H
+# include <sys/un.h>
+#endif
+#if defined(THREAD) && !defined(__WIN32__)
+#include <my_pthread.h> /* because of signal() */
+#endif
+#ifndef INADDR_NONE
+#define INADDR_NONE -1
+#endif
+
+
+static 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);
+
+
+#define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | CLIENT_LOCAL_FILES)
+
+#if defined(MSDOS) || defined(__WIN32__)
+#define ERRNO WSAGetLastError()
+#define perror(A)
+#else
+#include <sys/errno.h>
+#define ERRNO errno
+#define SOCKET_ERROR -1
+#define closesocket(A) close(A)
+#endif
+
+#ifdef __WIN32__
+static my_bool is_NT(void)
+{
+ char *os=getenv("OS");
+ return (os && !strcmp(os, "Windows_NT")) ? 1 : 0;
+}
+#endif
+
+/*
+** Create a named pipe connection
+*/
+
+#ifdef __WIN32__
+
+HANDLE create_named_pipe(NET *net, uint connect_timeout, char **arg_host,
+ char **arg_unix_socket)
+{
+ HANDLE hPipe=INVALID_HANDLE_VALUE;
+ char szPipeName [ 257 ];
+ DWORD dwMode;
+ int i;
+ my_bool testing_named_pipes=0;
+ char *host= *arg_host, *unix_socket= *arg_unix_socket;
+
+ if (!host || !strcmp(host,LOCAL_HOST))
+ host=LOCAL_HOST_NAMEDPIPE;
+
+ sprintf( szPipeName, "\\\\%s\\pipe\\%s", host, unix_socket);
+ DBUG_PRINT("info",("Server name: '%s'. Named Pipe: %s",
+ host, unix_socket));
+
+ for (i=0 ; i < 100 ; i++) /* Don't retry forever */
+ {
+ if ((hPipe = CreateFile(szPipeName,
+ GENERIC_READ | GENERIC_WRITE,
+ 0,
+ NULL,
+ OPEN_EXISTING,
+ 0,
+ NULL )) != INVALID_HANDLE_VALUE)
+ break;
+ if (GetLastError() != ERROR_PIPE_BUSY)
+ {
+ net->last_errno=CR_NAMEDPIPEOPEN_ERROR;
+ sprintf(net->last_error,ER(net->last_errno),host, unix_socket,
+ (ulong) GetLastError());
+ return INVALID_HANDLE_VALUE;
+ }
+ /* wait for for an other instance */
+ if (! WaitNamedPipe(szPipeName, connect_timeout*1000) )
+ {
+ net->last_errno=CR_NAMEDPIPEWAIT_ERROR;
+ sprintf(net->last_error,ER(net->last_errno),host, unix_socket,
+ (ulong) GetLastError());
+ return INVALID_HANDLE_VALUE;
+ }
+ }
+ if (hPipe == INVALID_HANDLE_VALUE)
+ {
+ net->last_errno=CR_NAMEDPIPEOPEN_ERROR;
+ sprintf(net->last_error,ER(net->last_errno),host, unix_socket,
+ (ulong) GetLastError());
+ return INVALID_HANDLE_VALUE;
+ }
+ dwMode = PIPE_READMODE_BYTE | PIPE_WAIT;
+ if ( !SetNamedPipeHandleState(hPipe, &dwMode, NULL, NULL) )
+ {
+ CloseHandle( hPipe );
+ net->last_errno=CR_NAMEDPIPESETSTATE_ERROR;
+ sprintf(net->last_error,ER(net->last_errno),host, unix_socket,
+ (ulong) GetLastError());
+ return INVALID_HANDLE_VALUE;
+ }
+ *arg_host=host ; *arg_unix_socket=unix_socket; /* connect arg */
+ return (hPipe);
+}
+#endif
+
+
+/****************************************************************************
+** Init MySQL structure or allocate one
+****************************************************************************/
+
+MYSQL * STDCALL
+mc_mysql_init(MYSQL *mysql)
+{
+ if (!mysql)
+ {
+ if (!(mysql=(MYSQL*) my_malloc(sizeof(*mysql),MYF(MY_WME | MY_ZEROFILL))))
+ return 0;
+ mysql->free_me=1;
+ mysql->net.vio = 0;
+ }
+ else
+ bzero((char*) (mysql),sizeof(*(mysql)));
+#ifdef __WIN32__
+ mysql->options.connect_timeout=20;
+#endif
+ return mysql;
+}
+
+/**************************************************************************
+** Shut down connection
+**************************************************************************/
+
+static void
+mc_end_server(MYSQL *mysql)
+{
+ DBUG_ENTER("mc_end_server");
+ if (mysql->net.vio != 0)
+ {
+ DBUG_PRINT("info",("Net: %s", vio_description(mysql->net.vio)));
+ vio_delete(mysql->net.vio);
+ mysql->net.vio= 0; /* Marker */
+ }
+ net_end(&mysql->net);
+ mc_free_old_query(mysql);
+ DBUG_VOID_RETURN;
+}
+
+static void mc_free_old_query(MYSQL *mysql)
+{
+ DBUG_ENTER("mc_free_old_query");
+ if (mysql->fields)
+ free_root(&mysql->field_alloc);
+ init_alloc_root(&mysql->field_alloc,8192); /* Assume rowlength < 8192 */
+ mysql->fields=0;
+ mysql->field_count=0; /* For API */
+ DBUG_VOID_RETURN;
+}
+
+
+/****************************************************************************
+* A modified version of connect(). mc_sock_connect() allows you to specify
+* a timeout value, in seconds, that we should wait until we
+* derermine we can't connect to a particular host. If timeout is 0,
+* mc_sock_connect() will behave exactly like connect().
+*
+* Base version coded by Steve Bernacki, Jr. <steve@navinet.net>
+*****************************************************************************/
+
+static int mc_sock_connect(File s, const struct sockaddr *name, uint namelen, uint to)
+{
+#if defined(__WIN32__)
+ return connect(s, (struct sockaddr*) name, namelen);
+#else
+ int flags, res, s_err;
+ size_socket s_err_size = sizeof(uint);
+ fd_set sfds;
+ struct timeval tv;
+
+ /* If they passed us a timeout of zero, we should behave
+ * exactly like the normal connect() call does.
+ */
+
+ if (to == 0)
+ return connect(s, (struct sockaddr*) name, namelen);
+
+ flags = fcntl(s, F_GETFL, 0); /* Set socket to not block */
+#ifdef O_NONBLOCK
+ fcntl(s, F_SETFL, flags | O_NONBLOCK); /* and save the flags.. */
+#endif
+
+ res = connect(s, (struct sockaddr*) name, namelen);
+ s_err = errno; /* Save the error... */
+ fcntl(s, F_SETFL, flags);
+ if ((res != 0) && (s_err != EINPROGRESS))
+ {
+ errno = s_err; /* Restore it */
+ return(-1);
+ }
+ if (res == 0) /* Connected quickly! */
+ return(0);
+
+ /* Otherwise, our connection is "in progress." We can use
+ * the select() call to wait up to a specified period of time
+ * for the connection to suceed. If select() returns 0
+ * (after waiting howevermany seconds), our socket never became
+ * writable (host is probably unreachable.) Otherwise, if
+ * select() returns 1, then one of two conditions exist:
+ *
+ * 1. An error occured. We use getsockopt() to check for this.
+ * 2. The connection was set up sucessfully: getsockopt() will
+ * return 0 as an error.
+ *
+ * Thanks goes to Andrew Gierth <andrew@erlenstar.demon.co.uk>
+ * who posted this method of timing out a connect() in
+ * comp.unix.programmer on August 15th, 1997.
+ */
+
+ FD_ZERO(&sfds);
+ FD_SET(s, &sfds);
+ tv.tv_sec = (long) to;
+ tv.tv_usec = 0;
+ res = select(s+1, NULL, &sfds, NULL, &tv);
+ if (res <= 0) /* Never became writable */
+ return(-1);
+
+ /* select() returned something more interesting than zero, let's
+ * see if we have any errors. If the next two statements pass,
+ * we've got an open socket!
+ */
+
+ s_err=0;
+ if (getsockopt(s, SOL_SOCKET, SO_ERROR, (char*) &s_err, &s_err_size) != 0)
+ return(-1);
+
+ if (s_err)
+ { /* getsockopt() could suceed */
+ errno = s_err;
+ return(-1); /* but return an error... */
+ }
+ return(0); /* It's all good! */
+#endif
+}
+
+/*****************************************************************************
+** read a packet from server. Give error message if socket was down
+** or packet is an error message
+*****************************************************************************/
+
+uint STDCALL
+mc_net_safe_read(MYSQL *mysql)
+{
+ NET *net= &mysql->net;
+ uint len=0;
+
+ if (net->vio != 0)
+ len=my_net_read(net);
+
+ if (len == packet_error || len == 0)
+ {
+ DBUG_PRINT("error",("Wrong connection or packet. fd: %s len: %d",
+ vio_description(net->vio),len));
+ if(errno != EINTR)
+ {
+ mc_end_server(mysql);
+ net->last_errno=CR_SERVER_LOST;
+ strmov(net->last_error,ER(net->last_errno));
+ }
+ return(packet_error);
+ }
+ if (net->read_pos[0] == 255)
+ {
+ if (len > 3)
+ {
+ char *pos=(char*) net->read_pos+1;
+ if (mysql->protocol_version > 9)
+ { /* New client protocol */
+ net->last_errno=uint2korr(pos);
+ pos+=2;
+ len-=2;
+ if(!net->last_errno)
+ net->last_errno = CR_UNKNOWN_ERROR;
+ }
+ else
+ {
+ net->last_errno=CR_UNKNOWN_ERROR;
+ len--;
+ }
+ (void) strmake(net->last_error,(char*) pos,
+ min(len,sizeof(net->last_error)-1));
+ }
+ else
+ {
+ net->last_errno=CR_UNKNOWN_ERROR;
+ (void) strmov(net->last_error,ER(net->last_errno));
+ }
+ DBUG_PRINT("error",("Got error: %d (%s)", net->last_errno,
+ net->last_error));
+ return(packet_error);
+ }
+ return len;
+}
+
+
+char * STDCALL mc_mysql_error(MYSQL *mysql)
+{
+ return (mysql)->net.last_error;
+}
+
+my_bool STDCALL mc_mysql_reconnect(MYSQL *mysql)
+{
+ MYSQL tmp_mysql;
+ DBUG_ENTER("mc_mysql_reconnect");
+
+ 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))
+ DBUG_RETURN(1);
+ tmp_mysql.free_me=mysql->free_me;
+ mysql->free_me=0;
+ bzero((char*) &mysql->options,sizeof(&mysql->options));
+ mc_mysql_close(mysql);
+ *mysql=tmp_mysql;
+ net_clear(&mysql->net);
+ mysql->affected_rows= ~(my_ulonglong) 0;
+ DBUG_RETURN(0);
+}
+
+
+
+int STDCALL
+mc_simple_command(MYSQL *mysql,enum enum_server_command command, const char *arg,
+ uint length, my_bool skipp_check)
+{
+ NET *net= &mysql->net;
+ int result= -1;
+
+ 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)
+ {
+ strmov(net->last_error,ER(mysql->net.last_errno=CR_COMMANDS_OUT_OF_SYNC));
+ goto end;
+ }
+
+ mysql->net.last_error[0]=0;
+ mysql->net.last_errno=0;
+ mysql->info=0;
+ mysql->affected_rows= ~(my_ulonglong) 0;
+ net_clear(net); /* Clear receive buffer */
+ if (!arg)
+ arg="";
+
+ if (net_write_command(net,(uchar) command,arg,
+ length ? length :strlen(arg)))
+ {
+ DBUG_PRINT("error",("Can't send command to server. Error: %d",errno));
+ mc_end_server(mysql);
+ if (mc_mysql_reconnect(mysql) ||
+ net_write_command(net,(uchar) command,arg,
+ length ? length :strlen(arg)))
+ {
+ net->last_errno=CR_SERVER_GONE_ERROR;
+ strmov(net->last_error,ER(net->last_errno));
+ goto end;
+ }
+ }
+ result=0;
+ if (!skipp_check)
+ result= ((mysql->packet_length=mc_net_safe_read(mysql)) == packet_error ?
+ -1 : 0);
+ end:
+ return result;
+}
+
+
+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)
+{
+ char buff[100],*end,*host_info;
+ int sock;
+ ulong ip_addr;
+ struct sockaddr_in sock_addr;
+ uint pkt_length;
+ NET *net= &mysql->net;
+#ifdef __WIN32__
+ HANDLE hPipe=INVALID_HANDLE_VALUE;
+#endif
+#ifdef HAVE_SYS_UN_H
+ struct sockaddr_un UNIXaddr;
+#endif
+ DBUG_ENTER("mysql_real_connect");
+
+ DBUG_PRINT("enter",("host: %s db: %s user: %s",
+ host ? host : "(Null)",
+ db ? db : "(Null)",
+ user ? user : "(Null)"));
+
+ bzero((char*) &mysql->options,sizeof(mysql->options));
+ net->vio = 0; /* If something goes wrong */
+ mysql->charset=default_charset_info; /* Set character set */
+ if (!port)
+ port = MYSQL_PORT; /* Should always be set by mysqld */
+ if (!unix_socket)
+ unix_socket=MYSQL_UNIX_ADDR;
+
+ mysql->reconnect=1; /* Reconnect as default */
+
+ /*
+ ** Grab a socket and connect it to the server
+ */
+
+#if defined(HAVE_SYS_UN_H)
+ if (!host || !strcmp(host,LOCAL_HOST))
+ {
+ host=LOCAL_HOST;
+ host_info=(char*) ER(CR_LOCALHOST_CONNECTION);
+ DBUG_PRINT("info",("Using UNIX sock '%s'",unix_socket));
+ if ((sock = socket(AF_UNIX,SOCK_STREAM,0)) == SOCKET_ERROR)
+ {
+ net->last_errno=CR_SOCKET_CREATE_ERROR;
+ sprintf(net->last_error,ER(net->last_errno),ERRNO);
+ goto error;
+ }
+ net->vio = vio_new(sock, VIO_TYPE_SOCKET, TRUE);
+ 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),
+ mysql->options.connect_timeout) <0)
+ {
+ DBUG_PRINT("error",("Got error %d on connect to local server",ERRNO));
+ net->last_errno=CR_CONNECTION_ERROR;
+ sprintf(net->last_error,ER(net->last_errno),unix_socket,ERRNO);
+ goto error;
+ }
+ }
+ else
+#elif defined(__WIN32__)
+ {
+ if ((unix_socket ||
+ !host && is_NT() ||
+ host && !strcmp(host,LOCAL_HOST_NAMEDPIPE) ||
+ mysql->options.named_pipe || !have_tcpip))
+ {
+ sock=0;
+ if ((hPipe=create_named_pipe(net, mysql->options.connect_timeout,
+ (char**) &host, (char**) &unix_socket)) ==
+ INVALID_HANDLE_VALUE)
+ {
+ DBUG_PRINT("error",
+ ("host: '%s' socket: '%s' named_pipe: %d have_tcpip: %d",
+ host ? host : "<null>",
+ unix_socket ? unix_socket : "<null>",
+ (int) mysql->options.named_pipe,
+ (int) have_tcpip));
+ if (mysql->options.named_pipe ||
+ (host && !strcmp(host,LOCAL_HOST_NAMEDPIPE)) ||
+ (unix_socket && !strcmp(unix_socket,MYSQL_NAMEDPIPE)))
+ goto error; /* User only requested named pipes */
+ /* Try also with TCP/IP */
+ }
+ else
+ {
+ net->vio=vio_new_win32pipe(hPipe);
+ sprintf(host_info=buff, ER(CR_NAMEDPIPE_CONNECTION), host,
+ unix_socket);
+ }
+ }
+ }
+ if (hPipe == INVALID_HANDLE_VALUE)
+#endif
+ {
+ unix_socket=0; /* This is not used */
+ if (!host)
+ 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)
+ {
+ net->last_errno=CR_IPSOCK_ERROR;
+ sprintf(net->last_error,ER(net->last_errno),ERRNO);
+ goto error;
+ }
+ net->vio = vio_new(sock,VIO_TYPE_TCPIP,FALSE);
+ bzero((char*) &sock_addr,sizeof(sock_addr));
+ sock_addr.sin_family = AF_INET;
+
+ /*
+ ** The server name may be a host name or IP address
+ */
+
+ if ((int) (ip_addr = inet_addr(host)) != (int) INADDR_NONE)
+ {
+ memcpy_fixed(&sock_addr.sin_addr,&ip_addr,sizeof(ip_addr));
+ }
+ else
+#if defined(HAVE_GETHOSTBYNAME_R) && defined(_REENTRANT) && defined(THREAD)
+ {
+ int tmp_errno;
+ struct hostent tmp_hostent,*hp;
+ char buff2[GETHOSTBYNAME_BUFF_SIZE];
+ hp = my_gethostbyname_r(host,&tmp_hostent,buff2,sizeof(buff2),
+ &tmp_errno);
+ if (!hp)
+ {
+ net->last_errno=CR_UNKNOWN_HOST;
+ sprintf(net->last_error, ER(CR_UNKNOWN_HOST), host, tmp_errno);
+ goto error;
+ }
+ memcpy(&sock_addr.sin_addr,hp->h_addr, (size_t) hp->h_length);
+ }
+#else
+ {
+ struct hostent *hp;
+ if (!(hp=gethostbyname(host)))
+ {
+ net->last_errno=CR_UNKNOWN_HOST;
+ sprintf(net->last_error, ER(CR_UNKNOWN_HOST), host, errno);
+ goto error;
+ }
+ memcpy(&sock_addr.sin_addr,hp->h_addr, (size_t) hp->h_length);
+ }
+#endif
+ sock_addr.sin_port = (ushort) htons((ushort) port);
+ if (mc_sock_connect(sock,(struct sockaddr *) &sock_addr, sizeof(sock_addr),
+ mysql->options.connect_timeout) <0)
+ {
+ DBUG_PRINT("error",("Got error %d on connect to '%s'",ERRNO,host));
+ net->last_errno= CR_CONN_HOST_ERROR;
+ sprintf(net->last_error ,ER(CR_CONN_HOST_ERROR), host, ERRNO);
+ goto error;
+ }
+ }
+
+ if (!net->vio || my_net_init(net, net->vio))
+ {
+ vio_delete(net->vio);
+ net->last_errno=CR_OUT_OF_MEMORY;
+ strmov(net->last_error,ER(net->last_errno));
+ goto error;
+ }
+ vio_keepalive(net->vio,TRUE);
+
+ /* Get version info */
+ mysql->protocol_version= PROTOCOL_VERSION; /* Assume this */
+ if ((pkt_length=mc_net_safe_read(mysql)) == packet_error)
+ goto error;
+
+ /* Check if version of protocoll matches current one */
+
+ mysql->protocol_version= net->read_pos[0];
+ DBUG_DUMP("packet",(char*) net->read_pos,10);
+ DBUG_PRINT("info",("mysql protocol version %d, server=%d",
+ PROTOCOL_VERSION, mysql->protocol_version));
+ if (mysql->protocol_version != PROTOCOL_VERSION &&
+ mysql->protocol_version != PROTOCOL_VERSION-1)
+ {
+ net->last_errno= CR_VERSION_ERROR;
+ sprintf(net->last_error, ER(CR_VERSION_ERROR), mysql->protocol_version,
+ PROTOCOL_VERSION);
+ goto error;
+ }
+ end=strend((char*) net->read_pos+1);
+ mysql->thread_id=uint4korr(end+1);
+ end+=5;
+ strmake(mysql->scramble_buff,end,8);
+ if (pkt_length > (uint) (end+9 - (char*) net->read_pos))
+ mysql->server_capabilities=uint2korr(end+9);
+
+ /* Save connection information */
+ if (!user) user="";
+ if (!passwd) passwd="";
+ if (!my_multi_malloc(MYF(0),
+ &mysql->host_info,strlen(host_info)+1,
+ &mysql->host,strlen(host)+1,
+ &mysql->unix_socket,unix_socket ? strlen(unix_socket)+1
+ :1,
+ &mysql->server_version,
+ (uint) (end - (char*) net->read_pos),
+ NullS) ||
+ !(mysql->user=my_strdup(user,MYF(0))) ||
+ !(mysql->passwd=my_strdup(passwd,MYF(0))))
+ {
+ strmov(net->last_error, ER(net->last_errno=CR_OUT_OF_MEMORY));
+ goto error;
+ }
+ strmov(mysql->host_info,host_info);
+ strmov(mysql->host,host);
+ if (unix_socket)
+ strmov(mysql->unix_socket,unix_socket);
+ else
+ mysql->unix_socket=0;
+ strmov(mysql->server_version,(char*) net->read_pos+1);
+ mysql->port=port;
+ mysql->client_flag=client_flag | mysql->options.client_flag;
+ DBUG_PRINT("info",("Server version = '%s' capabilites: %ld",
+ mysql->server_version,mysql->server_capabilities));
+
+ /* Send client information for access check */
+ client_flag|=CLIENT_CAPABILITIES;
+
+#ifdef HAVE_OPENSSL
+ if (mysql->options.use_ssl)
+ client_flag|=CLIENT_SSL;
+#endif /* HAVE_OPENSSL */
+
+ if (db)
+ client_flag|=CLIENT_CONNECT_WITH_DB;
+#ifdef HAVE_COMPRESS
+ if (mysql->server_capabilities & CLIENT_COMPRESS &&
+ (mysql->options.compress || client_flag & CLIENT_COMPRESS))
+ client_flag|=CLIENT_COMPRESS; /* We will use compression */
+ else
+#endif
+ client_flag&= ~CLIENT_COMPRESS;
+
+#ifdef HAVE_OPENSSL
+ if ((mysql->server_capabilities & CLIENT_SSL) &&
+ (mysql->options.use_ssl || (client_flag & CLIENT_SSL)))
+ {
+ DBUG_PRINT("info", ("Changing IO layer to SSL"));
+ client_flag |= CLIENT_SSL;
+ }
+ else
+ {
+ if (client_flag & CLIENT_SSL)
+ {
+ DBUG_PRINT("info", ("Leaving IO layer intact because server doesn't support SSL"));
+ }
+ client_flag &= ~CLIENT_SSL;
+ }
+#endif /* HAVE_OPENSSL */
+
+ int2store(buff,client_flag);
+ mysql->client_flag=client_flag;
+
+#ifdef HAVE_OPENSSL
+ /* 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))
+ 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);
+ }
+#endif /* HAVE_OPENSSL */
+
+ 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 );
+ }
+
+ DBUG_PRINT("info",("user: %s",buff+5));
+ end=scramble(strend(buff+5)+1, mysql->scramble_buff, passwd,
+ (my_bool) (mysql->protocol_version == 9));
+ if (db)
+ {
+ end=strmov(end+1,db);
+ mysql->db=my_strdup(db,MYF(MY_WME));
+ }
+ if (my_net_write(net,buff,(uint) (end-buff)) || net_flush(net) ||
+ mc_net_safe_read(mysql) == packet_error)
+ goto error;
+ if (client_flag & CLIENT_COMPRESS) /* We will use compression */
+ net->compress=1;
+ DBUG_PRINT("exit",("Mysql handler: %lx",mysql));
+ DBUG_RETURN(mysql);
+
+error:
+ DBUG_PRINT("error",("message: %u (%s)",net->last_errno,net->last_error));
+ {
+ /* Free alloced memory */
+ my_bool free_me=mysql->free_me;
+ mc_end_server(mysql);
+ mysql->free_me=0;
+ mc_mysql_close(mysql);
+ mysql->free_me=free_me;
+ }
+ DBUG_RETURN(0);
+}
+
+/*************************************************************************
+** Send a QUIT to the server and close the connection
+** If handle is alloced by mysql connect free it.
+*************************************************************************/
+
+void STDCALL
+mc_mysql_close(MYSQL *mysql)
+{
+ DBUG_ENTER("mysql_close");
+ if (mysql) /* Some simple safety */
+ {
+ if (mysql->net.vio != 0)
+ {
+ mc_free_old_query(mysql);
+ mysql->status=MYSQL_STATUS_READY; /* Force command */
+ mc_simple_command(mysql,COM_QUIT,NullS,0,1);
+ mc_end_server(mysql);
+ }
+ my_free((gptr) mysql->host_info,MYF(MY_ALLOW_ZERO_PTR));
+ my_free(mysql->user,MYF(MY_ALLOW_ZERO_PTR));
+ my_free(mysql->passwd,MYF(MY_ALLOW_ZERO_PTR));
+ my_free(mysql->db,MYF(MY_ALLOW_ZERO_PTR));
+ /* Clear pointers for better safety */
+ mysql->host_info=mysql->user=mysql->passwd=mysql->db=0;
+ bzero((char*) &mysql->options,sizeof(mysql->options));
+ mysql->net.vio = 0;
+#ifdef HAVE_OPENSSL
+ ((VioConnectorFd*)(mysql->connector_fd))->delete();
+ mysql->connector_fd = 0;
+#endif /* HAVE_OPENSSL */
+ if (mysql->free_me)
+ my_free((gptr) mysql,MYF(0));
+ }
+ DBUG_VOID_RETURN;
+}
diff --git a/sql/Attic/mini_client_errors.c b/sql/Attic/mini_client_errors.c
new file mode 100644
index 00000000000..96f1f415544
--- /dev/null
+++ b/sql/Attic/mini_client_errors.c
@@ -0,0 +1,64 @@
+/* Copyright Abandoned 1997 TCX DataKonsult AB & Monty Program KB & Detron HB
+ This file is public domain and comes with NO WARRANTY of any kind */
+
+/* Error messages for MySQL clients */
+/* error messages for the demon is in share/language/errmsg.sys */
+
+#include <global.h>
+#include <my_sys.h>
+#include "errmsg.h"
+
+#ifdef GERMAN
+const char *client_errors[]=
+{
+ "Unbekannter MySQL Fehler",
+ "Kann UNIX-Socket nicht anlegen (%d)",
+ "Keine Verbindung zu lokalem MySQL Server, socket: '%-.64s' (%d)",
+ "Keine Verbindung zu MySQL Server auf %-.64s (%d)",
+ "Kann TCP/IP-Socket nicht anlegen (%d)",
+ "Unbekannter MySQL Server Host (%-.64s) (%d)",
+ "MySQL Server nicht vorhanden",
+ "Protokolle ungleich. Server Version = % d Client Version = %d",
+ "MySQL client got out of memory",
+ "Wrong host info",
+ "Localhost via UNIX socket",
+ "%s via TCP/IP",
+ "Error in server handshake",
+ "Lost connection to MySQL server during query",
+ "Commands out of sync; You can't run this command now",
+ "Verbindung ueber Named Pipe; Host: %-.64s",
+ "Kann nicht auf Named Pipe warten. Host: %-.64s pipe: %-.32s (%lu)",
+ "Kann Named Pipe nicht oeffnen. Host: %-.64s pipe: %-.32s (%lu)",
+ "Kann den Status der Named Pipe nicht setzen. Host: %-.64s pipe: %-.32s (%lu)"
+};
+
+#else /* ENGLISH */
+const char *client_errors[]=
+{
+ "Unknown MySQL error",
+ "Can't create UNIX socket (%d)",
+ "Can't connect to local MySQL server through socket '%-.64s' (%d)",
+ "Can't connect to MySQL server on '%-.64s' (%d)",
+ "Can't create TCP/IP socket (%d)",
+ "Unknown MySQL Server Host '%-.64s' (%d)",
+ "MySQL server has gone away",
+ "Protocol mismatch. Server Version = %d Client Version = %d",
+ "MySQL client run out of memory",
+ "Wrong host info",
+ "Localhost via UNIX socket",
+ "%s via TCP/IP",
+ "Error in server handshake",
+ "Lost connection to MySQL server during query",
+ "Commands out of sync; You can't run this command now",
+ "%s via named pipe",
+ "Can't wait for named pipe to host: %-.64s pipe: %-.32s (%lu)",
+ "Can't open named pipe to host: %-.64s pipe: %-.32s (%lu)",
+ "Can't set state of named pipe to host: %-.64s pipe: %-.32s (%lu)",
+};
+#endif
+
+
+void init_client_errs(void)
+{
+ errmsg[CLIENT_ERRMAP] = &client_errors[0];
+}
diff --git a/sql/Attic/mybinlogdump.cc b/sql/Attic/mybinlogdump.cc
new file mode 100644
index 00000000000..b94943c9847
--- /dev/null
+++ b/sql/Attic/mybinlogdump.cc
@@ -0,0 +1,138 @@
+
+#undef MYSQL_SERVER
+#include "log_event.h"
+#include <getopt.h>
+#include <config.h>
+
+static const char* default_dbug_option = "d:t:o,/tmp/mybinlogdump.trace";
+
+static struct option long_options[] =
+{
+ {"short-form", no_argument, 0, 's'},
+ {"offset", required_argument,0, 'o'},
+ {"help", no_argument, 0, 'h'},
+#ifndef DBUG_OFF
+ {"debug", required_argument, 0, '#'}
+#endif
+};
+
+static bool short_form = 0;
+static int offset = 0;
+
+static int parse_args(int argc, char** argv);
+static void dump_log_entries();
+static void die(char* fmt, ...);
+
+static void die(char* fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ fprintf(stderr, "ERROR: ");
+ vfprintf(stderr, fmt, args);
+ fprintf(stderr, "\n");
+ va_end(args);
+ exit(1);
+}
+
+static void usage()
+{
+ fprintf(stderr, "Usage: mybinlogdump [options] log-files\n");
+ fprintf(stderr, "Options:\n\
+ -s,--short-form - just show the queries, no extra info\n\
+ -o,--offset=N - skip the first N entries\n\
+ -h,--help - this message\n");
+}
+
+static int parse_args(int *argc, char*** argv)
+{
+ int c, opt_index = 0;
+
+ while((c = getopt_long(*argc, *argv, "so:#:h", long_options,
+ &opt_index)) != EOF)
+ {
+ switch(c)
+ {
+#ifndef DBUG_OFF
+ case '#':
+ DBUG_PUSH(optarg ? optarg : default_dbug_option);
+ break;
+#endif
+ case 's':
+ short_form = 1;
+ break;
+
+ case 'o':
+ offset = atoi(optarg);
+ break;
+
+ case 'h':
+ default:
+ usage();
+ exit(0);
+
+ }
+ }
+
+ (*argc)-=optind;
+ (*argv)+=optind;
+
+
+ return 0;
+}
+
+
+static void dump_log_entries(const char* logname)
+{
+ FILE* file;
+ int rec_count = 0;
+
+ if(logname && logname[0] != '-')
+ file = my_fopen(logname, O_RDONLY, MYF(MY_WME));
+ else
+ file = stdin;
+
+ if(!file)
+ die("Could not open log file %s", logname);
+ while(1)
+ {
+ Log_event* ev = Log_event::read_log_event(file);
+ if(!ev)
+ if(!feof(file))
+ die("Could not read entry at offset %ld : Error in log format or \
+read error",
+ my_ftell(file, MYF(MY_WME)));
+ else
+ break;
+
+ if(rec_count >= offset)
+ ev->print(stdout, short_form);
+ rec_count++;
+ delete ev;
+ }
+
+ my_fclose(file, MYF(MY_WME));
+}
+
+int main(int argc, char** argv)
+{
+ MY_INIT(argv[0]);
+ parse_args(&argc, (char***)&argv);
+
+ if(!argc)
+ {
+ usage();
+ return -1;
+ }
+
+ while(--argc >= 0)
+ {
+ dump_log_entries(*(argv++));
+ }
+
+ return 0;
+}
+
+
+
+
+
diff --git a/sql/Attic/net_serv.c b/sql/Attic/net_serv.c
new file mode 100644
index 00000000000..8f5ba20664a
--- /dev/null
+++ b/sql/Attic/net_serv.c
@@ -0,0 +1,616 @@
+/* Copyright Abandoned 1996 TCX DataKonsult AB & Monty Program KB & Detron HB
+ This file is public domain and comes with NO WARRANTY of any kind */
+
+/* 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 _WIN32
+#include <winsock.h>
+#endif
+#include <global.h>
+#include <violite.h>
+#include <my_sys.h>
+#include <m_string.h>
+#include "mysql.h"
+#include <signal.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <violite.h>
+
+#if !defined(__WIN32__) && !defined(MSDOS)
+#include <sys/socket.h>
+#endif
+#if !defined(MSDOS) && !defined(__WIN32__) && !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>
+#endif
+#endif
+#include "mysqld_error.h"
+#ifdef MYSQL_SERVER
+#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
+typedef my_bool thr_alarm_t;
+#define thr_alarm_init(A) (*A)=0
+#define thr_alarm_in_use(A) (A)
+#define thr_end_alarm(A)
+#define thr_alarm(A,B) local_thr_alarm((A),(B))
+static inline int local_thr_alarm(my_bool *A,int B __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 pthread_mutex_t LOCK_bytes_sent , LOCK_bytes_received;
+#else
+#undef thread_safe_add
+#define thread_safe_add(A,B,C)
+#endif
+
+/*
+** 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.
+*/
+
+#ifdef MYSQL_SERVER
+ulong max_allowed_packet=65536;
+extern uint test_flags;
+#else
+ulong max_allowed_packet=16*1024*1024L;
+#endif
+ulong net_buffer_length=8192; /* Default length. Enlarged if necessary */
+
+#define TEST_BLOCKING 8
+static int net_write_buff(NET *net,const char *packet,uint 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);
+ net->vio = vio;
+ net->error=net->return_errno=0;
+ net->timeout=NET_READ_TIMEOUT; /* Timeout for read */
+ net->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;
+
+ if (vio != 0) /* If real connection */
+ {
+ net->fd = vio_fd(vio); /* For perl DBI/DBD */
+#if defined(MYSQL_SERVER) && !defined(___WIN32__) && !defined(__EMX__)
+ if (!(test_flags & TEST_BLOCKING))
+ vio_blocking(vio, FALSE);
+#endif
+ vio_fastsend(vio,TRUE);
+ }
+ return 0;
+}
+
+void net_end(NET *net)
+{
+ my_free((gptr) net->buff,MYF(MY_ALLOW_ZERO_PTR));
+ net->buff=0;
+}
+
+/* Realloc the packet buffer */
+
+static my_bool net_realloc(NET *net, ulong length)
+{
+ uchar *buff;
+ ulong pkt_length;
+ if (length >= max_allowed_packet)
+ {
+ DBUG_PRINT("error",("Packet too large (%ld)", length));
+#ifdef MYSQL_SERVER
+ sql_print_error("Packet too large (%ld)\n", length);
+#else
+ fprintf(stderr,"Packet too large (%ld)\n", length);
+#endif
+ net->error=1;
+#ifdef MYSQL_SERVER
+ net->last_errno=ER_NET_PACKET_TOO_LARGE;
+#endif
+ return 1;
+ }
+ pkt_length = (length+IO_SIZE-1) & ~(IO_SIZE-1);
+ if (!(buff=(uchar*) my_realloc((char*) net->buff, pkt_length, MYF(MY_WME))))
+ {
+ net->error=1;
+#ifdef MYSQL_SERVER
+ net->last_errno=ER_OUT_OF_RESOURCES;
+#endif
+ return 1;
+ }
+ net->buff=net->write_pos=buff;
+ net->buff_end=buff+(net->max_packet=pkt_length);
+ return 0;
+}
+
+ /* Remove unwanted characters from connection */
+
+void net_clear(NET *net)
+{
+#ifndef EXTRA_DEBUG
+ int count;
+ 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 */
+ {
+ 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);
+ }
+#endif /* EXTRA_DEBUG */
+ net->pkt_nr=0; /* Ready for new command */
+ net->write_pos=net->buff;
+}
+
+ /* Flush write_buffer if not empty. */
+
+int net_flush(NET *net)
+{
+ int error=0;
+ DBUG_ENTER("net_flush");
+ if (net->buff != net->write_pos)
+ {
+ error=net_real_write(net,(char*) net->buff,
+ (uint) (net->write_pos - net->buff));
+ net->write_pos=net->buff;
+ }
+ DBUG_RETURN(error);
+}
+
+
+/*****************************************************************************
+** 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!
+*/
+
+int
+my_net_write(NET *net,const char *packet,ulong len)
+{
+ uchar buff[NET_HEADER_SIZE];
+ int3store(buff,len);
+ buff[3]= (net->compress) ? 0 : (uchar) (net->pkt_nr++);
+ if (net_write_buff(net,(char*) buff,NET_HEADER_SIZE))
+ return 1;
+ return net_write_buff(net,packet,len);
+}
+
+int
+net_write_command(NET *net,uchar command,const char *packet,ulong len)
+{
+ uchar buff[NET_HEADER_SIZE+1];
+ uint length=len+1; /* 1 extra byte for command */
+
+ 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));
+}
+
+
+static int
+net_write_buff(NET *net,const char *packet,uint len)
+{
+ uint left_length=(uint) (net->buff_end - net->write_pos);
+
+ while (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;
+ }
+ memcpy((char*) net->write_pos,packet,len);
+ net->write_pos+=len;
+ return 0;
+}
+
+/* Read and write using timeouts */
+
+int
+net_real_write(NET *net,const char *packet,ulong len)
+{
+ int length;
+ char *pos,*end;
+ thr_alarm_t alarmed;
+ uint retry_count=0;
+ my_bool net_blocking = vio_is_blocking(net->vio);
+ DBUG_ENTER("net_real_write");
+
+ net->reading_or_writing=2;
+#ifdef HAVE_COMPRESS
+ if (net->compress)
+ {
+ 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))))
+ {
+#ifdef MYSQL_SERVER
+ net->last_errno=ER_OUT_OF_RESOURCES;
+ net->error=1;
+#endif
+ net->reading_or_writing=0;
+ DBUG_RETURN(1);
+ }
+ 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++);
+ len+= header_length;
+ packet= (char*) b;
+ }
+#endif /* HAVE_COMPRESS */
+
+ /* DBUG_DUMP("net",packet,len); */
+#ifdef MYSQL_SERVER
+ thr_alarm_init(&alarmed);
+ if (net_blocking)
+ thr_alarm(&alarmed,NET_WRITE_TIMEOUT);
+#else
+ alarmed=0;
+#endif /* MYSQL_SERVER */
+
+ pos=(char*) packet; end=pos+len;
+ while (pos != end)
+ {
+ if ((int) (length=vio_write(net->vio,pos,(size_t) (end-pos))) <= 0)
+ {
+ my_bool interrupted = vio_should_retry(net->vio);
+#if (!defined(__WIN32__) && !defined(__EMX__))
+ if ((interrupted || length==0) && !thr_alarm_in_use(alarmed))
+ {
+ if (!thr_alarm(&alarmed,NET_WRITE_TIMEOUT))
+ { /* Always true for client */
+ if (!vio_is_blocking(net->vio))
+ {
+ while (vio_blocking(net->vio, TRUE) < 0)
+ {
+ if (vio_should_retry(net->vio) && retry_count++ < 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));
+#endif /* EXTRA_DEBUG */
+ net->error=1; /* Close socket */
+ goto end;
+ }
+ }
+ retry_count=0;
+ continue;
+ }
+ }
+ else
+#endif /* (!defined(__WIN32__) && !defined(__EMX__)) */
+ if (thr_alarm_in_use(alarmed) && !thr_got_alarm(alarmed) &&
+ interrupted)
+ {
+ if (retry_count++ < RETRY_COUNT)
+ continue;
+#ifdef EXTRA_DEBUG
+ fprintf(stderr, "%s: write looped, aborting thread\n",
+ my_progname);
+#endif /* EXTRA_DEBUG */
+ }
+#if defined(THREAD_SAFE_CLIENT) && !defined(MYSQL_SERVER)
+ if (vio_errno(net->vio) == EINTR)
+ {
+ DBUG_PRINT("warning",("Interrupted write. Retrying..."));
+ continue;
+ }
+#endif /* defined(THREAD_SAFE_CLIENT) && !defined(MYSQL_SERVER) */
+ net->error=1; /* Close socket */
+#ifdef MYSQL_SERVER
+ net->last_errno= (interrupted ? ER_NET_WRITE_INTERRUPTED :
+ ER_NET_ERROR_ON_WRITE);
+#endif /* MYSQL_SERVER */
+ break;
+ }
+ pos+=length;
+ thread_safe_add(bytes_sent,length,&LOCK_bytes_sent);
+ }
+#ifndef __WIN32__
+ end:
+#endif
+#ifdef HAVE_COMPRESS
+ if (net->compress)
+ my_free((char*) packet,MYF(0));
+#endif
+ if (thr_alarm_in_use(alarmed))
+ {
+ thr_end_alarm(&alarmed);
+ vio_blocking(net->vio, net_blocking);
+ }
+ net->reading_or_writing=0;
+ DBUG_RETURN(((int) (pos != end)));
+}
+
+
+/*****************************************************************************
+** Read something from server/clinet
+*****************************************************************************/
+
+
+static uint
+my_real_read(NET *net, ulong *complen)
+{
+ uchar *pos;
+ long length;
+ uint i,retry_count=0;
+ ulong len=packet_error;
+ thr_alarm_t alarmed;
+ my_bool net_blocking=vio_is_blocking(net->vio);
+ ulong 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
+ if (net_blocking)
+ thr_alarm(&alarmed,net->timeout);
+#endif /* MYSQL_SERVER */
+
+ pos = net->buff + net->where_b; /* net->packet -4 */
+ for (i=0 ; i < 2 ; i++)
+ {
+ while (remain > 0)
+ {
+ /* First read is done with non blocking mode */
+ if ((int) (length=vio_read(net->vio,(char*) pos,remain)) <= 0L)
+ {
+ my_bool interrupted = vio_should_retry(net->vio);
+
+ DBUG_PRINT("info",("vio_read returned %d, errno: %d",
+ length, vio_errno(net->vio)));
+#if (!defined(__WIN32__) && !defined(__EMX__)) || !defined(MYSQL_SERVER)
+ /*
+ We got an error that there was no data on the socket. We now set up
+ an alarm to not 'read forever', change the socket to non blocking
+ mode and try again
+ */
+ if ((interrupted || length == 0) && !thr_alarm_in_use(alarmed))
+ {
+ if (!thr_alarm(&alarmed,net->timeout)) /* 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)));
+#ifdef EXTRA_DEBUG
+ 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=1; /* Close socket */
+#ifdef MYSQL_SERVER
+ net->last_errno=ER_NET_FCNTL_ERROR;
+#endif
+ goto end;
+ }
+ }
+ retry_count=0;
+ continue;
+ }
+ }
+#endif /* (!defined(__WIN32__) && !defined(__EMX__)) || !defined(MYSQL_SERVER) */
+ if (thr_alarm_in_use(alarmed) && !thr_got_alarm(alarmed) &&
+ interrupted)
+ { /* Probably in MIT threads */
+ if (retry_count++ < RETRY_COUNT)
+ continue;
+#ifdef EXTRA_DEBUG
+ fprintf(stderr, "%s: read looped with error %d, aborting thread\n",
+ my_progname,vio_errno(net->vio));
+#endif /* EXTRA_DEBUG */
+ }
+#if defined(THREAD_SAFE_CLIENT) && !defined(MYSQL_SERVER)
+ if (vio_should_retry(net->vio))
+ {
+ DBUG_PRINT("warning",("Interrupted read. Retrying..."));
+ continue;
+ }
+#endif
+ DBUG_PRINT("error",("Couldn't read packet: remain: %d errno: %d length: %d alarmed: %d", remain,vio_errno(net->vio),length,alarmed));
+ len= packet_error;
+ net->error=1; /* Close socket */
+#ifdef MYSQL_SERVER
+ net->last_errno= (interrupted ? ER_NET_READ_INTERRUPTED :
+ ER_NET_READ_ERROR);
+#endif
+ goto end;
+ }
+ remain -= (ulong) length;
+ pos+= (ulong) length;
+ thread_safe_add(bytes_received,(ulong) length,&LOCK_bytes_received);
+ }
+ if (i == 0)
+ { /* First parts is packet length */
+ ulong helping;
+ if (net->buff[net->where_b + 3] != (uchar) net->pkt_nr)
+ {
+ if (net->buff[net->where_b] != (uchar) 255)
+ {
+ DBUG_PRINT("error",
+ ("Packets out of order (Found: %d, expected %d)",
+ (int) net->buff[net->where_b + 3],
+ (uint) (uchar) net->pkt_nr));
+#ifdef EXTRA_DEBUG
+ fprintf(stderr,"Packets out of order (Found: %d, expected %d)\n",
+ (int) net->buff[net->where_b + 3],
+ (uint) (uchar) net->pkt_nr);
+#endif
+ }
+ len= packet_error;
+#ifdef MYSQL_SERVER
+ net->last_errno=ER_NET_PACKETS_OUT_OF_ORDER;
+#endif
+ goto end;
+ }
+ net->pkt_nr++;
+#ifdef HAVE_COMPRESS
+ if (net->compress)
+ {
+ /* complen is > 0 if package is really compressed */
+ *complen=uint3korr(&(net->buff[net->where_b + NET_HEADER_SIZE]));
+ }
+#endif
+
+ len=uint3korr(net->buff+net->where_b);
+ 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))
+ {
+ len= packet_error; /* Return error */
+ goto end;
+ }
+ }
+ pos=net->buff + net->where_b;
+ remain = len;
+ }
+ }
+
+end:
+ if (thr_alarm_in_use(alarmed))
+ {
+ thr_end_alarm(&alarmed);
+ vio_blocking(net->vio, net_blocking);
+ }
+ net->reading_or_writing=0;
+ return(len);
+}
+
+
+uint
+my_net_read(NET *net)
+{
+ ulong len,complen;
+
+#ifdef HAVE_COMPRESS
+ if (!net->compress)
+ {
+#endif
+ len = my_real_read (net,&complen);
+ 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 (;;)
+ {
+ if (net->remain_in_buf)
+ {
+ uchar *pos = net->buff + net->buf_length - net->remain_in_buf;
+ if (net->remain_in_buf >= 4)
+ {
+ net->length = uint3korr(pos);
+ if (net->length <= net->remain_in_buf - 4)
+ {
+ /* 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 */
+ }
+ }
+ /* Move data down to read next data packet after current one */
+ if (net->buf_length != net->remain_in_buf)
+ {
+ memmove(net->buff,pos,net->remain_in_buf);
+ net->buf_length=net->remain_in_buf;
+ }
+ 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=1; /* caller will close socket */
+#ifdef MYSQL_SERVER
+ net->last_errno=ER_NET_UNCOMPRESS_ERROR;
+#endif
+ break;
+ }
+ net->buf_length+=len;
+ net->remain_in_buf+=len;
+ }
+ if (len != packet_error)
+ {
+ net->save_char= net->read_pos[len]; /* Must be saved */
+ net->read_pos[len]=0; /* Safeguard for mysql_use_result */
+ }
+ return len;
+#endif
+}
diff --git a/sql/ChangeLog b/sql/ChangeLog
new file mode 100644
index 00000000000..81ce83aa243
--- /dev/null
+++ b/sql/ChangeLog
@@ -0,0 +1,3268 @@
+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
new file mode 100644
index 00000000000..a4aee97d0bb
--- /dev/null
+++ b/sql/Makefile.am
@@ -0,0 +1,126 @@
+# 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
+
+#called from the top level Makefile
+
+
+MYSQLDATAdir = $(localstatedir)
+MYSQLSHAREdir = $(pkgdatadir)
+MYSQLBASEdir= $(prefix)
+INCLUDES = @MT_INCLUDES@ @bdb_includes@ -I$(srcdir)/../include \
+ -I$(srcdir)/../regex \
+ -I$(srcdir) -I../include -I.. -I.
+WRAPLIBS= @WRAPLIBS@
+SUBDIRS = share
+bin_PROGRAMS = mysqlbinlog
+libexec_PROGRAMS = mysqld
+noinst_PROGRAMS = gen_lex_hash
+LDADD = ../isam/libnisam.a \
+ ../merge/libmerge.a \
+ ../myisam/libmyisam.a \
+ ../myisammrg/libmyisammrg.a \
+ ../heap/libheap.a \
+ ../mysys/libmysys.a \
+ ../dbug/libdbug.a \
+ ../regex/libregex.a \
+ ../strings/libmystrings.a
+mysqld_LDADD = @MYSQLD_EXTRA_LDFLAGS@ @bdb_libs@ $(LDADD) $(CXXLDFLAGS) $(WRAPLIBS)
+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 \
+ procedure.h sql_class.h sql_lex.h sql_list.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\
+ opt_range.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
+mysqld_SOURCES = sql_lex.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 \
+ sql_string.cc sql_map.cc \
+ mysqld.cc password.c hash_filo.cc hostname.cc \
+ convert.cc sql_parse.cc sql_yacc.yy \
+ sql_base.cc table.cc sql_select.cc sql_insert.cc \
+ sql_update.cc sql_delete.cc \
+ procedure.cc item_uniq.cc sql_test.cc \
+ log.cc init.cc derror.cc sql_acl.cc unireg.cc \
+ time.cc opt_range.cc opt_sum.cc \
+ records.cc filesort.cc handler.cc \
+ ha_isam.cc ha_isammrg.cc ha_heap.cc \
+ ha_myisam.cc ha_myisammrg.cc ha_berkeley.cc \
+ sql_db.cc sql_table.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 \
+ md5.c log_event.cc mini_client.cc mini_client_errors.c
+gen_lex_hash_SOURCES = gen_lex_hash.cc
+gen_lex_hash_LDADD = $(LDADD) $(CXXLDFLAGS)
+mysqlbinlog_SOURCES = mysqlbinlog.cc mini_client.cc net_serv.cc \
+ mini_client_errors.c violite.c password.c
+mysqlbinlog_LDADD = $(LDADD) $(CXXLDFLAGS)
+
+DEFS = -DMYSQL_SERVER \
+ -DDEFAULT_MYSQL_HOME="\"$(MYSQLBASEdir)\"" \
+ -DDATADIR="\"$(MYSQLDATAdir)\"" \
+ -DSHAREDIR="\"$(MYSQLSHAREdir)\"" \
+ @DEFS@
+# Don't put lex_hash.h in BUILT_SOURCES as this will give infinite recursion
+BUILT_SOURCES = sql_yacc.cc sql_yacc.h
+EXTRA_DIST = udf_example.cc $(BUILT_SOURCES)
+YFLAGS = -d
+
+OMIT_DEPENDENCIES = pthread.h stdio.h __stdio.h stdlib.h __stdlib.h math.h\
+ __math.h time.h __time.h unistd.h __unistd.h types.h \
+ xtypes.h ac-types.h posix.h string.h __string.h \
+ errno.h socket.h inet.h dirent.h netdb.h \
+ cleanup.h cond.h debug_out.h fd.h kernel.h mutex.h \
+ prio_queue.h pthread_attr.h pthread_once.h queue.h\
+ sleep.h specific.h version.h pwd.h timers.h uio.h \
+ cdefs.h machdep.h signal.h __signal.h util.h lex.h \
+ wait.h
+
+link_sources:
+ rm -f mini_client_errors.c
+ @LN_CP_F@ ../libmysql/errmsg.c mini_client_errors.c
+
+gen_lex_hash.o: gen_lex_hash.cc lex.h
+ $(CXXCOMPILE) -c $(INCLUDES) $<
+
+# Try to get better dependencies for the grammar. Othervise really bad
+# things like different grammars for different pars of MySQL can
+# happen if you are unlucky.
+sql_yacc.cc: sql_yacc.yy
+sql_yacc.h: sql_yacc.yy
+
+sql_yacc.o: sql_yacc.cc sql_yacc.h
+ @echo "Note: The folloing compile may take a long time."
+ @echo "If it fails, re-run configure with --with-low-memory"
+ $(CXXCOMPILE) $(LM_CFLAGS) -c $<
+
+lex_hash.h: lex.h gen_lex_hash.cc sql_yacc.h
+ $(MAKE) gen_lex_hash
+ ./gen_lex_hash > $@
+
+# Hack to ensure that lex_hash.h is built early
+sql_lex.o: lex_hash.h
+
+#distclean:
+# rm -f lex_hash.h
diff --git a/sql/add_errmsg b/sql/add_errmsg
new file mode 100755
index 00000000000..cf54ede5dce
--- /dev/null
+++ b/sql/add_errmsg
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+if test $# -ne 1
+then
+ echo "Copies # error messages from share/english/errmsg.txt to other message files"
+ echo "Usage: $0 number_of_messages_to_copy"
+ exit 1;
+fi
+
+FILE=/tmp/add.$$
+tail -$1 share/english/errmsg.txt > $FILE
+for i in `ls share/*/errmsg.txt | grep -v english`
+do
+ cat $FILE >> $i
+done
+rm $FILE
+
diff --git a/sql/cache_manager.cc b/sql/cache_manager.cc
new file mode 100644
index 00000000000..9ea25315f8c
--- /dev/null
+++ b/sql/cache_manager.cc
@@ -0,0 +1,150 @@
+/* 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 __GNUC__
+#pragma implementation /* gcc: Class implementation */
+#endif
+
+#include <global.h>
+#include <my_sys.h>
+#include "cache_manager.h"
+
+/*
+ cache_manager.cc
+ -----------------------------------------------------------------------
+ The cache_manager class manages a number of blocks (which are allocatable
+ units of memory).
+ -----------------------------------------------------------------------
+*/
+
+#define HEADER_LENGTH ALIGN_SIZE(8)
+#define SUFFIX_LENGTH 4
+
+#define ALLOC_MASK 0x3FFFFFFFL
+#define FREE_BIT (1L << 31)
+#define LOCK_BIT (1L << 30)
+#define SMALLEST_BLOCK 32
+
+
+
+/*
+** Internal Methods
+** --------------------
+*/
+
+
+/* list manipulation methods */
+void *cache_manager::init_list(void)
+{
+
+return;
+}
+
+
+void *cache_manager::link_into_abs(void *ptr)
+{
+ for (int i(0); (*abs_list)[i] != NULL; i++);
+
+ (*abs_list)[i] = ptr;
+
+ return (abs_list)[i]; // ???
+}
+
+
+
+bool *cache_manager::unlink_from_abs(void *ptr)
+{
+ (*ptr) = NULL;
+
+return;
+}
+
+
+
+/* memory allocation methods */
+void *cache_manager::find_in_llist(uint)
+{
+
+return;
+}
+
+
+void cache_manager::defrag(void)
+{
+ printf("Defragging: ..........");
+
+ return;
+}
+
+
+
+/*
+** Public Methods
+** ------------------
+*/
+
+cache_manager::cache_manager(uint size)
+{
+ base_ptr = my_malloc(size, MY_WME); /* MY_WME = write mesg on err */
+
+ return;
+}
+
+
+cache_manager::~cache_manager(void)
+{
+ free(base_ptr);
+ delete base_ptr;
+
+ return;
+}
+
+
+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)))
+ {
+ //defrag();
+ if (!(llist = find_in_llist(size)))
+ return 0; /* return null pointer, buffer exhausted! */
+ }
+ 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;
+}
+
+
+void cache_manager::dealloc(void)
+{
+ printf("Deallocating: ..........\n");
+
+ return;
+}
+
+
+
+void cache_manager::clear(void)
+{
+ // reset the internal linked list, forgetting all pointers to mem blks
+
+ return;
+}
diff --git a/sql/cache_manager.h b/sql/cache_manager.h
new file mode 100644
index 00000000000..fc3b8f7016a
--- /dev/null
+++ b/sql/cache_manager.h
@@ -0,0 +1,65 @@
+/* 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 __GNUC__
+#pragma interface /* gcc class implementation */
+#endif
+
+#ifndef _CACHE_MANAGER_H_
+#define _CACHE_MANAGER_H_
+#endif
+
+/*
+ cache_manager.h
+ -----------------------------------------------------------------------
+ The cache_manager class manages a number of blocks (which are allocatable
+ units of memory).
+ -----------------------------------------------------------------------
+*/
+
+
+
+class cache_manager {
+ void **abs_list; /* List holding block abstraction ptrs */
+
+ typedef struct free_blks {
+ struct free_blks *next, **prev;
+ uint Size;
+ } FREE_BLKS;
+ FREE_BLKS *base_ptr; /* Pointer to newly allocated sys mem */
+
+
+ /* list manipulation methods */
+ void *link_into_abs(void *); /* Return an abstract pointer to blk */
+ bool *unlink_from_abs(void *); /* Used to dealloc a blk */
+ void *find_in_fblist(uint); /* */
+
+ /* memory allocation methods */
+ void defrag(void); /* Defragment the cache */
+ bool *init_blk(void *); /* Return a pointer to new list */
+
+ public:
+ cache_manager(uint); /* Get allocation of size from system */
+ ~cache_manager(void); /* Destructor; return the cache */
+
+ void *alloc(uint); /* Alloc size bytes from the cache */
+ bool *dealloc(void *); /* Deallocate blocks (with *ptr_arg) */
+ void clear(void); /* Clear the cache */
+};
+
+
+
+
diff --git a/sql/convert.cc b/sql/convert.cc
new file mode 100644
index 00000000000..3e0fbf18ace
--- /dev/null
+++ b/sql/convert.cc
@@ -0,0 +1,465 @@
+/* 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 */
+
+
+/*
+** Convert tables between different character sets
+** Some of the tables are hidden behind IFDEF to reduce some space.
+** One can enable them by removing the // characters from the next comment
+** One must also give a name to each mapping that one wants to use...
+*/
+
+/* #define DEFINE_ALL_CHARACTER_SETS */
+
+#include "mysql_priv.h"
+
+/****************************************************************************
+** Convert tables
+****************************************************************************/
+
+/* Windows cp1251->koi8 and reverse conversion by Timur I. Bakeyev <translate@bat.ru> */
+/* based on Russian-Apache Team tables by Dmitry M. Klimoff <dmk@kosnet.ru> */
+
+static unsigned char cp1251_koi8[256] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,
+112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
+128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
+144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,
+160,161,162,163,164,189,166,167,179,169,180,171,172,173,174,183,
+176,177,182,166,173,181,182,183,163,185,164,187,188,189,190,167,
+225,226,247,231,228,229,246,250,233,234,235,236,237,238,239,240,
+242,243,244,245,230,232,227,254,251,253,255,249,248,252,224,241,
+193,194,215,199,196,197,214,218,201,202,203,204,205,206,207,208,
+210,211,212,213,198,200,195,222,219,221,223,217,216,220,192,209
+};
+
+static unsigned char koi8_cp1251[256] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,
+112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
+128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
+144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,
+160,161,162,184,186,165,179,191,168,169,170,171,172,180,174,175,
+176,177,178,168,170,181,178,175,184,185,186,187,188,165,190,191,
+254,224,225,246,228,229,244,227,245,232,233,234,235,236,237,238,
+239,255,240,241,242,243,230,226,252,251,231,248,253,249,247,250,
+222,192,193,214,196,197,212,195,213,200,201,202,203,204,205,206,
+207,223,208,209,210,211,198,194,220,219,199,216,221,217,215,218
+};
+
+
+#ifdef DEFINE_ALL_CHARACTER_SETS
+
+/* These tables was generated from package 'cstools' (author Jan "Yenya" Kasprzak <kas@muni.cz>) */
+
+/* Windows pc1250 to iso 8859-2 */
+
+static unsigned char t1250_til2[256] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,
+112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
+128,129, 0,131, 0, 0, 0, 0,136, 0,169, 0,166,171,174,172,
+144, 0, 0, 0, 0, 0, 0, 0,152, 0,185, 0,182,187,190,188,
+160,183,162,163,164,161, 0,167,168, 0,170, 0, 0,173, 0,175,
+176, 0,178,179,180, 0, 0, 0,184,177,186, 0,165,189,181,191,
+192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,
+208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,
+224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,
+240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255
+};
+
+static unsigned char til2_t1250[256] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,
+112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
+128,129, 0,131, 0, 0, 0, 0,136, 0, 0, 0, 0, 0, 0, 0,
+144, 0, 0, 0, 0, 0, 0, 0,152, 0, 0, 0, 0, 0, 0, 0,
+160,165,162,163,164,188,140,167,168,138,170,141,143,173,142,175,
+176,185,178,179,180,190,156,161,184,154,186,157,159,189,158,191,
+192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,
+208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,
+224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,
+240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255
+};
+
+/* Windows pc1252 to iso 8859-2 */
+
+static unsigned char t1252_til2[256] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,
+112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
+128,129, 0, 0, 0, 0, 0, 0,136, 0,169, 0, 0,141,142,143,
+144, 0, 0, 0, 0, 0, 0, 0,189, 0,185, 0, 0,157,158, 0,
+160, 0, 0, 0,164, 0, 0,167,168, 0, 0, 0, 0,173, 0, 0,
+176, 0, 0, 0,180, 0, 0, 0,184, 0, 0, 0, 0, 0, 0, 0,
+ 0,193,194, 0,196, 0, 0,199, 0,201, 0,203, 0,205,206, 0,
+208, 0, 0,211,212, 0,214,215, 0, 0,218, 0,220,221, 0,223,
+ 0,225,226, 0,228, 0, 0,231, 0,233, 0,235, 0,237,238, 0,
+240, 0, 0,243,244, 0,246,247, 0, 0,250, 0,252,253, 0, 0
+};
+static unsigned char til2_t1252[256] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,
+112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
+128,129, 0, 0, 0, 0, 0, 0,136, 0, 0, 0, 0,141,142,143,
+144, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,157,158, 0,
+160, 0, 0, 0,164, 0, 0,167,168,138, 0, 0, 0,173, 0, 0,
+176, 0, 0, 0,180, 0, 0, 0,184,154, 0, 0, 0,152, 0, 0,
+ 0,193,194, 0,196, 0, 0,199, 0,201, 0,203, 0,205,206, 0,
+208, 0, 0,211,212, 0,214,215, 0, 0,218, 0,220,221, 0,223,
+ 0,225,226, 0,228, 0, 0,231, 0,233, 0,235, 0,237,238, 0,
+240, 0, 0,243,244, 0,246,247, 0, 0,250, 0,252,253, 0, 0
+};
+
+/* MSDOS Kamenicky encoding (for Czech/Slovak) to iso 8859-2 */
+
+static unsigned char tkam_til2[256] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,
+112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
+200,252,233,239,228,207,171,232,236,204,197,205,181,229,196,193,
+201,190,174,244,246,211,249,218,253,214,220,169,165,221,216,187,
+225,237,243,250,242,210,217,212,185,248,224,192, 0,167, 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, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,247, 0,176, 0, 0, 0, 0, 0, 0, 0
+};
+static unsigned char til2_tkam[256] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,
+112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
+ 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,156, 0,173, 0,155, 0,134, 0, 0,146, 0,
+248, 0, 0, 0, 0,140, 0, 0, 0,168, 0,159, 0, 0,145, 0,
+171,143, 0, 0,142,138, 0, 0,128,144, 0, 0,137,139, 0,133,
+ 0, 0,165,149,167, 0,153, 0,158,166,151, 0,154,157, 0, 0,
+170,160, 0, 0,132,141, 0, 0,135,130, 0, 0,136,161, 0,131,
+ 0, 0,164,162,147, 0,148,246,169,150,163, 0,129,152, 0, 0
+};
+
+/* Macintosh Roman encoding to iso 8859-2 */
+
+static unsigned char tmac_til2[256] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,
+112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
+196, 0,199,201, 0,214,220,225, 0,226,228, 0, 0,231,233, 0,
+ 0,235,237, 0,238, 0, 0,243, 0,244,246, 0,250, 0, 0,252,
+ 0,176, 0, 0,167, 0, 0,223, 0, 0, 0,180,168, 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,160, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,247, 0, 0, 0, 0,164, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,194, 0,193,203, 0,205,206, 0, 0,211,212,
+ 0, 0,218, 0, 0, 0, 0, 0, 0,162,255, 0,184,189,178,183
+};
+static unsigned char til2_tmac[256] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,
+112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
+ 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,
+202, 0,249, 0,219, 0, 0,164,172, 0, 0, 0, 0, 0, 0, 0,
+161, 0,254, 0,171, 0, 0,255,252, 0, 0, 0, 0,253, 0, 0,
+ 0,231,229, 0,128, 0, 0,130, 0,131, 0,232, 0,234,235, 0,
+ 0, 0, 0,238,239, 0,133, 0, 0, 0,242, 0,134, 0, 0,167,
+ 0,135,137, 0,138, 0, 0,141, 0,142, 0,145, 0,146,148, 0,
+ 0, 0, 0,151,153, 0,154,214, 0, 0,156, 0,159, 0, 0,250
+};
+
+/* Macintosh Central European encodingto iso 8859-2 */
+
+static unsigned char tmacce_til2[256] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,
+112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
+196, 0, 0,201,161,214,220,225,177,200,228,232,198,230,233,172,
+188,207,237,239, 0, 0, 0,243, 0,244,246, 0,250,204,236,252,
+ 0,176,202, 0,167, 0, 0,223, 0, 0, 0,234,168, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,179, 0, 0,165,181,197,229, 0,
+ 0,209, 0, 0,241,210, 0, 0, 0, 0,160,242,213, 0,245, 0,
+ 0, 0, 0, 0, 0, 0,247, 0, 0,192,224,216, 0, 0,248, 0,
+ 0,169, 0, 0,185,166,182,193,171,187,205,174,190, 0,211,212,
+ 0,217,218,249,219,251, 0, 0,221,253, 0,175,163,191, 0,183
+};
+static unsigned char til2_tmacce[256] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,
+112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
+ 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,
+202,132, 0,252, 0,187,229,164,172,225, 0,232,143, 0,235,251,
+161,136, 0,184, 0,188,230,255, 0,228, 0,233,144, 0,236,253,
+217,231, 0, 0,128,189,140, 0,137,131,162, 0,157,234, 0,145,
+ 0,193,197,238,239,204,133, 0,219,241,242,244,134,248, 0,167,
+218,135, 0, 0,138,190,141, 0,139,142,171, 0,158,146, 0,147,
+ 0,196,203,151,153,206,154,214,222,243,156,245,159,249, 0, 0
+};
+
+/* PC-Latin2 encoding, supported by M$-DOS to iso 8859-2 */
+
+static unsigned char tpc2_til2[256] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,
+112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
+199,252,233,226,228,249,230,231,179,235,213,245,238,172,196,198,
+201,197,229,244,246,165,181,166,182,214,220,171,187,163,215,232,
+225,237,243,250,161,177,174,190,202,234, 0,188,200,186, 0, 0,
+ 0, 0, 0, 0, 0,193,194,204,170, 0, 0, 0, 0,175,191, 0,
+ 0, 0, 0, 0, 0, 0,195,227, 0, 0, 0, 0, 0, 0, 0,164,
+240,208,207,203,239,210,205,206,236, 0, 0, 0, 0,222,217, 0,
+211,223,212,209,241,242,169,185,192,218,224,219,253,221,254,180,
+ 0,189,178,183,162,167,247,184, 0,168,255,251,216,248, 0, 0
+};
+static unsigned char til2_tpc2[256] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,
+112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
+ 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,164,244,157,207,149,151,245,249,230,184,155,141, 0,166,189,
+ 0,165,242,136,239,150,152,243,247,231,173,156,171,241,167,190,
+232,181,182,198,142,145,143,128,172,144,168,211,183,214,215,210,
+209,227,213,224,226,138,153,158,252,222,233,235,154,237,221,225,
+234,160,131,199,132,146,134,135,159,130,169,137,216,161,140,212,
+208,228,229,162,147,139,148,246,253,133,163,251,129,236,238,250
+};
+
+/* Encoding used by standard IBM PC vga cards to iso8859-2 */
+static unsigned char tvga_til2[256] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,
+112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
+199,252,233,226,228, 0, 0,231, 0,235, 0, 0,238, 0,196, 0,
+201, 0, 0,244,246, 0, 0, 0, 0,214,220, 0, 0, 0, 0, 0,
+225,237,243,250, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,247, 0,176, 0, 0, 0, 0, 0, 0, 0
+};
+static unsigned char til2_tvga[256] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,
+112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
+ 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,
+248, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0,142, 0, 0,128, 0,144, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0,153, 0, 0, 0, 0, 0,154, 0, 0, 0,
+ 0,160,131, 0,132, 0, 0,135, 0,130, 0,137, 0,161,140, 0,
+ 0, 0, 0,162,147, 0,148,246, 0, 0,163, 0,129, 0, 0, 0
+};
+
+//Ukrainian koi8 and win1251 converting tables by Max Veremayenko
+//(verem@tg.kiev.ua
+
+static unsigned char koi8_ukr_win1251ukr[256] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,
+112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
+128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
+144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,
+160,161,162,184,186,165,179,191,168,169,170,171,172,180,174,175,
+176,177,178,168,170,181,178,175,184,185,186,187,188,165,190,191,
+254,224,225,246,228,229,244,227,245,232,233,234,235,236,237,238,
+239,255,240,241,242,243,230,226,252,251,231,248,253,249,247,250,
+222,192,193,214,196,197,212,195,213,200,201,202,203,204,205,206,
+207,223,208,209,210,211,198,194,220,219,199,216,221,217,215,218
+};
+
+static unsigned char win1251ukr_koi8_ukr[256] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,
+112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
+128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
+144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,
+160,161,162,163,164,189,166,167,179,169,180,171,172,173,174,183,
+176,177,182,166,173,181,182,183,163,185,164,187,188,189,190,167,
+225,226,247,231,228,229,246,250,233,234,235,236,237,238,239,240,
+242,243,244,245,230,232,227,254,251,253,255,249,248,252,224,241,
+193,194,215,199,196,197,214,218,201,202,203,204,205,206,207,208,
+210,211,212,213,198,200,195,222,219,221,223,217,216,220,192,209
+};
+
+#endif /* DEFINE_ALL_CHARACTER_SETS */
+
+/****************************************************************************
+** Declare mapping variables
+****************************************************************************/
+
+
+CONVERT conv_cp1251_koi8("cp1251_koi8", cp1251_koi8, koi8_cp1251);
+#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_win1251ukr_koi8_ukr("win1251ukr_koi8_ukr", win1251ukr_koi8_ukr,
+ koi8_ukr_win1251ukr);
+CONVERT conv_koi8_ukr_win1251ukr("koi8_ukr_win1251ukr", koi8_ukr_win1251ukr,
+ win1251ukr_koi8_ukr);
+#endif /* DEFINE_ALL_CHARACTER_SETS */
+
+CONVERT *convert_tables[]= {
+ &conv_cp1251_koi8,
+#ifdef DEFINE_ALL_CHARACTER_SETS
+ &conv_cp1250_latin2,
+ &conv_kam_latin2,
+ &conv_mac_latin2,
+ &conv_macce_latin2,
+ &conv_pc2_latin2,
+ &conv_vga_latin2,
+ &conv_koi8_cp1251,
+ &conv_win1251ukr_koi8_ukr,
+ &conv_koi8_ukr_win1251ukr,
+#endif /* DEFINE_ALL_CHARACTER_SETS */
+ NULL
+};
+
+
+CONVERT *get_convert_set(const char *name)
+{
+ for (CONVERT **ptr=convert_tables ; *ptr ; ptr++)
+ {
+ if (!my_strcasecmp((*ptr)->name,name))
+ return (*ptr);
+ }
+ return 0;
+}
+
+
+void CONVERT::convert_array(const uchar *map, uchar * buf, uint len)
+{
+ for (uchar *end=buf+len ; buf != end ; buf++)
+ *buf= map[*buf];
+}
+
+
+/* This is identical as net_store_data, but with a conversion */
+
+bool CONVERT::store(String *packet,const char *from,uint length)
+{
+ uint packet_length=packet->length();
+ if (packet_length+5+length > packet->alloced_length() &&
+ packet->realloc(packet_length+5+length))
+ return 1;
+ char *to=(char*) net_store_length((char*) packet->ptr()+packet_length,
+ length);
+
+ for (const char *end=from+length ; from != end ; from++)
+ *to++= to_map[(uchar) *from];
+ packet->length((uint) (to-packet->ptr()));
+ return 0;
+}
diff --git a/sql/custom_conf.h b/sql/custom_conf.h
new file mode 100644
index 00000000000..af6012e28ec
--- /dev/null
+++ b/sql/custom_conf.h
@@ -0,0 +1,28 @@
+/* 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 __MYSQL_CUSTOM_BUILD_CONFIG__
+#define __MYSQL_CUSTOM_BUILD_CONFIG__
+
+#define MYSQL_PORT 5002
+#ifdef __WIN__
+#define MYSQL_NAMEDPIPE "SwSqlServer"
+#define MYSQL_SERVICENAME "SwSqlServer"
+#define KEY_SERVICE_PARAMETERS
+"SYSTEM\\CurrentControlSet\\Services\\SwSqlServer\\Parameters"
+#endif
+
+#endif /* __MYSQL_CUSTOM_BUILD_CONFIG__ */
diff --git a/sql/derror.cc b/sql/derror.cc
new file mode 100644
index 00000000000..3b29d2c511b
--- /dev/null
+++ b/sql/derror.cc
@@ -0,0 +1,146 @@
+/* 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 */
+
+
+/* Read language depeneded messagefile */
+
+#include "mysql_priv.h"
+#include "mysys_err.h"
+
+static void read_texts(const char *file_name,const char ***point,
+ uint error_messages);
+static void init_myfunc_errs(void);
+
+ /* Read messages from errorfile */
+
+void init_errmessage(void)
+{
+ DBUG_ENTER("init_errmessage");
+
+ read_texts(ERRMSG_FILE,&errmsg[ERRMAPP],ER_ERROR_MESSAGES);
+ errmesg=errmsg[ERRMAPP]; /* Init global variabel */
+ init_myfunc_errs(); /* Init myfunc messages */
+ DBUG_VOID_RETURN;
+}
+
+
+ /* Read text from packed textfile in language-directory */
+ /* If we can't read messagefile then it's panic- we can't continue */
+
+static void read_texts(const char *file_name,const char ***point,
+ uint error_messages)
+{
+ register uint i;
+ uint ant,funktpos,length,textant;
+ File file;
+ char name[FN_REFLEN];
+ const char *buff;
+ uchar head[32],*pos;
+ DBUG_ENTER("read_texts");
+
+ LINT_INIT(buff);
+ funktpos=0;
+ if ((file=my_open(fn_format(name,file_name,language,"",4),
+ O_RDONLY | O_SHARE | O_BINARY,
+ MYF(0))) < 0)
+ goto err; /* purecov: inspected */
+
+ funktpos=1;
+ if (my_read(file,(byte*) head,32,MYF(MY_NABP))) goto err;
+ 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);
+
+ if (ant < 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);
+ VOID(my_close(file,MYF(MY_WME)));
+ clean_up(); /* Clean_up frees everything */
+ exit(1); /* We can't continue */
+ }
+
+ x_free((gptr) *point); /* Free old language */
+ if (!(*point= (const char**)
+ my_malloc((uint) (length+ant*sizeof(char*)),MYF(0))))
+ {
+ funktpos=2; /* purecov: inspected */
+ goto err; /* purecov: inspected */
+ }
+ buff= (char*) (*point + ant);
+
+ if (my_read(file,(byte*) buff,(uint) ant*2,MYF(MY_NABP))) goto err;
+ for (i=0, pos= (uchar*) buff ; i< ant ; 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++)
+ {
+ point[i]= *point +uint2korr(head+10+i+i);
+ }
+ VOID(my_close(file,MYF(0)));
+ DBUG_VOID_RETURN;
+
+err:
+ switch (funktpos) {
+ case 2:
+ buff="\n%s: Fatal error: Not enough memory for messagefile '%s'\n\n";
+ break;
+ case 1:
+ buff="\n%s: Fatal error: Can't read from messagefile '%s'\n\n";
+ break;
+ default:
+ buff="\n%s: Fatal error: Can't find messagefile '%s'\n\n";
+ break;
+ }
+ if (file != FERR)
+ VOID(my_close(file,MYF(MY_WME)));
+ fprintf(stderr,buff,my_progname,name);
+ clean_up(); /* Clean_up frees everything */
+ exit(1); /* We can't continue */
+} /* read_texts */
+
+
+ /* Initiates error-messages used by my_func-library */
+
+static void init_myfunc_errs()
+{
+ init_glob_errs(); /* Initiate english errors */
+ if (!(specialflag & SPECIAL_ENGLISH))
+ {
+ globerrs[EE_FILENOTFOUND % ERRMOD] = ER(ER_FILE_NOT_FOUND);
+ globerrs[EE_CANTCREATEFILE % ERRMOD]= ER(ER_CANT_CREATE_FILE);
+ globerrs[EE_READ % ERRMOD] = ER(ER_ERROR_ON_READ);
+ globerrs[EE_WRITE % ERRMOD] = ER(ER_ERROR_ON_WRITE);
+ globerrs[EE_BADCLOSE % ERRMOD] = ER(ER_ERROR_ON_CLOSE);
+ globerrs[EE_OUTOFMEMORY % ERRMOD] = ER(ER_OUTOFMEMORY);
+ globerrs[EE_DELETE % ERRMOD] = ER(ER_CANT_DELETE_FILE);
+ globerrs[EE_LINK % ERRMOD] = ER(ER_ERROR_ON_RENAME);
+ globerrs[EE_EOFERR % ERRMOD] = ER(ER_UNEXPECTED_EOF);
+ globerrs[EE_CANTLOCK % ERRMOD] = ER(ER_CANT_LOCK);
+ globerrs[EE_DIR % ERRMOD] = ER(ER_CANT_READ_DIR);
+ globerrs[EE_STAT % ERRMOD] = ER(ER_CANT_GET_STAT);
+ globerrs[EE_GETWD % ERRMOD] = ER(ER_CANT_GET_WD);
+ globerrs[EE_SETWD % ERRMOD] = ER(ER_CANT_SET_WD);
+ globerrs[EE_DISK_FULL % ERRMOD] = ER(ER_DISK_FULL);
+ }
+}
diff --git a/sql/field.cc b/sql/field.cc
new file mode 100644
index 00000000000..c903ea456a5
--- /dev/null
+++ b/sql/field.cc
@@ -0,0 +1,4576 @@
+/* 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 */
+
+
+/*
+ NOTES:
+ Some of the number class uses the system functions strtol(), strtoll()...
+ To avoid patching the end \0 or copying the buffer unnecessary, all calls
+ to system functions are wrapped to a String object that adds the end null
+ if it only if it isn't there.
+ This adds some overhead when assigning numbers from strings but makes
+ everything simpler.
+ */
+
+/*****************************************************************************
+** This file implements classes defined in field.h
+*****************************************************************************/
+
+#ifdef __GNUC__
+#pragma implementation // gcc: Class implementation
+#endif
+
+#include "mysql_priv.h"
+#include "sql_select.h"
+#include <m_ctype.h>
+#include <errno.h>
+#ifdef HAVE_FCONVERT
+#include <floatingpoint.h>
+#endif
+
+/*****************************************************************************
+** Instansiate templates and static variables
+*****************************************************************************/
+
+#ifdef __GNUC__
+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
+*****************************************************************************/
+
+ /*
+ ** 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;
+ if ((diff= (int) (field_length - value->length())) > 0)
+ {
+ bmove_upp((char*) value->ptr()+field_length,value->ptr()+value->length(),
+ value->length());
+ bfill((char*) value->ptr(),diff,'0');
+ value->length(field_length);
+ (void) value->c_ptr_quick(); // Avoid warnings in purify
+ }
+}
+
+/*
+** 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)
+{
+ const char *end=str+length;
+
+ while (str != end && isspace(*str)) // Allow start space
+ str++; /* purecov: inspected */
+ if (str != end && (*str == '-' || *str == '+'))
+ str++;
+ if (str == end)
+ return 0; // Error: Empty string
+ for ( ; str != end ; str++)
+ {
+ if (!isdigit(*str))
+ {
+ if (*str == '.')
+ { // Allow '.0000'
+ for (str++ ; str != end && *str == '0'; str++) ;
+ if (str == end)
+ return 1;
+ }
+ if (!isspace(*str))
+ return 0;
+ for (str++ ; str != end ; str++)
+ if (!isspace(*str))
+ return 0;
+ return 1;
+ }
+ }
+ return 1;
+}
+
+
+static bool test_if_real(const char *str,int length)
+{
+ while (length && isspace(*str))
+ { // Allow start space
+ length--; str++;
+ }
+ if (!length)
+ return 0;
+ if (*str == '+' || *str == '-')
+ {
+ length--; str++;
+ if (!length || !(isdigit(*str) || *str == '.'))
+ return 0;
+ }
+ while (length && isdigit(*str))
+ {
+ length--; str++;
+ }
+ if (!length)
+ return 1;
+ if (*str == '.')
+ {
+ length--; str++;
+ while (length && isdigit(*str))
+ {
+ length--; str++;
+ }
+ }
+ if (!length)
+ return 1;
+ if (*str == 'E' || *str == 'e')
+ {
+ if (length < 3 || (str[1] != '+' && str[1] != '-') || !isdigit(str[2]))
+ return 0;
+ length-=3;
+ str+=3;
+ while (length && isdigit(*str))
+ {
+ length--; str++;
+ }
+ }
+ for ( ; length ; length--, str++)
+ { // Allow end space
+ if (!isspace(*str))
+ return 0;
+ }
+ return 1;
+}
+
+
+/****************************************************************************
+** Functions for the base classes
+** This is a unpacked number.
+****************************************************************************/
+
+Field::Field(char *ptr_arg,uint32 length_arg,uchar *null_ptr_arg,
+ uint 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),
+ table_name(table_arg ? table_arg->table_name : 0),
+ field_name(field_name_arg), unireg_check(unireg_check_arg),
+ field_length(length_arg)
+{
+ flags=null_ptr ? 0: NOT_NULL_FLAG;
+}
+
+uint Field::offset()
+{
+ return (uint) (ptr - (char*) table->record[0]);
+}
+
+
+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);
+ }
+}
+
+
+bool Field::send(String *packet)
+{
+ if (is_null())
+ return net_store_null(packet);
+ char buff[MAX_FIELD_WIDTH];
+ String tmp(buff,sizeof(buff));
+ val_str(&tmp,&tmp);
+ CONVERT *convert;
+ if ((convert=current_thd->convert_set))
+ return convert->store(packet,tmp.ptr(),tmp.length());
+ return net_store_data(packet,tmp.ptr(),tmp.length());
+}
+
+
+void Field_num::add_zerofill_and_unsigned(String &res) const
+{
+ res.length(strlen(res.ptr())); // Fix length
+ if (unsigned_flag)
+ res.append(" unsigned");
+ if (zerofill)
+ res.append(" zerofill");
+}
+
+void Field_num::make_field(Send_field *field)
+{
+ field->table_name=table_name;
+ field->col_name=field_name;
+ field->length=field_length;
+ field->type=type();
+ field->flags=table->maybe_null ? (flags & ~NOT_NULL_FLAG) : flags;
+ field->decimals=dec;
+}
+
+
+void Field_str::make_field(Send_field *field)
+{
+ field->table_name=table_name;
+ field->col_name=field_name;
+ field->length=field_length;
+ field->type=type();
+ field->flags=table->maybe_null ? (flags & ~NOT_NULL_FLAG) : flags;
+ field->decimals=0;
+}
+
+
+uint Field::fill_cache_field(CACHE_FIELD *copy)
+{
+ copy->str=ptr;
+ copy->length=pack_length();
+ copy->blob_field=0;
+ if (flags & BLOB_FLAG)
+ {
+ copy->blob_field=(Field_blob*) this;
+ copy->strip=0;
+ copy->length-=table->blob_ptr_size;
+ return copy->length;
+ }
+ else if (!zero_pack() && (type() == FIELD_TYPE_STRING && copy->length > 4 ||
+ type() == FIELD_TYPE_VAR_STRING))
+ copy->strip=1; /* Remove end space */
+ else
+ copy->strip=0;
+ return copy->length+(int) copy->strip;
+}
+
+bool Field::get_date(TIME *ltime,bool fuzzydate)
+{
+ char buff[40];
+ String tmp(buff,sizeof(buff)),tmp2,*res;
+ if (!(res=val_str(&tmp,&tmp2)) ||
+ str_to_TIME(res->ptr(),res->length(),ltime,fuzzydate) == TIMESTAMP_NONE)
+ return 1;
+ return 0;
+}
+
+bool Field::get_time(TIME *ltime)
+{
+ char buff[40];
+ String tmp(buff,sizeof(buff)),tmp2,*res;
+ if (!(res=val_str(&tmp,&tmp2)) ||
+ str_to_time(res->ptr(),res->length(),ltime) == TIMESTAMP_NONE)
+ return 1;
+ return 0;
+}
+
+
+/* This is called when storing a date in a string */
+void Field::store_time(TIME *ltime,timestamp_type type)
+{
+ char buff[25];
+ switch (type) {
+ case TIMESTAMP_NONE:
+ store("",0); // Probably an error
+ break;
+ case TIMESTAMP_DATE:
+ sprintf(buff,"%04d-%02d-%02d", ltime->year,ltime->month,ltime->day);
+ store(buff,10);
+ break;
+ case TIMESTAMP_FULL:
+ sprintf(buff,"%04d-%02d-%02d %02d:%02d:%02d",
+ ltime->year,ltime->month,ltime->day,
+ ltime->hour,ltime->minute,ltime->second);
+ store(buff,19);
+ break;
+ case TIMESTAMP_TIME:
+ sprintf(buff, "%02d:%02d:%02d",
+ ltime->hour,ltime->minute,ltime->second);
+ store(buff,strlen(buff));
+ break;
+ }
+}
+
+
+bool Field::optimize_range()
+{
+ return test(table->file->option_flag() & HA_READ_NEXT);
+}
+
+/****************************************************************************
+** Functions for the Field_decimal class
+** This is a unpacked number.
+****************************************************************************/
+
+void
+Field_decimal::reset(void)
+{
+ Field_decimal::store("0",1);
+}
+
+void Field_decimal::overflow(bool negative)
+{
+ uint len=field_length;
+ char *to=ptr;
+ if (negative && !unsigned_flag)
+ {
+ *to++ = '-';
+ len--;
+ }
+ bfill(to,len,negative && unsigned_flag ? '0' : '9');
+ if (dec)
+ ptr[field_length-dec-1]='.';
+ return;
+}
+
+
+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;
+
+ if ((tmp_dec= dec))
+ tmp_dec++; // Calculate pos of '.'
+ while (from != end && isspace(*from))
+ from++;
+ if (zerofill)
+ {
+ fyllchar = '0';
+ if (from != end)
+ while (*from == '0' && from != end-1) // Skipp prezero
+ from++;
+ }
+ else
+ fyllchar=' ';
+ error=number_dec(&decstr,from,end);
+ if (decstr.sign)
+ {
+ from++;
+ if (unsigned_flag) // No sign with zerofill
+ {
+ if (!error)
+ current_thd->cuted_fields++;
+ Field_decimal::overflow(1);
+ return;
+ }
+ }
+ /*
+ ** Remove pre-zeros if too big number
+ */
+ for (i= (int) (decstr.nr_length+decstr.extra -(field_length-tmp_dec)+
+ decstr.sign) ;
+ i > 0 ;
+ i--)
+ {
+ if (*from == '0')
+ {
+ from++;
+ decstr.nr_length--;
+ continue;
+ }
+ if (decstr.sign && decstr.sign_char == '+' && i == 1)
+ { // Remove pre '+'
+ decstr.sign=0;
+ break;
+ }
+ current_thd->cuted_fields++;
+ // too big number, change to max or min number
+ Field_decimal::overflow(decstr.sign && decstr.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';
+ }
+
+ /*
+ ** Check for incorrect string if in batch mode (ALTER TABLE/LOAD DATA...)
+ */
+ if (!error && current_thd->count_cuted_fields && from != end)
+ { // Check if number was cuted
+ for (; from != end ; from++)
+ {
+ if (*from != '0')
+ {
+ if (!isspace(*from)) // Space is ok
+ current_thd->cuted_fields++;
+ break;
+ }
+ }
+ }
+}
+
+
+void Field_decimal::store(double nr)
+{
+ if (unsigned_flag && nr < 0)
+ {
+ overflow(1);
+ current_thd->cuted_fields++;
+ return;
+ }
+ reg4 uint i,length;
+ char fyllchar,*to;
+ char buff[320];
+
+ fyllchar = zerofill ? (char) '0' : (char) ' ';
+ sprintf(buff,"%.*f",dec,nr);
+ length=strlen(buff);
+
+ if (length > field_length)
+ {
+ overflow(nr < 0.0);
+ current_thd->cuted_fields++;
+ }
+ else
+ {
+ to=ptr;
+ for (i=field_length-length ; i-- > 0 ;)
+ *to++ = fyllchar;
+ memcpy(to,buff,length);
+ }
+}
+
+
+void Field_decimal::store(longlong nr)
+{
+ if (unsigned_flag && nr < 0)
+ {
+ overflow(1);
+ current_thd->cuted_fields++;
+ return;
+ }
+ char buff[22];
+ uint length=(uint) (longlong10_to_str(nr,buff,-10)-buff);
+ 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) ' ';
+ char *to=ptr;
+ for (uint i=int_part-length ; i-- > 0 ;)
+ *to++ = fyllchar;
+ memcpy(to,buff,length);
+ if (dec)
+ {
+ to[length]='.';
+ bfill(to+length+1,dec,'0');
+ }
+ }
+}
+
+
+double Field_decimal::val_real(void)
+{
+ char temp= *(ptr+field_length); *(ptr+field_length) = '\0';
+ double nr=atod(ptr);
+ *(ptr+field_length)=temp;
+ return(nr);
+}
+
+longlong Field_decimal::val_int(void)
+{
+ char temp= *(ptr+field_length); *(ptr+field_length) = '\0';
+ longlong nr;
+ if (unsigned_flag)
+ nr=(longlong) strtoull(ptr,NULL,10);
+ else
+ nr=strtoll(ptr,NULL,10);
+ *(ptr+field_length)=temp;
+ return(nr);
+}
+
+String *Field_decimal::val_str(String *val_buffer __attribute__((unused)),
+ String *val_ptr)
+{
+ char *str;
+ for (str=ptr ; *str == ' ' ; str++) ;
+ uint tmp_length=(uint) (str-ptr);
+ if (field_length < tmp_length) // Error in data
+ val_ptr->length(0);
+ else
+ val_ptr->set((const char*) str,field_length-tmp_length);
+ return val_ptr;
+}
+
+/*
+** Should be able to handle at least the following fixed decimal formats:
+** 5.00 , -1.0, 05, -05, +5 with optional pre/end space
+*/
+
+int Field_decimal::cmp(const char *a_ptr,const char *b_ptr)
+{
+ const char *end;
+ /* First remove prefixes '0', ' ', and '-' */
+ for (end=a_ptr+field_length;
+ a_ptr != end &&
+ (*a_ptr == *b_ptr ||
+ ((isspace(*a_ptr) || *a_ptr == '+' || *a_ptr == '0') &&
+ (isspace(*b_ptr) || *b_ptr == '+' || *b_ptr == '0')));
+ a_ptr++,b_ptr++) ;
+
+ if (a_ptr == end)
+ return 0;
+ int swap=0;
+ if (*a_ptr == '-')
+ {
+ if (*b_ptr != '-')
+ return -1;
+ swap= -1 ^ 1; // Swap result
+ a_ptr++, b_ptr++;
+ } else if (*b_ptr == '-')
+ return 1;
+
+ while (a_ptr != end)
+ {
+ if (*a_ptr++ != *b_ptr++)
+ return swap ^ (a_ptr[-1] < b_ptr[-1] ? -1 : 1); // compare digits
+ }
+ return 0;
+}
+
+
+void Field_decimal::sort_string(char *to,uint length)
+{
+ char *str,*end;
+ for (str=ptr,end=ptr+length;
+ str != end &&
+ ((isspace(*str) || *str == '+' || *str == '0')) ;
+
+ str++)
+ *to++=' ';
+ if (str == end)
+ return; /* purecov: inspected */
+
+ if (*str == '-')
+ {
+ *to++=1; // Smaller than any number
+ str++;
+ while (str != end)
+ if (isdigit(*str))
+ *to++= (char) ('9' - *str++);
+ else
+ *to++= *str++;
+ }
+ else memcpy(to,str,(uint) (end-str));
+}
+
+void Field_decimal::sql_type(String &res) const
+{
+ uint tmp=field_length;
+ if (!unsigned_flag)
+ tmp--;
+ if (dec)
+ tmp--;
+ sprintf((char*) res.ptr(),"decimal(%d,%d)",tmp,dec);
+ add_zerofill_and_unsigned(res);
+}
+
+
+/****************************************************************************
+** tiny int
+****************************************************************************/
+
+void Field_tiny::store(const char *from,uint len)
+{
+ String tmp_str(from,len);
+ long tmp= strtol(tmp_str.c_ptr(),NULL,10);
+
+ if (unsigned_flag)
+ {
+ if (tmp < 0)
+ {
+ tmp=0; /* purecov: inspected */
+ current_thd->cuted_fields++; /* purecov: inspected */
+ }
+ else if (tmp > 255)
+ {
+ tmp= 255;
+ current_thd->cuted_fields++;
+ }
+ else if (current_thd->count_cuted_fields && !test_if_int(from,len))
+ current_thd->cuted_fields++;
+ }
+ else
+ {
+ if (tmp < -128)
+ {
+ tmp= -128;
+ current_thd->cuted_fields++;
+ }
+ else if (tmp >= 128)
+ {
+ tmp= 127;
+ current_thd->cuted_fields++;
+ }
+ else if (current_thd->count_cuted_fields && !test_if_int(from,len))
+ current_thd->cuted_fields++;
+ }
+ ptr[0]= (char) tmp;
+}
+
+
+void Field_tiny::store(double nr)
+{
+ nr=rint(nr);
+ if (unsigned_flag)
+ {
+ if (nr < 0.0)
+ {
+ *ptr=0;
+ current_thd->cuted_fields++;
+ }
+ else if (nr > 255.0)
+ {
+ *ptr=(char) 255;
+ current_thd->cuted_fields++;
+ }
+ else
+ *ptr=(char) nr;
+ }
+ else
+ {
+ if (nr < -128.0)
+ {
+ *ptr= (char) -128;
+ current_thd->cuted_fields++;
+ }
+ else if (nr > 127.0)
+ {
+ *ptr=127;
+ current_thd->cuted_fields++;
+ }
+ else
+ *ptr=(char) nr;
+ }
+}
+
+void Field_tiny::store(longlong nr)
+{
+ if (unsigned_flag)
+ {
+ if (nr < 0L)
+ {
+ *ptr=0;
+ current_thd->cuted_fields++;
+ }
+ else if (nr > 255L)
+ {
+ *ptr= (char) 255;
+ current_thd->cuted_fields++;
+ }
+ else
+ *ptr=(char) nr;
+ }
+ else
+ {
+ if (nr < -128L)
+ {
+ *ptr= (char) -128;
+ current_thd->cuted_fields++;
+ }
+ else if (nr > 127L)
+ {
+ *ptr=127;
+ current_thd->cuted_fields++;
+ }
+ else
+ *ptr=(char) nr;
+ }
+}
+
+
+double Field_tiny::val_real(void)
+{
+ int tmp= unsigned_flag ? (int) ((uchar*) ptr)[0] :
+ (int) ((signed char*) ptr)[0];
+ return (double) tmp;
+}
+
+longlong Field_tiny::val_int(void)
+{
+ int tmp= unsigned_flag ? (int) ((uchar*) ptr)[0] :
+ (int) ((signed char*) ptr)[0];
+ return (longlong) tmp;
+}
+
+String *Field_tiny::val_str(String *val_buffer,
+ String *val_ptr __attribute__((unused)))
+{
+ uint length;
+ val_buffer->alloc(max(field_length+1,5));
+ char *to=(char*) val_buffer->ptr();
+ if (unsigned_flag)
+ length= (uint) (int10_to_str((long) *((uchar*) ptr),to,10)-to);
+ else
+ length=(int10_to_str((long) *((signed char*) ptr),to,-10)-to);
+ val_buffer->length(length);
+ if (zerofill)
+ prepend_zeros(val_buffer);
+ return val_buffer;
+}
+
+
+int Field_tiny::cmp(const char *a_ptr, const char *b_ptr)
+{
+ signed char a,b;
+ a=(signed char) a_ptr[0]; b= (signed char) b_ptr[0];
+ if (unsigned_flag)
+ return ((uchar) a < (uchar) b) ? -1 : ((uchar) a > (uchar) b) ? 1 : 0;
+ return (a < b) ? -1 : (a > b) ? 1 : 0;
+}
+
+void Field_tiny::sort_string(char *to,uint length __attribute__((unused)))
+{
+ if (unsigned_flag)
+ *to= *ptr;
+ else
+ to[0] = (char) ((uchar) ptr[0] ^ (uchar) 128); /* Revers signbit */
+}
+
+void Field_tiny::sql_type(String &res) const
+{
+ sprintf((char*) res.ptr(),"tinyint(%d)",(int) field_length);
+ add_zerofill_and_unsigned(res);
+}
+
+/****************************************************************************
+** short int
+****************************************************************************/
+
+
+// Note: Sometimes this should be fixed to use one strtol() to use
+// len and check for garbage after number.
+
+void Field_short::store(const char *from,uint len)
+{
+ String tmp_str(from,len);
+ long tmp= strtol(tmp_str.c_ptr(),NULL,10);
+ if (unsigned_flag)
+ {
+ if (tmp < 0)
+ {
+ tmp=0;
+ current_thd->cuted_fields++;
+ }
+ else if (tmp > (uint16) ~0)
+ {
+ tmp=(uint16) ~0;
+ current_thd->cuted_fields++;
+ }
+ else if (current_thd->count_cuted_fields && !test_if_int(from,len))
+ current_thd->cuted_fields++;
+ }
+ else
+ {
+ if (tmp < INT_MIN16)
+ {
+ tmp= INT_MIN16;
+ current_thd->cuted_fields++;
+ }
+ else if (tmp > INT_MAX16)
+ {
+ tmp=INT_MAX16;
+ current_thd->cuted_fields++;
+ }
+ else if (current_thd->count_cuted_fields && !test_if_int(from,len))
+ current_thd->cuted_fields++;
+ }
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ int2store(ptr,tmp);
+ }
+ else
+#endif
+ shortstore(ptr,(short) tmp);
+}
+
+
+void Field_short::store(double nr)
+{
+ int16 res;
+ nr=rint(nr);
+ if (unsigned_flag)
+ {
+ if (nr < 0)
+ {
+ res=0;
+ current_thd->cuted_fields++;
+ }
+ else if (nr > (double) (uint16) ~0)
+ {
+ res=(int16) (uint16) ~0;
+ current_thd->cuted_fields++;
+ }
+ else
+ res=(int16) (uint16) nr;
+ }
+ else
+ {
+ if (nr < (double) INT_MIN16)
+ {
+ res=INT_MIN16;
+ current_thd->cuted_fields++;
+ }
+ else if (nr > (double) INT_MAX16)
+ {
+ res=INT_MAX16;
+ current_thd->cuted_fields++;
+ }
+ else
+ res=(int16) nr;
+ }
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ int2store(ptr,res);
+ }
+ else
+#endif
+ shortstore(ptr,res);
+}
+
+void Field_short::store(longlong nr)
+{
+ int16 res;
+ if (unsigned_flag)
+ {
+ if (nr < 0L)
+ {
+ res=0;
+ current_thd->cuted_fields++;
+ }
+ else if (nr > (longlong) (uint16) ~0)
+ {
+ res=(int16) (uint16) ~0;
+ current_thd->cuted_fields++;
+ }
+ else
+ res=(int16) (uint16) nr;
+ }
+ else
+ {
+ if (nr < INT_MIN16)
+ {
+ res=INT_MIN16;
+ current_thd->cuted_fields++;
+ }
+ else if (nr > INT_MAX16)
+ {
+ res=INT_MAX16;
+ current_thd->cuted_fields++;
+ }
+ else
+ res=(int16) nr;
+ }
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ int2store(ptr,res);
+ }
+ else
+#endif
+ shortstore(ptr,res);
+}
+
+
+double Field_short::val_real(void)
+{
+ short j;
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ j=sint2korr(ptr);
+ else
+#endif
+ shortget(j,ptr);
+ return unsigned_flag ? (double) (unsigned short) j : (double) j;
+}
+
+longlong Field_short::val_int(void)
+{
+ short j;
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ j=sint2korr(ptr);
+ else
+#endif
+ shortget(j,ptr);
+ return unsigned_flag ? (longlong) (unsigned short) j : (longlong) j;
+}
+
+String *Field_short::val_str(String *val_buffer,
+ String *val_ptr __attribute__((unused)))
+{
+ uint length;
+ val_buffer->alloc(max(field_length+1,7));
+ char *to=(char*) val_buffer->ptr();
+ short j;
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ j=sint2korr(ptr);
+ else
+#endif
+ shortget(j,ptr);
+
+ if (unsigned_flag)
+ length=(uint) (int10_to_str((long) (uint16) j,to,10)-to);
+ else
+ length=(uint) (int10_to_str((long) j,to,-10)-to);
+ val_buffer->length(length);
+ if (zerofill)
+ prepend_zeros(val_buffer);
+ return val_buffer;
+}
+
+
+int Field_short::cmp(const char *a_ptr, const char *b_ptr)
+{
+ short a,b;
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ a=sint2korr(a_ptr);
+ b=sint2korr(b_ptr);
+ }
+ else
+#endif
+ {
+ shortget(a,a_ptr);
+ shortget(b,b_ptr);
+ }
+
+ if (unsigned_flag)
+ return ((unsigned short) a < (unsigned short) b) ? -1 :
+ ((unsigned short) a > (unsigned short) b) ? 1 : 0;
+ return (a < b) ? -1 : (a > b) ? 1 : 0;
+}
+
+void Field_short::sort_string(char *to,uint length __attribute__((unused)))
+{
+#ifdef WORDS_BIGENDIAN
+ if (!table->db_low_byte_first)
+ {
+ if (unsigned_flag)
+ to[0] = ptr[0];
+ else
+ to[0] = ptr[0] ^ 128; /* Revers signbit */
+ to[1] = ptr[1];
+ }
+ else
+#endif
+ {
+ if (unsigned_flag)
+ to[0] = ptr[1];
+ else
+ to[0] = ptr[1] ^ 128; /* Revers signbit */
+ to[1] = ptr[0];
+ }
+}
+
+void Field_short::sql_type(String &res) const
+{
+ sprintf((char*) res.ptr(),"smallint(%d)",(int) field_length);
+ add_zerofill_and_unsigned(res);
+}
+
+
+/****************************************************************************
+** medium int
+****************************************************************************/
+
+// Note: Sometimes this should be fixed to use one strtol() to use
+// len and check for garbage after number.
+
+void Field_medium::store(const char *from,uint len)
+{
+ String tmp_str(from,len);
+ long tmp= strtol(tmp_str.c_ptr(),NULL,10);
+
+ if (unsigned_flag)
+ {
+ if (tmp < 0)
+ {
+ tmp=0;
+ current_thd->cuted_fields++;
+ }
+ else if (tmp >= (long) (1L << 24))
+ {
+ tmp=(long) (1L << 24)-1L;
+ current_thd->cuted_fields++;
+ }
+ else if (current_thd->count_cuted_fields && !test_if_int(from,len))
+ current_thd->cuted_fields++;
+ }
+ else
+ {
+ if (tmp < INT_MIN24)
+ {
+ tmp= INT_MIN24;
+ current_thd->cuted_fields++;
+ }
+ else if (tmp > INT_MAX24)
+ {
+ tmp=INT_MAX24;
+ current_thd->cuted_fields++;
+ }
+ else if (current_thd->count_cuted_fields && !test_if_int(from,len))
+ current_thd->cuted_fields++;
+ }
+
+ int3store(ptr,tmp);
+}
+
+
+void Field_medium::store(double nr)
+{
+ nr=rint(nr);
+ if (unsigned_flag)
+ {
+ if (nr < 0)
+ {
+ int3store(ptr,0);
+ current_thd->cuted_fields++;
+ }
+ else if (nr >= (double) (long) (1L << 24))
+ {
+ ulong tmp=(ulong) (1L << 24)-1L;
+ int3store(ptr,tmp);
+ current_thd->cuted_fields++;
+ }
+ else
+ int3store(ptr,(ulong) nr);
+ }
+ else
+ {
+ if (nr < (double) INT_MIN24)
+ {
+ long tmp=(long) INT_MIN24;
+ int3store(ptr,tmp);
+ current_thd->cuted_fields++;
+ }
+ else if (nr > (double) INT_MAX24)
+ {
+ long tmp=(long) INT_MAX24;
+ int3store(ptr,tmp);
+ current_thd->cuted_fields++;
+ }
+ else
+ int3store(ptr,(long) nr);
+ }
+}
+
+void Field_medium::store(longlong nr)
+{
+ if (unsigned_flag)
+ {
+ if (nr < 0L)
+ {
+ int3store(ptr,0);
+ current_thd->cuted_fields++;
+ }
+ else if (nr >= (longlong) (long) (1L << 24))
+ {
+ long tmp=(long) (1L << 24)-1L;;
+ int3store(ptr,tmp);
+ current_thd->cuted_fields++;
+ }
+ else
+ int3store(ptr,(ulong) nr);
+ }
+ else
+ {
+ if (nr < (longlong) INT_MIN24)
+ {
+ long tmp=(long) INT_MIN24;
+ int3store(ptr,tmp);
+ current_thd->cuted_fields++;
+ }
+ else if (nr > (longlong) INT_MAX24)
+ {
+ long tmp=(long) INT_MAX24;
+ int3store(ptr,tmp);
+ current_thd->cuted_fields++;
+ }
+ else
+ int3store(ptr,(long) nr);
+ }
+}
+
+
+double Field_medium::val_real(void)
+{
+ long j= unsigned_flag ? (long) uint3korr(ptr) : sint3korr(ptr);
+ return (double) j;
+}
+
+longlong Field_medium::val_int(void)
+{
+ long j= unsigned_flag ? (long) uint3korr(ptr) : sint3korr(ptr);
+ return (longlong) j;
+}
+
+String *Field_medium::val_str(String *val_buffer,
+ String *val_ptr __attribute__((unused)))
+{
+ uint length;
+ val_buffer->alloc(max(field_length+1,10));
+ char *to=(char*) val_buffer->ptr();
+ long j= unsigned_flag ? (long) uint3korr(ptr) : sint3korr(ptr);
+
+ length=(uint) (int10_to_str(j,to,-10)-to);
+ val_buffer->length(length);
+ if (zerofill)
+ prepend_zeros(val_buffer); /* purecov: inspected */
+ return val_buffer;
+}
+
+
+int Field_medium::cmp(const char *a_ptr, const char *b_ptr)
+{
+ long a,b;
+ if (unsigned_flag)
+ {
+ a=uint3korr(a_ptr);
+ b=uint3korr(b_ptr);
+ }
+ else
+ {
+ a=sint3korr(a_ptr);
+ b=sint3korr(b_ptr);
+ }
+ return (a < b) ? -1 : (a > b) ? 1 : 0;
+}
+
+void Field_medium::sort_string(char *to,uint length __attribute__((unused)))
+{
+ if (unsigned_flag)
+ to[0] = ptr[2];
+ else
+ to[0] = (uchar) (ptr[2] ^ 128); /* Revers signbit */
+ to[1] = ptr[1];
+ to[2] = ptr[0];
+}
+
+
+void Field_medium::sql_type(String &res) const
+{
+ sprintf((char*) res.ptr(),"mediumint(%d)",(int) field_length);
+ add_zerofill_and_unsigned(res);
+}
+
+/****************************************************************************
+** long int
+****************************************************************************/
+
+
+// Note: Sometimes this should be fixed to use one strtol() to use
+// len and check for garbage after number.
+
+void Field_long::store(const char *from,uint len)
+{
+ while (len && isspace(*from))
+ {
+ len--; from++;
+ }
+ long tmp;
+ String tmp_str(from,len);
+ errno=0;
+ if (unsigned_flag)
+ {
+ if (!len || *from == '-')
+ {
+ tmp=0; // Set negative to 0
+ errno=ERANGE;
+ }
+ else
+ tmp=(long) strtoul(tmp_str.c_ptr(),NULL,10);
+ }
+ else
+ tmp=strtol(tmp_str.c_ptr(),NULL,10);
+ if (errno || current_thd->count_cuted_fields && !test_if_int(from,len))
+ current_thd->cuted_fields++;
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ int4store(ptr,tmp);
+ }
+ else
+#endif
+ longstore(ptr,tmp);
+}
+
+
+void Field_long::store(double nr)
+{
+ int32 res;
+ nr=rint(nr);
+ if (unsigned_flag)
+ {
+ if (nr < 0)
+ {
+ res=0;
+ current_thd->cuted_fields++;
+ }
+ else if (nr > (double) (ulong) ~0L)
+ {
+ res=(int32) (uint32) ~0L;
+ current_thd->cuted_fields++;
+ }
+ else
+ res=(int32) (ulong) nr;
+ }
+ else
+ {
+ if (nr < (double) INT_MIN32)
+ {
+ res=(int32) INT_MIN32;
+ current_thd->cuted_fields++;
+ }
+ else if (nr > (double) INT_MAX32)
+ {
+ res=(int32) INT_MAX32;
+ current_thd->cuted_fields++;
+ }
+ else
+ res=(int32) nr;
+ }
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ int4store(ptr,res);
+ }
+ else
+#endif
+ longstore(ptr,res);
+}
+
+
+void Field_long::store(longlong nr)
+{
+ int32 res;
+ if (unsigned_flag)
+ {
+ if (nr < 0)
+ {
+ res=0;
+ current_thd->cuted_fields++;
+ }
+ else if (nr >= (LL(1) << 32))
+ {
+ res=(int32) (uint32) ~0L;
+ current_thd->cuted_fields++;
+ }
+ else
+ res=(int32) (uint32) nr;
+ }
+ else
+ {
+ if (nr < (longlong) INT_MIN32)
+ {
+ res=(int32) INT_MIN32;
+ current_thd->cuted_fields++;
+ }
+ else if (nr > (longlong) INT_MAX32)
+ {
+ res=(int32) INT_MAX32;
+ current_thd->cuted_fields++;
+ }
+ else
+ res=(int32) nr;
+ }
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ int4store(ptr,res);
+ }
+ else
+#endif
+ longstore(ptr,res);
+}
+
+
+double Field_long::val_real(void)
+{
+ int32 j;
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ j=sint4korr(ptr);
+ else
+#endif
+ longget(j,ptr);
+ return unsigned_flag ? (double) (uint32) j : (double) j;
+}
+
+longlong Field_long::val_int(void)
+{
+ int32 j;
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ j=sint4korr(ptr);
+ else
+#endif
+ longget(j,ptr);
+ return unsigned_flag ? (longlong) (uint32) j : (longlong) j;
+}
+
+String *Field_long::val_str(String *val_buffer,
+ String *val_ptr __attribute__((unused)))
+{
+ uint length;
+ val_buffer->alloc(max(field_length+1,12));
+ char *to=(char*) val_buffer->ptr();
+ int32 j;
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ j=sint4korr(ptr);
+ else
+#endif
+ longget(j,ptr);
+
+ length=(uint) (int10_to_str((unsigned_flag ? (long) (uint32) j : (long) j),
+ to,
+ unsigned_flag ? 10 : -10)-to);
+ val_buffer->length(length);
+ if (zerofill)
+ prepend_zeros(val_buffer);
+ return val_buffer;
+}
+
+
+int Field_long::cmp(const char *a_ptr, const char *b_ptr)
+{
+ int32 a,b;
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ a=sint4korr(a_ptr);
+ b=sint4korr(b_ptr);
+ }
+ else
+#endif
+ {
+ longget(a,a_ptr);
+ longget(b,b_ptr);
+ }
+ if (unsigned_flag)
+ return ((ulong) a < (ulong) b) ? -1 : ((ulong) a > (ulong) b) ? 1 : 0;
+ return (a < b) ? -1 : (a > b) ? 1 : 0;
+}
+
+void Field_long::sort_string(char *to,uint length __attribute__((unused)))
+{
+#ifdef WORDS_BIGENDIAN
+ if (!table->db_low_byte_first)
+ {
+ if (unsigned_flag)
+ to[0] = ptr[0];
+ else
+ to[0] = ptr[0] ^ 128; /* Revers signbit */
+ to[1] = ptr[1];
+ to[2] = ptr[2];
+ to[3] = ptr[3];
+ }
+ else
+#endif
+ {
+ if (unsigned_flag)
+ to[0] = ptr[3];
+ else
+ to[0] = ptr[3] ^ 128; /* Revers signbit */
+ to[1] = ptr[2];
+ to[2] = ptr[1];
+ to[3] = ptr[0];
+ }
+}
+
+
+void Field_long::sql_type(String &res) const
+{
+ sprintf((char*) res.ptr(),"int(%d)",(int) field_length);
+ add_zerofill_and_unsigned(res);
+}
+
+/****************************************************************************
+** longlong int
+****************************************************************************/
+
+void Field_longlong::store(const char *from,uint len)
+{
+ while (len && isspace(*from))
+ { // For easy error check
+ len--; from++;
+ }
+ longlong tmp;
+ String tmp_str(from,len);
+ errno=0;
+ if (unsigned_flag)
+ {
+ if (!len || *from == '-')
+ {
+ tmp=0; // Set negative to 0
+ errno=ERANGE;
+ }
+ else
+ tmp=(longlong) strtoull(tmp_str.c_ptr(),NULL,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++;
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ int8store(ptr,tmp);
+ }
+ else
+#endif
+ longlongstore(ptr,tmp);
+}
+
+
+void Field_longlong::store(double nr)
+{
+ longlong res;
+ nr=rint(nr);
+ if (unsigned_flag)
+ {
+ if (nr < 0)
+ {
+ res=0;
+ current_thd->cuted_fields++;
+ }
+ else if (nr >= (double) ~ (ulonglong) 0)
+ {
+ res= ~(longlong) 0;
+ current_thd->cuted_fields++;
+ }
+ else
+ res=(longlong) (ulonglong) nr;
+ }
+ else
+ {
+ if (nr <= (double) LONGLONG_MIN)
+ {
+ res=(longlong) LONGLONG_MIN;
+ current_thd->cuted_fields++;
+ }
+ else if (nr >= (double) LONGLONG_MAX)
+ {
+ res=(longlong) LONGLONG_MAX;
+ current_thd->cuted_fields++;
+ }
+ else
+ res=(longlong) nr;
+ }
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ int8store(ptr,res);
+ }
+ else
+#endif
+ longlongstore(ptr,res);
+}
+
+
+void Field_longlong::store(longlong nr)
+{
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ int8store(ptr,nr);
+ }
+ else
+#endif
+ longlongstore(ptr,nr);
+}
+
+
+double Field_longlong::val_real(void)
+{
+ longlong j;
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ j=sint8korr(ptr);
+ }
+ else
+#endif
+ longlongget(j,ptr);
+ return unsigned_flag ? ulonglong2double(j) : (double) j;
+}
+
+longlong Field_longlong::val_int(void)
+{
+ longlong j;
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ j=sint8korr(ptr);
+ else
+#endif
+ longlongget(j,ptr);
+ return j;
+}
+
+
+String *Field_longlong::val_str(String *val_buffer,
+ String *val_ptr __attribute__((unused)))
+{
+ uint length;
+ val_buffer->alloc(max(field_length+1,22));
+ char *to=(char*) val_buffer->ptr();
+ longlong j;
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ j=sint8korr(ptr);
+ else
+#endif
+ longlongget(j,ptr);
+
+ length=(uint) (longlong10_to_str(j,to,unsigned_flag ? 10 : -10)-to);
+ val_buffer->length(length);
+ if (zerofill)
+ prepend_zeros(val_buffer);
+ return val_buffer;
+}
+
+
+int Field_longlong::cmp(const char *a_ptr, const char *b_ptr)
+{
+ longlong a,b;
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ a=sint8korr(a_ptr);
+ b=sint8korr(b_ptr);
+ }
+ else
+#endif
+ {
+ longlongget(a,a_ptr);
+ longlongget(b,b_ptr);
+ }
+ if (unsigned_flag)
+ return ((ulonglong) a < (ulonglong) b) ? -1 :
+ ((ulonglong) a > (ulonglong) b) ? 1 : 0;
+ return (a < b) ? -1 : (a > b) ? 1 : 0;
+}
+
+void Field_longlong::sort_string(char *to,uint length __attribute__((unused)))
+{
+#ifdef WORDS_BIGENDIAN
+ if (!table->db_low_byte_first)
+ {
+ if (unsigned_flag)
+ to[0] = ptr[0];
+ else
+ to[0] = ptr[0] ^ 128; /* Revers signbit */
+ to[1] = ptr[1];
+ to[2] = ptr[2];
+ to[3] = ptr[3];
+ to[4] = ptr[4];
+ to[5] = ptr[5];
+ to[6] = ptr[6];
+ to[7] = ptr[7];
+ }
+ else
+#endif
+ {
+ if (unsigned_flag)
+ to[0] = ptr[7];
+ else
+ to[0] = ptr[7] ^ 128; /* Revers signbit */
+ to[1] = ptr[6];
+ to[2] = ptr[5];
+ to[3] = ptr[4];
+ to[4] = ptr[3];
+ to[5] = ptr[2];
+ to[6] = ptr[1];
+ to[7] = ptr[0];
+ }
+}
+
+
+void Field_longlong::sql_type(String &res) const
+{
+ sprintf((char*) res.ptr(),"bigint(%d)",(int) field_length);
+ add_zerofill_and_unsigned(res);
+}
+
+/****************************************************************************
+** single precision float
+****************************************************************************/
+
+void Field_float::store(const char *from,uint len)
+{
+ String tmp_str(from,len);
+ errno=0;
+ Field_float::store(atof(tmp_str.c_ptr()));
+ if (errno || current_thd->count_cuted_fields && !test_if_real(from,len))
+ current_thd->cuted_fields++;
+}
+
+
+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 (nr < -FLT_MAX)
+ {
+ j= -FLT_MAX;
+ current_thd->cuted_fields++;
+ }
+ else if (nr > FLT_MAX)
+ {
+ j=FLT_MAX;
+ current_thd->cuted_fields++;
+ }
+ else
+ j= (float) nr;
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ float4store(ptr,j);
+ }
+ else
+#endif
+ memcpy_fixed(ptr,(byte*) &j,sizeof(j));
+}
+
+
+void Field_float::store(longlong nr)
+{
+ float j= (float) nr;
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ float4store(ptr,j);
+ }
+ else
+#endif
+ memcpy_fixed(ptr,(byte*) &j,sizeof(j));
+}
+
+
+double Field_float::val_real(void)
+{
+ float j;
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ float4get(j,ptr);
+ }
+ else
+#endif
+ memcpy_fixed((byte*) &j,ptr,sizeof(j));
+ return ((double) j);
+}
+
+longlong Field_float::val_int(void)
+{
+ float j;
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ float4get(j,ptr);
+ }
+ else
+#endif
+ memcpy_fixed((byte*) &j,ptr,sizeof(j));
+ return ((longlong) j);
+}
+
+
+String *Field_float::val_str(String *val_buffer,
+ String *val_ptr __attribute__((unused)))
+{
+ float nr;
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ float4get(nr,ptr);
+ }
+ else
+#endif
+ memcpy_fixed((byte*) &nr,ptr,sizeof(nr));
+
+ val_buffer->alloc(max(field_length,70));
+ char *to=(char*) val_buffer->ptr();
+
+ if (dec >= NOT_FIXED_DEC)
+ {
+ sprintf(to,"%-*.*g",(int) field_length,FLT_DIG,nr);
+ to=strcend(to,' ');
+ *to=0;
+ }
+ else
+ {
+#ifdef HAVE_FCONVERT
+ char buff[70],*pos=buff;
+ int decpt,sign,tmp_dec=dec;
+
+ VOID(sfconvert(&nr,tmp_dec,&decpt,&sign,buff));
+ if (sign)
+ {
+ *to++='-';
+ }
+ if (decpt < 0)
+ { /* val_buffer is < 0 */
+ *to++='0';
+ if (!tmp_dec)
+ goto end;
+ *to++='.';
+ if (-decpt > tmp_dec)
+ decpt= - (int) tmp_dec;
+ tmp_dec=(uint) ((int) tmp_dec+decpt);
+ while (decpt++ < 0)
+ *to++='0';
+ }
+ else if (decpt == 0)
+ {
+ *to++= '0';
+ if (!tmp_dec)
+ goto end;
+ *to++='.';
+ }
+ else
+ {
+ while (decpt-- > 0)
+ *to++= *pos++;
+ if (!tmp_dec)
+ goto end;
+ *to++='.';
+ }
+ while (tmp_dec--)
+ *to++= *pos++;
+#else
+ sprintf(to,"%.*f",dec,nr);
+ to=strend(to);
+#endif
+ }
+#ifdef HAVE_FCONVERT
+ end:
+#endif
+ val_buffer->length((uint) (to-val_buffer->ptr()));
+ if (zerofill)
+ prepend_zeros(val_buffer);
+ return val_buffer;
+}
+
+
+int Field_float::cmp(const char *a_ptr, const char *b_ptr)
+{
+ float a,b;
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ float4get(a,a_ptr);
+ float4get(b,b_ptr);
+ }
+ else
+#endif
+ {
+ memcpy_fixed(&a,a_ptr,sizeof(float));
+ memcpy_fixed(&b,b_ptr,sizeof(float));
+ }
+ return (a < b) ? -1 : (a > b) ? 1 : 0;
+}
+
+#define FLT_EXP_DIG (sizeof(float)*8-FLT_MANT_DIG)
+
+void Field_float::sort_string(char *to,uint length __attribute__((unused)))
+{
+ float nr;
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ float4get(nr,ptr);
+ }
+ else
+#endif
+ memcpy_fixed(&nr,ptr,sizeof(float));
+
+ uchar *tmp= (uchar*) to;
+ if (nr == (float) 0.0)
+ { /* Change to zero string */
+ tmp[0]=(uchar) 128;
+ bzero((char*) tmp+1,sizeof(nr)-1);
+ }
+ else
+ {
+#ifdef WORDS_BIGENDIAN
+ memcpy_fixed(tmp,&nr,sizeof(nr));
+#else
+ tmp[0]= ptr[3]; tmp[1]=ptr[2]; tmp[2]= ptr[1]; tmp[3]=ptr[0];
+#endif
+ if (tmp[0] & 128) /* Negative */
+ { /* make complement */
+ uint i;
+ for (i=0 ; i < sizeof(nr); i++)
+ tmp[i]=tmp[i] ^ (uchar) 255;
+ }
+ else
+ {
+ ushort exp_part=(((ushort) tmp[0] << 8) | (ushort) tmp[1] |
+ (ushort) 32768);
+ exp_part+= (ushort) 1 << (16-1-FLT_EXP_DIG);
+ tmp[0]= (uchar) (exp_part >> 8);
+ tmp[1]= (uchar) exp_part;
+ }
+ }
+}
+
+
+void Field_float::sql_type(String &res) const
+{
+ if (dec == NOT_FIXED_DEC)
+ strmov((char*) res.ptr(),"float");
+ else
+ sprintf((char*) res.ptr(),"float(%d,%d)",(int) field_length,dec);
+ add_zerofill_and_unsigned(res);
+}
+
+/****************************************************************************
+** double precision floating point numbers
+****************************************************************************/
+
+void Field_double::store(const char *from,uint len)
+{
+ String tmp_str(from,len);
+ errno=0;
+ double j= atof(tmp_str.c_ptr());
+ if (errno || current_thd->count_cuted_fields && !test_if_real(from,len))
+ current_thd->cuted_fields++;
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ float8store(ptr,j);
+ }
+ else
+#endif
+ doublestore(ptr,j);
+}
+
+
+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
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ float8store(ptr,nr);
+ }
+ else
+#endif
+ doublestore(ptr,nr);
+}
+
+
+void Field_double::store(longlong nr)
+{
+ double j= (double) nr;
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ float8store(ptr,j);
+ }
+ else
+#endif
+ doublestore(ptr,j);
+}
+
+
+double Field_double::val_real(void)
+{
+ double j;
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ float8get(j,ptr);
+ }
+ else
+#endif
+ doubleget(j,ptr);
+ return j;
+}
+
+longlong Field_double::val_int(void)
+{
+ double j;
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ float8get(j,ptr);
+ }
+ else
+#endif
+ doubleget(j,ptr);
+ return ((longlong) j);
+}
+
+
+String *Field_double::val_str(String *val_buffer,
+ String *val_ptr __attribute__((unused)))
+{
+ double nr;
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ float8get(nr,ptr);
+ }
+ else
+#endif
+ doubleget(nr,ptr);
+
+ uint to_length=max(field_length,320);
+ val_buffer->alloc(to_length);
+ char *to=(char*) val_buffer->ptr();
+
+ if (dec >= NOT_FIXED_DEC)
+ {
+ sprintf(to,"%-*.*g",(int) field_length,DBL_DIG,nr);
+ to=strcend(to,' ');
+ }
+ else
+ {
+#ifdef HAVE_FCONVERT
+ char buff[320],*pos=buff;
+ int decpt,sign,tmp_dec=dec;
+
+ VOID(fconvert(nr,tmp_dec,&decpt,&sign,buff));
+ if (sign)
+ {
+ *to++='-';
+ }
+ if (decpt < 0)
+ { /* val_buffer is < 0 */
+ *to++='0';
+ if (!tmp_dec)
+ goto end;
+ *to++='.';
+ if (-decpt > tmp_dec)
+ decpt= - (int) tmp_dec;
+ tmp_dec=(uint) ((int) tmp_dec+decpt);
+ while (decpt++ < 0)
+ *to++='0';
+ }
+ else if (decpt == 0)
+ {
+ *to++= '0';
+ if (!tmp_dec)
+ goto end;
+ *to++='.';
+ }
+ else
+ {
+ while (decpt-- > 0)
+ *to++= *pos++;
+ if (!tmp_dec)
+ goto end;
+ *to++='.';
+ }
+ while (tmp_dec--)
+ *to++= *pos++;
+#else
+#ifdef HAVE_SNPRINTF
+ snprintf(to,to_length,"%.*f",dec,nr);
+#else
+ sprintf(to,"%.*f",dec,nr);
+#endif
+ to=strend(to);
+#endif
+ }
+#ifdef HAVE_FCONVERT
+ end:
+#endif
+
+ val_buffer->length((uint) (to-val_buffer->ptr()));
+ if (zerofill)
+ prepend_zeros(val_buffer);
+ return val_buffer;
+}
+
+
+int Field_double::cmp(const char *a_ptr, const char *b_ptr)
+{
+ double a,b;
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ float8get(a,a_ptr);
+ float8get(b,b_ptr);
+ }
+ else
+#endif
+ {
+ memcpy_fixed(&a,a_ptr,sizeof(double));
+ memcpy_fixed(&b,b_ptr,sizeof(double));
+ }
+ return (a < b) ? -1 : (a > b) ? 1 : 0;
+}
+
+
+#define DBL_EXP_DIG (sizeof(double)*8-DBL_MANT_DIG)
+
+/* The following should work for IEEE */
+
+void Field_double::sort_string(char *to,uint length __attribute__((unused)))
+{
+ double nr;
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ float8get(nr,ptr);
+ }
+ else
+#endif
+ memcpy_fixed(&nr,ptr,sizeof(nr));
+ change_double_for_sort(nr, (byte*) to);
+}
+
+
+void Field_double::sql_type(String &res) const
+{
+ if (dec == NOT_FIXED_DEC)
+ strmov((char*) res.ptr(),"double");
+ else
+ sprintf((char*) res.ptr(),"double(%d,%d)",(int) field_length,dec);
+ add_zerofill_and_unsigned(res);
+}
+
+
+/****************************************************************************
+** timestamp
+** The first timestamp in the table is automaticly updated
+** by handler.cc. The form->timestamp points at the automatic timestamp.
+****************************************************************************/
+
+Field_timestamp::Field_timestamp(char *ptr_arg, uint32 len_arg,
+ enum utype unireg_check_arg,
+ const char *field_name_arg,
+ struct st_table *table_arg)
+ :Field_num(ptr_arg, len_arg, (uchar*) 0,0,
+ unireg_check_arg, field_name_arg, table_arg,
+ 0, 1, 1)
+{
+ if (table && !table->timestamp_field)
+ {
+ table->timestamp_field= this; // Automatic timestamp
+ table->time_stamp=(ulong) (ptr_arg - (char*) table->record[0])+1;
+ flags|=TIMESTAMP_FLAG;
+ }
+}
+
+
+void Field_timestamp::store(const char *from,uint len)
+{
+ long tmp=(long) str_to_timestamp(from,len);
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ int4store(ptr,tmp);
+ }
+ else
+#endif
+ longstore(ptr,tmp);
+}
+
+void Field_timestamp::fill_and_store(char *from,uint len)
+{
+ uint res_length;
+ if (len <= field_length)
+ res_length=field_length;
+ else if (len <= 12)
+ res_length=12; /* purecov: inspected */
+ else if (len <= 14)
+ res_length=14; /* purecov: inspected */
+ else
+ res_length=(len+1)/2*2; // must be even
+ if (res_length != len)
+ {
+ bmove_upp(from+res_length,from+len,len);
+ bfill(from,res_length-len,'0');
+ len=res_length;
+ }
+ long tmp=(long) str_to_timestamp(from,len);
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ int4store(ptr,tmp);
+ }
+ else
+#endif
+ longstore(ptr,tmp);
+}
+
+
+void Field_timestamp::store(double nr)
+{
+ if (nr < 0 || nr > 99991231235959.0)
+ {
+ nr=0; // Avoid overflow on buff
+ current_thd->cuted_fields++;
+ }
+ Field_timestamp::store((longlong) rint(nr));
+}
+
+
+/*
+** Convert a datetime of formats YYMMDD, YYYYMMDD or YYMMDDHHMSS to
+** YYYYMMDDHHMMSS. The high date '99991231235959' is checked before this
+** function.
+*/
+
+static longlong fix_datetime(longlong nr)
+{
+ if (nr == LL(0) || nr >= LL(10000101000000))
+ return nr; // Normal datetime >= Year 1000
+ if (nr < 101)
+ goto err;
+ if (nr <= (YY_PART_YEAR-1)*10000L+1231L)
+ return (nr+20000000L)*1000000L; // YYMMDD, year: 2000-2069
+ if (nr < (YY_PART_YEAR)*10000L+101L)
+ goto err;
+ if (nr <= 991231L)
+ return (nr+19000000L)*1000000L; // YYMMDD, year: 1970-1999
+ if (nr < 10000101L)
+ goto err;
+ if (nr <= 99991231L)
+ return nr*1000000L;
+ if (nr < 101000000L)
+ goto err;
+ if (nr <= (YY_PART_YEAR-1)*LL(10000000000)+LL(1231235959))
+ return nr+LL(20000000000000); // YYMMDDHHMMSS, 2000-2069
+ if (nr < YY_PART_YEAR*LL(10000000000)+ LL(101000000))
+ goto err;
+ if (nr <= LL(991231235959))
+ return nr+LL(19000000000000); // YYMMDDHHMMSS, 1970-1999
+
+ err:
+ current_thd->cuted_fields++;
+ return LL(0);
+}
+
+
+void Field_timestamp::store(longlong nr)
+{
+ TIME l_time;
+ time_t timestamp;
+ long part1,part2;
+
+ if ((nr=fix_datetime(nr)))
+ {
+ part1=(long) (nr/LL(1000000));
+ part2=(long) (nr - (longlong) part1*LL(1000000));
+ l_time.year= 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.minute=(int) part2 / 100;
+ l_time.second=(int) part2 % 100;
+ timestamp=my_gmt_sec(&l_time);
+ }
+ else
+ timestamp=0;
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ int4store(ptr,timestamp);
+ }
+ else
+#endif
+ longstore(ptr,timestamp);
+}
+
+
+double Field_timestamp::val_real(void)
+{
+ return (double) Field_timestamp::val_int();
+}
+
+longlong Field_timestamp::val_int(void)
+{
+ uint len,pos;
+ int part_time;
+ uint32 temp;
+ time_t time_arg;
+ struct tm *l_time;
+ longlong res;
+ struct tm tm_tmp;
+
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ temp=uint4korr(ptr);
+ else
+#endif
+ longget(temp,ptr);
+
+ if (temp == 0L) // No time
+ return(0); /* purecov: inspected */
+ time_arg=(time_t) temp;
+ localtime_r(&time_arg,&tm_tmp);
+ l_time=&tm_tmp;
+ res=(longlong) 0;
+ for (pos=len=0; len+1 < (uint) field_length ; len+=2,pos++)
+ {
+ bool year_flag=0;
+ switch (dayord.pos[pos]) {
+ case 0: part_time=l_time->tm_year % 100; year_flag=1 ; break;
+ case 1: part_time=l_time->tm_mon+1; break;
+ case 2: part_time=l_time->tm_mday; break;
+ case 3: part_time=l_time->tm_hour; break;
+ case 4: part_time=l_time->tm_min; break;
+ case 5: part_time=l_time->tm_sec; break;
+ default: part_time=0; break; /* purecov: deadcode */
+ }
+ if (year_flag && (field_length == 8 || field_length == 14))
+ {
+ res=res*(longlong) 10000+(part_time+
+ ((part_time < YY_PART_YEAR) ? 2000 : 1900));
+ len+=2;
+ }
+ else
+ res=res*(longlong) 100+part_time;
+ }
+ return (longlong) res;
+}
+
+
+String *Field_timestamp::val_str(String *val_buffer,
+ String *val_ptr __attribute__((unused)))
+{
+ uint pos;
+ int part_time;
+ uint32 temp;
+ time_t time_arg;
+ struct tm *l_time;
+ struct tm tm_tmp;
+
+ val_buffer->alloc(field_length+1);
+ char *to=(char*) val_buffer->ptr(),*end=to+field_length;
+
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ temp=uint4korr(ptr);
+ else
+#endif
+ longget(temp,ptr);
+
+ if (temp == 0L)
+ { /* Zero time is "000000" */
+ VOID(strfill(to,field_length,'0'));
+ val_buffer->length(field_length);
+ return val_buffer;
+ }
+ time_arg=(time_t) temp;
+ localtime_r(&time_arg,&tm_tmp);
+ l_time=&tm_tmp;
+ for (pos=0; to < end ; pos++)
+ {
+ bool year_flag=0;
+ switch (dayord.pos[pos]) {
+ case 0: part_time=l_time->tm_year % 100; year_flag=1; break;
+ case 1: part_time=l_time->tm_mon+1; break;
+ case 2: part_time=l_time->tm_mday; break;
+ case 3: part_time=l_time->tm_hour; break;
+ case 4: part_time=l_time->tm_min; break;
+ case 5: part_time=l_time->tm_sec; break;
+ default: part_time=0; break; /* purecov: deadcode */
+ }
+ if (year_flag && (field_length == 8 || field_length == 14))
+ {
+ if (part_time < YY_PART_YEAR)
+ {
+ *to++='2'; *to++='0'; /* purecov: inspected */
+ }
+ else
+ {
+ *to++='1'; *to++='9';
+ }
+ }
+ *to++=(char) ('0'+((uint) part_time/10));
+ *to++=(char) ('0'+((uint) part_time % 10));
+ }
+ *to=0; // Safeguard
+ val_buffer->length((uint) (to-val_buffer->ptr()));
+ return val_buffer;
+}
+
+bool Field_timestamp::get_date(TIME *ltime,
+ bool fuzzydate __attribute__((unused)))
+{
+ long temp;
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ temp=uint4korr(ptr);
+ else
+#endif
+ longget(temp,ptr);
+ if (temp == 0L)
+ { /* Zero time is "000000" */
+ bzero((char*) ltime,sizeof(*ltime));
+ }
+ else
+ {
+ struct tm tm_tmp;
+ time_t time_arg= (time_t) temp;
+ localtime_r(&time_arg,&tm_tmp);
+ struct tm *start= &tm_tmp;
+ ltime->year= start->tm_year+1900;
+ ltime->month= start->tm_mon+1;
+ ltime->day= start->tm_mday;
+ ltime->hour= start->tm_hour;
+ ltime->minute= start->tm_min;
+ ltime->second= start->tm_sec;
+ ltime->second_part= 0;
+ ltime->neg= 0;
+ }
+ return 0;
+}
+
+bool Field_timestamp::get_time(TIME *ltime)
+{
+ Field_timestamp::get_date(ltime,0);
+ return 0;
+}
+
+int Field_timestamp::cmp(const char *a_ptr, const char *b_ptr)
+{
+ int32 a,b;
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ a=sint4korr(a_ptr);
+ b=sint4korr(b_ptr);
+ }
+ else
+#endif
+ {
+ longget(a,a_ptr);
+ longget(b,b_ptr);
+ }
+ return ((uint32) a < (uint32) b) ? -1 : ((uint32) a > (uint32) b) ? 1 : 0;
+}
+
+void Field_timestamp::sort_string(char *to,uint length __attribute__((unused)))
+{
+#ifdef WORDS_BIGENDIAN
+ if (!table->db_low_byte_first)
+ {
+ to[0] = ptr[0];
+ to[1] = ptr[1];
+ to[2] = ptr[2];
+ to[3] = ptr[3];
+ }
+ else
+#endif
+ {
+ to[0] = ptr[3];
+ to[1] = ptr[2];
+ to[2] = ptr[1];
+ to[3] = ptr[0];
+ }
+}
+
+
+void Field_timestamp::sql_type(String &res) const
+{
+ sprintf((char*) res.ptr(),"timestamp(%d)",(int) field_length);
+ res.length(strlen(res.ptr()));
+}
+
+
+void Field_timestamp::set_time()
+{
+ long tmp= (long) current_thd->query_start();
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ int4store(ptr,tmp);
+ }
+ else
+#endif
+ longstore(ptr,tmp);
+}
+
+/****************************************************************************
+** time type
+** In string context: HH:MM:SS
+** In number context: HHMMSS
+** Stored as a 3 byte unsigned int
+****************************************************************************/
+
+void Field_time::store(const char *from,uint len)
+{
+ TIME ltime;
+ long tmp;
+ if (str_to_time(from,len,&ltime))
+ tmp=0L;
+ else
+ {
+ if (ltime.month)
+ ltime.day=0;
+ tmp=(ltime.day*24L+ltime.hour)*10000L+(ltime.minute*100+ltime.second);
+ if (tmp > 8385959)
+ {
+ tmp=8385959;
+ current_thd->cuted_fields++;
+ }
+ }
+ if (ltime.neg)
+ tmp= -tmp;
+ Field_time::store((longlong) tmp);
+}
+
+
+void Field_time::store(double nr)
+{
+ long tmp;
+ if (nr > 8385959.0)
+ {
+ tmp=8385959L;
+ current_thd->cuted_fields++;
+ }
+ else if (nr < -8385959.0)
+ {
+ tmp= -8385959L;
+ current_thd->cuted_fields++;
+ }
+ else
+ {
+ tmp=(long) floor(fabs(nr)); // Remove fractions
+ if (nr < 0)
+ tmp= -tmp;
+ if (tmp % 100 > 59 || tmp/100 % 100 > 59)
+ {
+ tmp=0;
+ current_thd->cuted_fields++;
+ }
+ }
+ int3store(ptr,tmp);
+}
+
+
+void Field_time::store(longlong nr)
+{
+ long tmp;
+ if (nr > (longlong) 8385959L)
+ {
+ tmp=8385959L;
+ current_thd->cuted_fields++;
+ }
+ else if (nr < (longlong) -8385959L)
+ {
+ tmp= -8385959L;
+ current_thd->cuted_fields++;
+ }
+ else
+ {
+ tmp=(long) nr;
+ if (tmp % 100 > 59 || tmp/100 % 100 > 59)
+ {
+ tmp=0;
+ current_thd->cuted_fields++;
+ }
+ }
+ int3store(ptr,tmp);
+}
+
+
+double Field_time::val_real(void)
+{
+ ulong j= (ulong) uint3korr(ptr);
+ return (double) j;
+}
+
+longlong Field_time::val_int(void)
+{
+ return (longlong) sint3korr(ptr);
+}
+
+String *Field_time::val_str(String *val_buffer,
+ String *val_ptr __attribute__((unused)))
+{
+ val_buffer->alloc(16);
+ long tmp=(long) sint3korr(ptr);
+ const char *sign="";
+ if (tmp < 0)
+ {
+ tmp= -tmp;
+ sign= "-";
+ }
+ sprintf((char*) val_buffer->ptr(),"%s%02d:%02d:%02d",
+ sign,(int) (tmp/10000), (int) (tmp/100 % 100),
+ (int) (tmp % 100));
+ val_buffer->length(strlen(val_buffer->ptr()));
+ return val_buffer;
+}
+
+bool Field_time::get_time(TIME *ltime)
+{
+ long tmp=(long) sint3korr(ptr);
+ ltime->neg=0;
+ if (tmp < 0)
+ {
+ ltime->neg= 1;
+ tmp=-tmp;
+ }
+ ltime->day=tmp/10000;
+ tmp-=ltime->day*10000;
+ ltime->hour= tmp/100;
+ ltime->second= 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);
+ return (a < b) ? -1 : (a > b) ? 1 : 0;
+}
+
+void Field_time::sort_string(char *to,uint length __attribute__((unused)))
+{
+ to[0] = (uchar) (ptr[2] ^ 128);
+ to[1] = ptr[1];
+ to[2] = ptr[0];
+}
+
+void Field_time::sql_type(String &res) const
+{
+ res.set("time",4);
+}
+
+/****************************************************************************
+** year type
+** Save in a byte the year 0, 1901->2155
+** Can handle 2 byte or 4 byte years!
+****************************************************************************/
+
+void Field_year::store(const char *from, uint len)
+{
+ String tmp_str(from,len);
+ long nr= strtol(tmp_str.c_ptr(),NULL,10);
+
+ if (nr < 0 || nr >= 100 && nr <= 1900 || nr > 2155)
+ {
+ *ptr=0;
+ current_thd->cuted_fields++;
+ return;
+ }
+ else if (current_thd->count_cuted_fields && !test_if_int(from,len))
+ current_thd->cuted_fields++;
+ if (nr != 0 || len != 4)
+ {
+ if (nr < YY_PART_YEAR)
+ nr+=100; // 2000 - 2069
+ else if (nr > 1900)
+ nr-= 1900;
+ }
+ *ptr= (char) (unsigned char) nr;
+}
+
+void Field_year::store(double nr)
+{
+ if (nr < 0.0 || nr >= 2155.0)
+ Field_year::store((longlong) -1);
+ else
+ Field_year::store((longlong) nr);
+}
+
+void Field_year::store(longlong nr)
+{
+ if (nr < 0 || nr >= 100 && nr <= 1900 || nr > 2155)
+ {
+ *ptr=0;
+ current_thd->cuted_fields++;
+ return;
+ }
+ if (nr != 0 || field_length != 4) // 0000 -> 0; 00 -> 2000
+ {
+ if (nr < YY_PART_YEAR)
+ nr+=100; // 2000 - 2069
+ else if (nr > 1900)
+ nr-= 1900;
+ }
+ *ptr= (char) (unsigned char) nr;
+}
+
+
+double Field_year::val_real(void)
+{
+ return (double) Field_year::val_int();
+}
+
+longlong Field_year::val_int(void)
+{
+ int tmp= (int) ((uchar*) ptr)[0];
+ if (field_length != 4)
+ tmp%=100; // Return last 2 char
+ else if (tmp)
+ tmp+=1900;
+ return (longlong) tmp;
+}
+
+String *Field_year::val_str(String *val_buffer,
+ String *val_ptr __attribute__((unused)))
+{
+ val_buffer->alloc(5);
+ val_buffer->length(field_length);
+ char *to=(char*) val_buffer->ptr();
+ sprintf(to,field_length == 2 ? "%02d" : "%04d",(int) Field_year::val_int());
+ return val_buffer;
+}
+
+void Field_year::sql_type(String &res) const
+{
+ sprintf((char*) res.ptr(),"year(%d)",(int) field_length);
+ res.length(strlen(res.ptr()));
+}
+
+
+/****************************************************************************
+** date type
+** In string context: YYYY-MM-DD
+** In number context: YYYYMMDD
+** Stored as a 4 byte unsigned int
+****************************************************************************/
+
+void Field_date::store(const char *from,uint len)
+{
+ TIME l_time;
+ ulong 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);
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ int4store(ptr,tmp);
+ }
+ else
+#endif
+ longstore(ptr,tmp);
+}
+
+
+void Field_date::store(double nr)
+{
+ long tmp;
+ if (nr >= 19000000000000.0 && nr <= 99991231235959.0)
+ nr=floor(nr/1000000.0); // Timestamp to date
+ if (nr < 0.0 || nr > 99991231.0)
+ {
+ tmp=0L;
+ current_thd->cuted_fields++;
+ }
+ else
+ tmp=(long) rint(nr);
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ int4store(ptr,tmp);
+ }
+ else
+#endif
+ longstore(ptr,tmp);
+}
+
+
+void Field_date::store(longlong nr)
+{
+ long tmp;
+ if (nr >= LL(19000000000000) && nr < LL(99991231235959))
+ nr=nr/LL(1000000); // Timestamp to date
+ if (nr < 0 || nr > LL(99991231))
+ {
+ tmp=0L;
+ current_thd->cuted_fields++;
+ }
+ else
+ tmp=(long) nr;
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ int4store(ptr,tmp);
+ }
+ else
+#endif
+ longstore(ptr,tmp);
+}
+
+
+double Field_date::val_real(void)
+{
+ int32 j;
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ j=sint4korr(ptr);
+ else
+#endif
+ longget(j,ptr);
+ return (double) (uint32) j;
+}
+
+longlong Field_date::val_int(void)
+{
+ int32 j;
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ j=sint4korr(ptr);
+ else
+#endif
+ longget(j,ptr);
+ return (longlong) (uint32) j;
+}
+
+String *Field_date::val_str(String *val_buffer,
+ String *val_ptr __attribute__((unused)))
+{
+ val_buffer->alloc(field_length);
+ val_buffer->length(field_length);
+ int32 tmp;
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ tmp=sint4korr(ptr);
+ else
+#endif
+ longget(tmp,ptr);
+ sprintf((char*) val_buffer->ptr(),"%04d-%02d-%02d",
+ (int) ((uint32) tmp/10000L % 10000), (int) ((uint32) tmp/100 % 100),
+ (int) ((uint32) tmp % 100));
+ return val_buffer;
+}
+
+int Field_date::cmp(const char *a_ptr, const char *b_ptr)
+{
+ int32 a,b;
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ a=sint4korr(a_ptr);
+ b=sint4korr(b_ptr);
+ }
+ else
+#endif
+ {
+ longget(a,a_ptr);
+ longget(b,b_ptr);
+ }
+ return ((uint32) a < (uint32) b) ? -1 : ((uint32) a > (uint32) b) ? 1 : 0;
+}
+
+
+void Field_date::sort_string(char *to,uint length __attribute__((unused)))
+{
+#ifdef WORDS_BIGENDIAN
+ if (!table->db_low_byte_first)
+ {
+ to[0] = ptr[0];
+ to[1] = ptr[1];
+ to[2] = ptr[2];
+ to[3] = ptr[3];
+ }
+ else
+#endif
+ {
+ to[0] = ptr[3];
+ to[1] = ptr[2];
+ to[2] = ptr[1];
+ to[3] = ptr[0];
+ }
+}
+
+void Field_date::sql_type(String &res) const
+{
+ res.set("date",4);
+}
+
+/****************************************************************************
+** The new date type
+** This is identical to the old date type, but stored on 3 bytes instead of 4
+** In number context: YYYYMMDD
+****************************************************************************/
+
+void Field_newdate::store(const char *from,uint len)
+{
+ TIME l_time;
+ long tmp;
+ if (str_to_TIME(from,len,&l_time,1) == TIMESTAMP_NONE)
+ tmp=0L;
+ else
+ tmp= l_time.day + l_time.month*32 + l_time.year*16*32;
+ int3store(ptr,tmp);
+}
+
+void Field_newdate::store(double nr)
+{
+ if (nr < 0.0 || nr > 99991231235959.0)
+ Field_newdate::store((longlong) -1);
+ else
+ Field_newdate::store((longlong) rint(nr));
+}
+
+
+void Field_newdate::store(longlong nr)
+{
+ long tmp;
+ if (nr >= LL(100000000) && nr <= LL(99991231235959))
+ nr=nr/LL(1000000); // Timestamp to date
+ if (nr < 0L || nr > 99991231L)
+ {
+ tmp=0;
+ current_thd->cuted_fields++;
+ }
+ else
+ {
+ tmp=(long) nr;
+ if (tmp)
+ {
+ if (tmp < YY_PART_YEAR*10000L) // Fix short dates
+ tmp+=20000000L;
+ else if (tmp < 999999L)
+ tmp+=19000000L;
+ }
+ uint month=((tmp/100) % 100);
+ uint day= tmp%100;
+ if (month > 12 || day > 31)
+ {
+ tmp=0L; // Don't allow date to change
+ current_thd->cuted_fields++;
+ }
+ else
+ tmp= day + month*32 + (tmp/10000)*16*32;
+ }
+ int3store(ptr,tmp);
+}
+
+void Field_newdate::store_time(TIME *ltime,timestamp_type type)
+{
+ long tmp;
+ if (type == TIMESTAMP_DATE || type == TIMESTAMP_FULL)
+ tmp=ltime->year*16*32+ltime->month*32+ltime->day;
+ else
+ {
+ tmp=0;
+ current_thd->cuted_fields++;
+ }
+ int3store(ptr,tmp);
+}
+
+
+
+double Field_newdate::val_real(void)
+{
+ return (double) Field_newdate::val_int();
+}
+
+longlong Field_newdate::val_int(void)
+{
+ ulong j=uint3korr(ptr);
+ j= (j % 32L)+(j / 32L % 16L)*100L + (j/(16L*32L))*10000L;
+ return (longlong) j;
+}
+
+String *Field_newdate::val_str(String *val_buffer,
+ String *val_ptr __attribute__((unused)))
+{
+ val_buffer->alloc(field_length);
+ val_buffer->length(field_length);
+ ulong tmp=(ulong) uint3korr(ptr);
+ int part;
+ char *pos=(char*) val_buffer->ptr()+10;
+
+ /* Open coded to get more speed */
+ *pos--=0;
+ part=(int) (tmp & 31);
+ *pos--='0'+part%10;
+ *pos--='0'+part/10;
+ *pos--='-';
+ part=(int) (tmp >> 5 & 15);
+ *pos--='0'+part%10;
+ *pos--='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;
+ return val_buffer;
+}
+
+bool Field_newdate::get_date(TIME *ltime,bool fuzzydate)
+{
+ if (is_null())
+ return 1;
+ ulong tmp=(ulong) uint3korr(ptr);
+ bzero((char*) ltime,sizeof(*ltime));
+ ltime->day= tmp & 31;
+ ltime->month= (tmp >> 5) & 15;
+ ltime->year= (tmp >> 9);
+ return (!fuzzydate && (!ltime->month || !ltime->day) && ltime->year) ? 1 : 0;
+}
+
+bool Field_newdate::get_time(TIME *ltime)
+{
+ Field_newdate::get_date(ltime,0);
+ return 0;
+}
+
+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);
+ return (a < b) ? -1 : (a > b) ? 1 : 0;
+}
+
+void Field_newdate::sort_string(char *to,uint length __attribute__((unused)))
+{
+ to[0] = ptr[2];
+ to[1] = ptr[1];
+ to[2] = ptr[0];
+}
+
+void Field_newdate::sql_type(String &res) const
+{
+ res.set("date",4);
+}
+
+
+/****************************************************************************
+** datetime type
+** In string context: YYYY-MM-DD HH:MM:DD
+** In number context: YYYYMMDDHHMMDD
+** Stored as a 8 byte unsigned int. Should sometimes be change to a 6 byte int.
+****************************************************************************/
+
+void Field_datetime::store(const char *from,uint len)
+{
+ longlong tmp=str_to_datetime(from,len,1);
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ int8store(ptr,tmp);
+ }
+ else
+#endif
+ longlongstore(ptr,tmp);
+}
+
+
+void Field_datetime::store(double nr)
+{
+ if (nr < 0.0 || nr > 99991231235959.0)
+ {
+ nr=0.0;
+ current_thd->cuted_fields++;
+ }
+ Field_datetime::store((longlong) rint(nr));
+}
+
+
+void Field_datetime::store(longlong nr)
+{
+ if (nr < 0 || nr > LL(99991231235959))
+ {
+ nr=0;
+ current_thd->cuted_fields++;
+ }
+ else
+ nr=fix_datetime(nr);
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ int8store(ptr,nr);
+ }
+ else
+#endif
+ longlongstore(ptr,nr);
+}
+
+void Field_datetime::store_time(TIME *ltime,timestamp_type type)
+{
+ longlong tmp;
+ if (type == TIMESTAMP_DATE || type == TIMESTAMP_FULL)
+ tmp=((ltime->year*10000L+ltime->month*100+ltime->day)*LL(1000000)+
+ (ltime->hour*10000L+ltime->minute*100+ltime->second));
+ else
+ {
+ tmp=0;
+ current_thd->cuted_fields++;
+ }
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ int8store(ptr,tmp);
+ }
+ else
+#endif
+ longlongstore(ptr,tmp);
+}
+
+
+double Field_datetime::val_real(void)
+{
+ return (double) Field_datetime::val_int();
+}
+
+longlong Field_datetime::val_int(void)
+{
+ longlong j;
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ j=sint8korr(ptr);
+ else
+#endif
+ longlongget(j,ptr);
+ return j;
+}
+
+
+String *Field_datetime::val_str(String *val_buffer,
+ String *val_ptr __attribute__((unused)))
+{
+ val_buffer->alloc(field_length);
+ val_buffer->length(field_length);
+ ulonglong tmp;
+ long part1,part2;
+ char *pos;
+ int part3;
+
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ tmp=sint8korr(ptr);
+ else
+#endif
+ longlongget(tmp,ptr);
+
+ /*
+ Avoid problem with slow longlong aritmetic and sprintf
+ */
+
+ part1=(long) (tmp/LL(1000000));
+ part2=(long) (tmp - (ulonglong) part1*LL(1000000));
+
+ 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;
+ 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));
+
+ 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;
+ return (!fuzzydate && (!ltime->month || !ltime->day) && ltime->year) ? 1 : 0;
+}
+
+bool Field_datetime::get_time(TIME *ltime)
+{
+ Field_datetime::get_date(ltime,0);
+ return 0;
+}
+
+int Field_datetime::cmp(const char *a_ptr, const char *b_ptr)
+{
+ longlong a,b;
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ a=sint8korr(a_ptr);
+ b=sint8korr(b_ptr);
+ }
+ else
+#endif
+ {
+ longlongget(a,a_ptr);
+ longlongget(b,b_ptr);
+ }
+ return ((ulonglong) a < (ulonglong) b) ? -1 :
+ ((ulonglong) a > (ulonglong) b) ? 1 : 0;
+}
+
+void Field_datetime::sort_string(char *to,uint length __attribute__((unused)))
+{
+#ifdef WORDS_BIGENDIAN
+ if (!table->db_low_byte_first)
+ {
+ to[0] = ptr[0];
+ to[1] = ptr[1];
+ to[2] = ptr[2];
+ to[3] = ptr[3];
+ to[4] = ptr[4];
+ to[5] = ptr[5];
+ to[6] = ptr[6];
+ to[7] = ptr[7];
+ }
+ else
+#endif
+ {
+ to[0] = ptr[7];
+ to[1] = ptr[6];
+ to[2] = ptr[5];
+ to[3] = ptr[4];
+ to[4] = ptr[3];
+ to[5] = ptr[2];
+ to[6] = ptr[1];
+ to[7] = ptr[0];
+ }
+}
+
+
+void Field_datetime::sql_type(String &res) const
+{
+ res.set("datetime",8);
+}
+
+/****************************************************************************
+** string type
+** A string may be varchar or binary
+****************************************************************************/
+
+ /* Copy a string and fill with space */
+
+void Field_string::store(const char *from,uint length)
+{
+#ifdef USE_TIS620
+ if(!binary_flag) {
+ ThNormalize((uchar *)ptr, field_length, (uchar *)from, length);
+ if(length < field_length) {
+ bfill(ptr + length, field_length - length, ' ');
+ }
+ }
+#else
+ if (length <= field_length)
+ {
+ memcpy(ptr,from,length);
+ if (length < field_length)
+ bfill(ptr+length,field_length-length,' ');
+ }
+ else
+ {
+ memcpy(ptr,from,field_length);
+ if (current_thd->count_cuted_fields)
+ { // Check if we loosed some info
+ const char *end=from+length;
+ for (from+=field_length ; from != end ; from++)
+ {
+ if (!isspace(*from))
+ {
+ current_thd->cuted_fields++;
+ break;
+ }
+ }
+ }
+ }
+#endif /* USE_TIS620 */
+}
+
+
+void Field_string::store(double nr)
+{
+ char buff[MAX_FIELD_WIDTH],*end;
+ int width=min(field_length,DBL_DIG+5);
+ sprintf(buff,"%-*.*g",width,max(width-5,0),nr);
+ end=strcend(buff,' ');
+ Field_string::store(buff,(uint) (end - buff));
+}
+
+
+void Field_string::store(longlong nr)
+{
+ char buff[22];
+ char *end=longlong10_to_str(nr,buff,-10);
+ Field_string::store(buff,end-buff);
+}
+
+
+double Field_string::val_real(void)
+{
+ double value;
+ char save=ptr[field_length]; // Ok to patch record
+ ptr[field_length]=0;
+ value=atof(ptr);
+ ptr[field_length]=save;
+ return value;
+}
+
+
+longlong Field_string::val_int(void)
+{
+ longlong value;
+ char save=ptr[field_length]; // Ok to patch record
+ ptr[field_length]=0;
+ value=strtoll(ptr,NULL,10);
+ ptr[field_length]=save;
+ return value;
+}
+
+
+String *Field_string::val_str(String *val_buffer __attribute__((unused)),
+ String *val_ptr)
+{
+ char *end=ptr+field_length;
+#ifdef WANT_TRUE_BINARY_STRINGS
+ if (!binary)
+#endif
+ while (end > ptr && end[-1] == ' ')
+ end--;
+ val_ptr->set((const char*) ptr,(uint) (end - ptr));
+ return val_ptr;
+}
+
+
+int Field_string::cmp(const char *a_ptr, const char *b_ptr)
+{
+ if (binary_flag)
+ return memcmp(a_ptr,b_ptr,field_length);
+ else
+ return my_sortcmp(a_ptr,b_ptr,field_length);
+}
+
+void Field_string::sort_string(char *to,uint length)
+{
+ if (binary_flag)
+ memcpy((byte*) to,(byte*) ptr,(size_t) length);
+ else
+ {
+#ifdef USE_STRCOLL
+ if (use_strcoll(default_charset_info)) {
+ uint tmp=my_strnxfrm(default_charset_info,
+ (unsigned char *)to, (unsigned char *) ptr,
+ length, field_length);
+ if (tmp < length)
+ bzero(to + tmp, length - tmp);
+ }
+ else
+#endif
+ for (char *from=ptr,*end=ptr+length ; from != end ;)
+ *to++=(char) my_sort_order[(uint) (uchar) *from++];
+ }
+}
+
+
+void Field_string::sql_type(String &res) const
+{
+ sprintf((char*) res.ptr(),"%s(%d)",
+ field_length > 3 &&
+ (table->db_options_in_use & HA_OPTION_PACK_RECORD) ?
+ "varchar" : "char",
+ (int) field_length);
+ res.length(strlen(res.ptr()));
+ if (binary_flag)
+ res.append(" binary");
+}
+
+
+char *Field_string::pack(char *to, const char *from, uint max_length)
+{
+ const char *end=from+min(field_length,max_length);
+ uchar length;
+ while (end > from && end[-1] == ' ')
+ end--;
+ *to= length=(uchar) (end-from);
+ memcpy(to+1, from, (int) length);
+ return to+1+length;
+}
+
+
+const char *Field_string::unpack(char *to, const char *from)
+{
+ uint length= (uint) (uchar) *from++;
+ memcpy(to, from, (int) length);
+ bfill(to+length, field_length - length, ' ');
+ return from+length;
+}
+
+
+int Field_string::pack_cmp(const char *a, const char *b, uint length)
+{
+ uint a_length= (uint) (uchar) *a++;
+ uint b_length= (uint) (uchar) *b++;
+
+ if (binary_flag)
+ {
+ int cmp= memcmp(a,b,min(a_length,b_length));
+ return cmp ? cmp : (int) (a_length - b_length);
+ }
+ return my_sortncmp(a,a_length, b,b_length);
+}
+
+
+uint Field_string::packed_col_length(const char *ptr)
+{
+ if (field_length > 255)
+ return uint2korr(ptr)+2;
+ else
+ return (uint) ((uchar) *ptr)+1;
+}
+
+uint Field_string::max_packed_col_length(uint max_length)
+{
+ return (field_length > 255 ? 2 : 1)+max_length;
+}
+
+
+/****************************************************************************
+** VARCHAR type (Not available for the end user yet)
+****************************************************************************/
+
+
+void Field_varstring::store(const char *from,uint length)
+{
+#ifdef USE_TIS620
+ if(!binary_flag)
+ {
+ ThNormalize((uchar *) ptr+2, field_length, (uchar *) from, length);
+ }
+#else
+ if (length <= field_length)
+ {
+ memcpy(ptr+2,from,length);
+ }
+ else
+ {
+ length=field_length;
+ memcpy(ptr+2,from,field_length);
+ current_thd->cuted_fields++;
+ }
+#endif /* USE_TIS620 */
+ int2store(ptr,length);
+}
+
+
+void Field_varstring::store(double nr)
+{
+ char buff[MAX_FIELD_WIDTH],*end;
+ int width=min(field_length,DBL_DIG+5);
+ sprintf(buff,"%-*.*g",width,max(width-5,0),nr);
+ end=strcend(buff,' ');
+ Field_varstring::store(buff,(uint) (end - buff));
+}
+
+
+void Field_varstring::store(longlong nr)
+{
+ char buff[22];
+ char *end=longlong10_to_str(nr,buff,-10);
+ Field_varstring::store(buff,end-buff);
+}
+
+
+double Field_varstring::val_real(void)
+{
+ double value;
+ uint length=uint2korr(ptr)+2;
+ char save=ptr[length]; // Ok to patch record
+ ptr[length]=0;
+ value=atof(ptr+2);
+ ptr[length]=save;
+ return value;
+}
+
+
+longlong Field_varstring::val_int(void)
+{
+ longlong value;
+ uint length=uint2korr(ptr)+2;
+ char save=ptr[length]; // Ok to patch record
+ ptr[length]=0;
+ value=strtoll(ptr+2,NULL,10);
+ ptr[length]=save;
+ return value;
+}
+
+
+String *Field_varstring::val_str(String *val_buffer __attribute__((unused)),
+ String *val_ptr)
+{
+ uint length=uint2korr(ptr);
+ val_ptr->set((const char*) ptr+2,length);
+ return val_ptr;
+}
+
+
+int Field_varstring::cmp(const char *a_ptr, const char *b_ptr)
+{
+ uint a_length=uint2korr(a_ptr);
+ uint b_length=uint2korr(b_ptr);
+ int diff;
+ if (binary_flag)
+ diff=memcmp(a_ptr+2,b_ptr+2,min(a_length,b_length));
+ else
+ diff=my_sortcmp(a_ptr+2,b_ptr+2,min(a_length,b_length));
+ return diff ? diff : (int) (a_length - b_length);
+}
+
+void Field_varstring::sort_string(char *to,uint length)
+{
+ uint tot_length=uint2korr(ptr);
+ if (binary_flag)
+ memcpy((byte*) to,(byte*) ptr+2,(size_t) tot_length);
+ else
+ {
+#ifdef USE_STRCOLL
+ if (use_strcoll(default_charset_info))
+ tot_length=my_strnxfrm(default_charset_info,
+ (unsigned char *) to, (unsigned char *)ptr+2,
+ length, tot_length);
+ else
+ {
+#endif
+ char *tmp=to;
+ if (tot_length > length)
+ tot_length=length;
+ for (char *from=ptr+2,*end=from+tot_length ; from != end ;)
+ *tmp++=(char) my_sort_order[(uint) (uchar) *from++];
+#ifdef USE_STRCOLL
+ }
+#endif
+ }
+ if (tot_length < length)
+ bzero(to+tot_length,length-tot_length);
+}
+
+
+void Field_varstring::sql_type(String &res) const
+{
+ sprintf((char*) res.ptr(),"varchar(%d)",(int) field_length);
+ res.length(strlen(res.ptr()));
+ if (binary_flag)
+ res.append(" binary");
+}
+
+char *Field_varstring::pack(char *to, const char *from, uint max_length)
+{
+ uint length=uint2korr(to);
+ if (length > max_length)
+ length=max_length;
+ *to++= (length & 255);
+ if (max_length > 255)
+ *to++= (uchar) (length >> 8);
+ if (length)
+ memcpy(to, from+2, length);
+ return to+length;
+}
+
+
+const char *Field_varstring::unpack(char *to, const char *from)
+{
+ uint length;
+ if (field_length > 255)
+ {
+ length= (uint) (uchar) (*to= *from++);
+ to[1]=0;
+ }
+ else
+ {
+ length=uint2korr(from);
+ to[0] = *from++;
+ to[1] = *from++;
+ }
+ if (length)
+ memcpy(to+2, from, length);
+ return from+length;
+}
+
+
+int Field_varstring::pack_cmp(const char *a, const char *b, uint key_length)
+{
+ uint a_length;
+ uint b_length;
+ if (key_length > 255)
+ {
+ a_length=uint2korr(a); a+=2;
+ b_length=uint2korr(b); b+=2;
+ }
+ else
+ {
+ a_length= (uint) (uchar) *a++;
+ b_length= (uint) (uchar) *b++;
+ }
+ if (binary_flag)
+ {
+ int cmp= memcmp(a,b,min(a_length,b_length));
+ return cmp ? cmp : (int) (a_length - b_length);
+ }
+ return my_sortncmp(a,a_length, b,b_length);
+}
+
+uint Field_varstring::packed_col_length(const char *ptr)
+{
+ if (field_length > 255)
+ return uint2korr(ptr)+2;
+ else
+ return (uint) ((uchar) *ptr)+1;
+}
+
+uint Field_varstring::max_packed_col_length(uint max_length)
+{
+ return (field_length > 255 ? 2 : 1)+max_length;
+}
+
+/****************************************************************************
+** blob type
+** A blob is saved as a length and a pointer. The length is stored in the
+** packlength slot and may be from 1-4.
+****************************************************************************/
+
+Field_blob::Field_blob(char *ptr_arg, uchar *null_ptr_arg, uint 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)
+ :Field_str(ptr_arg, (1L << min(blob_pack_length,3)*8)-1L,
+ null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg,
+ table_arg),
+ packlength(blob_pack_length),binary_flag(binary_arg)
+{
+ flags|= BLOB_FLAG;
+ if (binary_arg)
+ flags|=BINARY_FLAG;
+ if (table)
+ table->blob_fields++;
+}
+
+
+void Field_blob::store_length(ulong number)
+{
+ switch (packlength) {
+ case 1:
+ if (number > 255)
+ {
+ number=255;
+ current_thd->cuted_fields++;
+ }
+ ptr[0]= (uchar) number;
+ break;
+ case 2:
+ if (number > (uint16) ~0)
+ {
+ number= (uint16) ~0;
+ current_thd->cuted_fields++;
+ }
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ int2store(ptr,(unsigned short) number);
+ }
+ else
+#endif
+ shortstore(ptr,(unsigned short) number);
+ break;
+ case 3:
+ if (number > (ulong) (1L << 24))
+ {
+ number= (ulong) (1L << 24)-1L;
+ current_thd->cuted_fields++;
+ }
+ int3store(ptr,number);
+ break;
+ case 4:
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ int4store(ptr,number);
+ }
+ else
+#endif
+ longstore(ptr,number);
+ }
+}
+
+
+ulong Field_blob::get_length(const char *pos)
+{
+ switch (packlength) {
+ case 1:
+ return (ulong) (uchar) pos[0];
+ case 2:
+ {
+ uint16 tmp;
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ tmp=sint2korr(pos);
+ else
+#endif
+ shortget(tmp,pos);
+ return (ulong) tmp;
+ }
+ case 3:
+ return (ulong) uint3korr(pos);
+ case 4:
+ {
+ uint32 tmp;
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ tmp=uint4korr(pos);
+ else
+#endif
+ longget(tmp,pos);
+ return (ulong) tmp;
+ }
+ }
+ return 0; // Impossible
+}
+
+
+void Field_blob::store(const char *from,uint len)
+{
+ if (!len)
+ {
+ bzero(ptr,Field_blob::pack_length());
+ }
+ else
+ {
+#ifdef USE_TIS620
+ char *th_ptr=0;
+#endif
+ Field_blob::store_length(len);
+ if (table->copy_blobs || len <= MAX_FIELD_WIDTH)
+ { // Must make a copy
+#ifdef USE_TIS620
+ if(!binary_flag)
+ {
+ /* If there isn't enough memory, use original string */
+ if ((th_ptr=(char * ) my_malloc(sizeof(char) * len,MYF(0))))
+ {
+ ThNormalize((uchar *) th_ptr, len, (uchar *) from, len);
+ from= (const char*) th_ptr;
+ }
+ }
+#endif /* USE_TIS620 */
+ value.copy(from,len);
+ from=value.ptr();
+#ifdef USE_TIS620
+ my_free(th_ptr,MYF(MY_ALLOW_ZERO_PTR));
+#endif
+ }
+ bmove(ptr+packlength,(char*) &from,sizeof(char*));
+ }
+}
+
+
+void Field_blob::store(double nr)
+{
+ value.set(nr);
+ Field_blob::store(value.ptr(),value.length());
+}
+
+
+void Field_blob::store(longlong nr)
+{
+ value.set(nr);
+ Field_blob::store(value.ptr(),value.length());
+}
+
+
+double Field_blob::val_real(void)
+{
+ char *blob;
+
+ memcpy_fixed(&blob,ptr+packlength,sizeof(char*));
+ if (!blob)
+ return 0.0;
+ ulong length=get_length(ptr);
+
+ char save=blob[length]; // Ok to patch blob in NISAM
+ blob[length]=0;
+ double nr=atof(blob);
+ blob[length]=save;
+ return nr;
+}
+
+
+longlong Field_blob::val_int(void)
+{
+ char *blob;
+ memcpy_fixed(&blob,ptr+packlength,sizeof(char*));
+ if (!blob)
+ return 0;
+ ulong length=get_length(ptr);
+
+ char save=blob[length]; // Ok to patch blob in NISAM
+ blob[length]=0;
+ longlong nr=strtoll(blob,NULL,10);
+ blob[length]=save;
+ return nr;
+}
+
+
+String *Field_blob::val_str(String *val_buffer __attribute__((unused)),
+ String *val_ptr)
+{
+ char *blob;
+ memcpy_fixed(&blob,ptr+packlength,sizeof(char*));
+ if (!blob)
+ val_ptr->length(0);
+ else
+ val_ptr->set((const char*) blob,get_length(ptr));
+ return val_ptr;
+}
+
+
+int Field_blob::cmp(const char *a,ulong a_length, const char *b,
+ ulong b_length)
+{
+ int diff;
+ if (binary_flag)
+ diff=memcmp(a,b,min(a_length,b_length));
+ else
+ diff=my_sortcmp(a,b,min(a_length,b_length));
+ return diff ? diff : (int) (a_length - b_length);
+}
+
+
+int Field_blob::cmp(const char *a_ptr, const char *b_ptr)
+{
+ char *blob1,*blob2;
+ memcpy_fixed(&blob1,a_ptr+packlength,sizeof(char*));
+ memcpy_fixed(&blob2,b_ptr+packlength,sizeof(char*));
+ return Field_blob::cmp(blob1,get_length(a_ptr),
+ blob2,get_length(b_ptr));
+}
+
+
+int Field_blob::cmp_offset(uint row_offset)
+{
+ return Field_blob::cmp(ptr,ptr+row_offset);
+}
+
+
+int Field_blob::cmp_binary_offset(uint row_offset)
+{
+ return cmp_binary(ptr, ptr+row_offset);
+}
+
+
+int Field_blob::cmp_binary(const char *a_ptr, const char *b_ptr,
+ ulong max_length)
+{
+ char *a,*b;
+ uint diff;
+ ulong 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);
+ if (a_length > max_length)
+ a_length=max_length;
+ b_length=get_length(b_ptr);
+ if (b_length > max_length)
+ b_length=max_length;
+ diff=memcmp(a,b,min(a_length,b_length));
+ return diff ? diff : (int) (a_length - b_length);
+}
+
+
+/* The following is used only when comparing a key */
+
+void Field_blob::get_key_image(char *buff,uint length)
+{
+ length-=HA_KEY_BLOB_LENGTH;
+ ulong blob_length=get_length(ptr);
+ char *blob;
+ if ((ulong) length > blob_length)
+ length=(uint) blob_length;
+ int2store(buff,length);
+ get_ptr(&blob);
+ memcpy(buff+2,blob,length);
+}
+
+void Field_blob::set_key_image(char *buff,uint length)
+{
+ length=uint2korr(buff);
+ Field_blob::store(buff+2,length);
+}
+
+int Field_blob::key_cmp(const byte *key_ptr, uint max_key_length)
+{
+ char *blob1;
+ uint blob_length=get_length(ptr);
+ max_key_length-=2;
+ memcpy_fixed(&blob1,ptr+packlength,sizeof(char*));
+ return Field_blob::cmp(blob1,min(blob_length, max_key_length),
+ (char*) key_ptr+2,uint2korr(key_ptr));
+}
+
+int Field_blob::key_cmp(const byte *a,const byte *b)
+{
+ return Field_blob::cmp((char*) a+2,uint2korr(a),
+ (char*) b+2,uint2korr(b));
+}
+
+
+void Field_blob::sort_string(char *to,uint length)
+{
+ char *blob;
+ uint blob_length=get_length();
+#ifdef USE_STRCOLL
+ uint blob_org_length=blob_length;
+#endif
+ if (!blob_length)
+ bzero(to,length);
+ else
+ {
+ if (blob_length > length)
+ blob_length=length;
+ memcpy_fixed(&blob,ptr+packlength,sizeof(char*));
+ if (binary_flag)
+ {
+ memcpy(to,blob,blob_length);
+ to+=blob_length;
+ }
+ else
+ {
+#ifdef USE_STRCOLL
+ if (use_strcoll(default_charset_info))
+ {
+ blob_length=my_strnxfrm(default_charset_info,
+ (unsigned char *)to,(unsigned char *)blob,
+ length,blob_org_length);
+ if (blob_length >= length)
+ return;
+ to+=blob_length;
+ }
+ else
+#endif
+ for (char *end=blob+blob_length ; blob != end ;)
+ *to++=(char) my_sort_order[(uint) (uchar) *blob++];
+ }
+ bzero(to,length-blob_length);
+ }
+}
+
+
+void Field_blob::sql_type(String &res) const
+{
+ const char *str;
+ switch (packlength) {
+ default: str="tiny"; break;
+ case 2: str=""; break;
+ case 3: str="medium"; break;
+ case 4: str="long"; break;
+ }
+ res.set(str,strlen(str));
+ res.append(binary_flag ? "blob" : "text");
+}
+
+
+/* Keys for blobs are like keys on varchars */
+
+int Field_blob::pack_cmp(const char *a, const char *b, uint key_length)
+{
+ uint a_length;
+ uint b_length;
+ if (key_length > 255)
+ {
+ a_length=uint2korr(a); a+=2;
+ b_length=uint2korr(b); b+=2;
+ }
+ else
+ {
+ a_length= (uint) (uchar) *a++;
+ b_length= (uint) (uchar) *b++;
+ }
+ if (binary_flag)
+ {
+ int cmp= memcmp(a,b,min(a_length,b_length));
+ return cmp ? cmp : (int) (a_length - b_length);
+ }
+ return my_sortncmp(a,a_length, b,b_length);
+}
+
+char *Field_blob::pack_key(char *to, const char *from, uint max_length)
+{
+ uint length=uint2korr(to);
+ if (length > max_length)
+ length=max_length;
+ *to++= (length & 255);
+ if (max_length > 255)
+ *to++= (uchar) (length >> 8);
+ if (length)
+ memcpy(to, from+2, length);
+ return to+length;
+}
+
+/****************************************************************************
+** enum type.
+** This is a string which only can have a selection of different values.
+** If one uses this string in a number context one gets the type number.
+****************************************************************************/
+
+enum ha_base_keytype Field_enum::key_type() const
+{
+ switch (packlength) {
+ default: return HA_KEYTYPE_BINARY;
+ case 2: return HA_KEYTYPE_USHORT_INT;
+ case 3: return HA_KEYTYPE_UINT24;
+ case 4: return HA_KEYTYPE_ULONG_INT;
+ case 8: return HA_KEYTYPE_ULONGLONG;
+ }
+}
+
+void Field_enum::store_type(ulonglong value)
+{
+ switch (packlength) {
+ case 1: ptr[0]= (uchar) value; break;
+ case 2:
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ int2store(ptr,(unsigned short) value);
+ }
+ else
+#endif
+ shortstore(ptr,(unsigned short) value);
+ break;
+ case 3: int3store(ptr,(long) value); break;
+ case 4:
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ int4store(ptr,value);
+ }
+ else
+#endif
+ longstore(ptr,(long) value);
+ break;
+ case 8:
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ int8store(ptr,value);
+ }
+ else
+#endif
+ longlongstore(ptr,value); break;
+ }
+}
+
+
+uint find_enum(TYPELIB *lib,const char *x, uint length)
+{
+ const char *end=x+length;
+ while (end > x && isspace(end[-1]))
+ end--;
+
+ const char *i;
+ const char *j;
+ for (uint pos=0 ; (j=lib->type_names[pos]) ; pos++)
+ {
+ for (i=x ; i != end && toupper(*i) == toupper(*j) ; i++, j++) ;
+ if (i == end && ! *j)
+ return(pos+1);
+ }
+ return(0);
+}
+
+
+/*
+** Note. Storing a empty string in a enum field gives a warning
+** (if there isn't a empty value in the enum)
+*/
+
+void Field_enum::store(const char *from,uint length)
+{
+ uint tmp=find_enum(typelib,from,length);
+ {
+ if (!tmp)
+ {
+ current_thd->cuted_fields++;
+ Field_enum::store_type((longlong) 0);
+ }
+ else
+ store_type((ulonglong) tmp);
+ }
+}
+
+
+void Field_enum::store(double nr)
+{
+ Field_enum::store((longlong) nr);
+}
+
+
+void Field_enum::store(longlong nr)
+{
+ if ((uint) nr > typelib->count || nr == 0)
+ {
+ current_thd->cuted_fields++;
+ nr=0;
+ }
+ store_type((ulonglong) (uint) nr);
+}
+
+
+double Field_enum::val_real(void)
+{
+ return (double) Field_enum::val_int();
+}
+
+
+longlong Field_enum::val_int(void)
+{
+ switch (packlength) {
+ case 1:
+ return (longlong) (uchar) ptr[0];
+ case 2:
+ {
+ uint16 tmp;
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ tmp=sint2korr(ptr);
+ else
+#endif
+ shortget(tmp,ptr);
+ return (longlong) tmp;
+ }
+ case 3:
+ return (longlong) uint3korr(ptr);
+ case 4:
+ {
+ uint32 tmp;
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ tmp=uint4korr(ptr);
+ else
+#endif
+ longget(tmp,ptr);
+ return (longlong) tmp;
+ }
+ case 8:
+ {
+ longlong tmp;
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ tmp=sint8korr(ptr);
+ else
+#endif
+ longlongget(tmp,ptr);
+ return tmp;
+ }
+ }
+ return 0; // impossible
+}
+
+
+String *Field_enum::val_str(String *val_buffer __attribute__((unused)),
+ String *val_ptr)
+{
+ uint tmp=(uint) Field_enum::val_int();
+ if (!tmp || tmp > typelib->count)
+ val_ptr->length(0);
+ else
+ val_ptr->set((const char*) typelib->type_names[tmp-1],
+ strlen(typelib->type_names[tmp-1]));
+ return val_ptr;
+}
+
+int Field_enum::cmp(const char *a_ptr, const char *b_ptr)
+{
+ char *old=ptr;
+ ptr=(char*) a_ptr;
+ ulonglong a=Field_enum::val_int();
+ ptr=(char*) b_ptr;
+ ulonglong b=Field_enum::val_int();
+ ptr=old;
+ return (a < b) ? -1 : (a > b) ? 1 : 0;
+}
+
+void Field_enum::sort_string(char *to,uint length __attribute__((unused)))
+{
+ ulonglong value=Field_enum::val_int();
+ to+=packlength-1;
+ for (uint i=0 ; i < packlength ; i++)
+ {
+ *to-- = (uchar) (value & 255);
+ value>>=8;
+ }
+}
+
+
+void Field_enum::sql_type(String &res) const
+{
+ res.length(0);
+ res.append("enum(");
+
+ bool flag=0;
+ for (const char **pos=typelib->type_names; *pos ; pos++)
+ {
+ if (flag)
+ res.append(',');
+ res.append('\'');
+ append_unescaped(&res,*pos);
+ res.append('\'');
+ flag=1;
+ }
+ res.append(')');
+}
+
+
+/****************************************************************************
+** set type.
+** This is a string which can have a collection of different values.
+** Each string value is separated with a ','.
+** For example "One,two,five"
+** If one uses this string in a number context one gets the bits as a longlong
+** number.
+****************************************************************************/
+
+ulonglong find_set(TYPELIB *lib,const char *x,uint length)
+{
+ const char *end=x+length;
+ while (end > x && isspace(end[-1]))
+ end--;
+
+ ulonglong found=0;
+ if (x != end)
+ {
+ const char *start=x;
+ bool error=0;
+ for (;;)
+ {
+ const char *pos=start;
+ for ( ; pos != end && *pos != field_separator ; pos++) ;
+ uint find=find_enum(lib,start,(uint) (pos-start));
+ if (!find)
+ error=1;
+ else
+ found|= ((longlong) 1 << (find-1));
+ if (pos == end)
+ break;
+ start=pos+1;
+ }
+ if (error)
+ current_thd->cuted_fields++;
+ }
+ return found;
+}
+
+
+void Field_set::store(const char *from,uint length)
+{
+ store_type(find_set(typelib,from,length));
+}
+
+
+void Field_set::store(longlong nr)
+{
+ if ((ulonglong) nr > (ulonglong) (((longlong) 1 << typelib->count) -
+ (longlong) 1))
+ {
+ nr&= (longlong) (((longlong) 1 << typelib->count) - (longlong) 1);
+ current_thd->cuted_fields++;
+ }
+ store_type((ulonglong) nr);
+}
+
+
+String *Field_set::val_str(String *val_buffer,
+ String *val_ptr __attribute__((unused)))
+{
+ ulonglong tmp=(ulonglong) Field_enum::val_int();
+ uint bitnr=0;
+
+ val_buffer->length(0);
+ while (tmp && bitnr < (uint) typelib->count)
+ {
+ if (tmp & 1)
+ {
+ if (val_buffer->length())
+ val_buffer->append(field_separator);
+ String str(typelib->type_names[bitnr],
+ strlen(typelib->type_names[bitnr]));
+ val_buffer->append(str);
+ }
+ tmp>>=1;
+ bitnr++;
+ }
+ return val_buffer;
+}
+
+
+void Field_set::sql_type(String &res) const
+{
+ res.length(0);
+ res.append("set(");
+
+ bool flag=0;
+ for (const char **pos=typelib->type_names; *pos ; pos++)
+ {
+ if (flag)
+ res.append(',');
+ res.append('\'');
+ append_unescaped(&res,*pos);
+ res.append('\'');
+ flag=1;
+ }
+ res.append(')');
+}
+
+/* returns 1 if the fields are equally defined */
+
+bool Field::eq_def(Field *field)
+{
+ if (real_type() != field->real_type() || binary() != field->binary() ||
+ pack_length() != field->pack_length())
+ return 0;
+ return 1;
+}
+
+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++)
+ if (my_strcasecmp(typelib->type_names[i],from_lib->type_names[i]))
+ return 0;
+ return 1;
+}
+
+bool Field_num::eq_def(Field *field)
+{
+ if (!Field::eq_def(field))
+ return 0;
+ Field_num *from_num= (Field_num*) field;
+
+ if (unsigned_flag != from_num->unsigned_flag ||
+ zerofill && !from_num->zerofill && !zero_pack() ||
+ dec != from_num->dec)
+ return 0;
+ return 1;
+}
+
+
+/*****************************************************************************
+** Handling of field and create_field
+*****************************************************************************/
+
+/*
+** Make a field from the .frm file info
+*/
+
+uint32 calc_pack_length(enum_field_types type,uint32 length)
+{
+ switch (type) {
+ case FIELD_TYPE_STRING:
+ case FIELD_TYPE_DECIMAL: return (length);
+ case FIELD_TYPE_VAR_STRING: return (length+2);
+ case FIELD_TYPE_YEAR:
+ case FIELD_TYPE_TINY : return 1;
+ case FIELD_TYPE_SHORT : return 2;
+ case FIELD_TYPE_INT24:
+ case FIELD_TYPE_NEWDATE:
+ case FIELD_TYPE_TIME: return 3;
+ case FIELD_TYPE_TIMESTAMP:
+ case FIELD_TYPE_DATE:
+ case FIELD_TYPE_LONG : return 4;
+ case FIELD_TYPE_FLOAT : return sizeof(float);
+ case FIELD_TYPE_DOUBLE: return sizeof(double);
+ case FIELD_TYPE_DATETIME:
+ case FIELD_TYPE_LONGLONG: return 8; /* Don't crash if no longlong */
+ case FIELD_TYPE_NULL : return 0;
+ case FIELD_TYPE_TINY_BLOB: return 1+portable_sizeof_char_ptr;
+ case FIELD_TYPE_BLOB: return 2+portable_sizeof_char_ptr;
+ case FIELD_TYPE_MEDIUM_BLOB: return 3+portable_sizeof_char_ptr;
+ 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
+ }
+ return 0; // This shouldn't happen
+}
+
+
+uint pack_length_to_packflag(uint type)
+{
+ switch (type) {
+ case 1: return f_settype((uint) FIELD_TYPE_TINY);
+ case 2: return f_settype((uint) FIELD_TYPE_SHORT);
+ case 3: return f_settype((uint) FIELD_TYPE_INT24);
+ case 4: return f_settype((uint) FIELD_TYPE_LONG);
+ case 8: return f_settype((uint) FIELD_TYPE_LONGLONG);
+ }
+ return 0; // This shouldn't happen
+}
+
+
+Field *make_field(char *ptr, uint32 field_length,
+ uchar *null_pos, uint null_bit,
+ uint pack_flag,
+ Field::utype unireg_check,
+ TYPELIB *interval,
+ const char *field_name,
+ struct st_table *table)
+{
+ if (!f_maybe_null(pack_flag))
+ {
+ null_pos=0;
+ null_bit=0;
+ }
+ if (f_is_alpha(pack_flag))
+ {
+ if (!f_is_packed(pack_flag))
+ return new Field_string(ptr,field_length,null_pos,null_bit,
+ unireg_check, field_name, table,
+ f_is_binary(pack_flag) != 0);
+
+ uint pack_length=calc_pack_length((enum_field_types)
+ f_packtype(pack_flag),
+ field_length);
+
+ if (f_is_blob(pack_flag))
+ return new Field_blob(ptr,null_pos,null_bit,
+ unireg_check, field_name, table,
+ pack_length,f_is_binary(pack_flag) != 0);
+ if (interval)
+ {
+ if (f_is_enum(pack_flag))
+ return new Field_enum(ptr,field_length,null_pos,null_bit,
+ unireg_check, field_name, table,
+ pack_length, interval);
+ else
+ return new Field_set(ptr,field_length,null_pos,null_bit,
+ unireg_check, field_name, table,
+ pack_length, interval);
+ }
+ }
+
+ switch ((enum enum_field_types) f_packtype(pack_flag)) {
+ case FIELD_TYPE_DECIMAL:
+ return new Field_decimal(ptr,field_length,null_pos,null_bit,
+ unireg_check, field_name, table,
+ f_decimals(pack_flag),
+ f_is_zerofill(pack_flag) != 0,
+ f_is_dec(pack_flag) == 0);
+ case FIELD_TYPE_FLOAT:
+ return new Field_float(ptr,field_length,null_pos,null_bit,
+ unireg_check, field_name, table,
+ f_decimals(pack_flag),
+ f_is_zerofill(pack_flag) != 0,
+ f_is_dec(pack_flag)== 0);
+ case FIELD_TYPE_DOUBLE:
+ return new Field_double(ptr,field_length,null_pos,null_bit,
+ unireg_check, field_name, table,
+ f_decimals(pack_flag),
+ f_is_zerofill(pack_flag) != 0,
+ f_is_dec(pack_flag)== 0);
+ case FIELD_TYPE_TINY:
+ return new Field_tiny(ptr,field_length,null_pos,null_bit,
+ unireg_check, field_name, table,
+ f_is_zerofill(pack_flag) != 0,
+ f_is_dec(pack_flag) == 0);
+ case FIELD_TYPE_SHORT:
+ return new Field_short(ptr,field_length,null_pos,null_bit,
+ unireg_check, field_name, table,
+ f_is_zerofill(pack_flag) != 0,
+ f_is_dec(pack_flag) == 0);
+ case FIELD_TYPE_INT24:
+ return new Field_medium(ptr,field_length,null_pos,null_bit,
+ unireg_check, field_name, table,
+ f_is_zerofill(pack_flag) != 0,
+ f_is_dec(pack_flag) == 0);
+ case FIELD_TYPE_LONG:
+ return new Field_long(ptr,field_length,null_pos,null_bit,
+ unireg_check, field_name, table,
+ f_is_zerofill(pack_flag) != 0,
+ f_is_dec(pack_flag) == 0);
+ case FIELD_TYPE_LONGLONG:
+ return new Field_longlong(ptr,field_length,null_pos,null_bit,
+ unireg_check, field_name, table,
+ f_is_zerofill(pack_flag) != 0,
+ f_is_dec(pack_flag) == 0);
+ case FIELD_TYPE_TIMESTAMP:
+ return new Field_timestamp(ptr,field_length,
+ unireg_check, field_name, table);
+ case FIELD_TYPE_YEAR:
+ return new Field_year(ptr,field_length,null_pos,null_bit,
+ unireg_check, field_name, table);
+ case FIELD_TYPE_DATE:
+ return new Field_date(ptr,null_pos,null_bit,
+ unireg_check, field_name, table);
+ case FIELD_TYPE_NEWDATE:
+ return new Field_newdate(ptr,null_pos,null_bit,
+ unireg_check, field_name, table);
+ case FIELD_TYPE_TIME:
+ return new Field_time(ptr,null_pos,null_bit,
+ unireg_check, field_name, table);
+ case FIELD_TYPE_DATETIME:
+ 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);
+ }
+ return 0; // Impossible (Wrong version)
+}
+
+
+/* Create a field suitable for create of table */
+
+create_field::create_field(Field *old_field,bool ignore_default)
+{
+ field= old_field;
+ field_name=change=old_field->field_name;
+ length= old_field->field_length;
+ flags= old_field->flags;
+ unireg_check=old_field->unireg_check;
+ pack_length=old_field->pack_length();
+ sql_type= old_field->real_type();
+
+ /* Fix if the original table had 4 byte pointer blobs */
+ if (flags & BLOB_FLAG)
+ pack_length= (pack_length- old_field->table->blob_ptr_size +
+ portable_sizeof_char_ptr);
+ decimals= old_field->decimals();
+ if (sql_type == FIELD_TYPE_STRING)
+ {
+ sql_type=old_field->type();
+ decimals=0;
+ }
+ if (flags & (ENUM_FLAG | SET_FLAG))
+ interval= ((Field_enum*) old_field)->typelib;
+ else
+ interval=0;
+ if (!ignore_default && !old_field->is_real_null() && ! (flags & BLOB_FLAG) &&
+ old_field->type() != FIELD_TYPE_TIMESTAMP && old_field->ptr)
+ {
+ char buff[MAX_FIELD_WIDTH],*pos;
+ String tmp(buff,sizeof(buff));
+ field->val_str(&tmp,&tmp);
+ pos= (char*) sql_memdup(tmp.ptr(),tmp.length()+1);
+ pos[tmp.length()]=0;
+ def=new Item_string(pos,tmp.length());
+ }
+ else
+ def=0;
+}
diff --git a/sql/field.h b/sql/field.h
new file mode 100644
index 00000000000..1d819231bd2
--- /dev/null
+++ b/sql/field.h
@@ -0,0 +1,1082 @@
+/* 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.
+*/
+
+#ifdef __GNUC__
+#pragma interface /* gcc class implementation */
+#endif
+
+#define NOT_FIXED_DEC 31
+
+class Send_field;
+struct st_cache_field;
+
+class Field {
+ Field(const Item &); /* Prevent use of theese */
+ void operator=(Field &);
+public:
+ static void *operator new(size_t size) {return (void*) sql_alloc(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,CASEDN,NEXT_NUMBER,INTERVAL_FIELD,BIT_FIELD,
+ TIMESTAMP_FIELD,CAPITALIZE,BLOB_FIELD};
+ 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; // Which keys a field is in
+ 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,
+ utype unireg_check_arg, const char *field_name_arg,
+ struct st_table *table_arg);
+ virtual ~Field() {}
+ virtual void store(const char *to,uint length)=0;
+ virtual void store(double nr)=0;
+ virtual void store(longlong nr)=0;
+ virtual void store_time(TIME *ltime,timestamp_type t_type);
+ virtual double val_real(void)=0;
+ virtual longlong val_int(void)=0;
+ virtual String *val_str(String*,String *)=0;
+ virtual Item_result result_type () const=0;
+ virtual Item_result cmp_type () const { return result_type(); }
+ bool eq(Field *field) { return ptr == field->ptr; }
+ virtual bool eq_def(Field *field);
+ virtual uint32 pack_length() const { return (uint32) field_length; }
+ virtual void reset(void) { bzero(ptr,pack_length()); }
+ virtual void reset_fields() {}
+ 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; }
+ virtual uint32 key_length() const { return pack_length(); }
+ virtual enum_field_types type() const =0;
+ 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)
+ { return memcmp(a,b,pack_length()); }
+ virtual int cmp_offset(uint row_offset)
+ { return memcmp(ptr,ptr+row_offset,pack_length()); }
+ virtual int cmp_binary_offset(uint row_offset)
+ { return memcmp(ptr,ptr+row_offset,pack_length()); }
+ virtual int key_cmp(const byte *a,const byte *b)
+ { return cmp((char*) a,(char*) b); }
+ virtual int key_cmp(const byte *str, uint length)
+ { return cmp(ptr,(char*) str); }
+ virtual uint decimals() const { return 0; }
+ 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
+ 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)
+ { return null_ptr ? (null_ptr[row_offset] & null_bit ? 1 : 0) : 0; }
+ 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; }
+ 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 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=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)
+ {
+ 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)
+ {
+ ptr=ADD_TO_PTR(ptr,ptr_diff,char*);
+ if (null_ptr)
+ null_ptr=ADD_TO_PTR(null_ptr,ptr_diff,uchar*);
+ }
+ inline void get_image(char *buff,uint length)
+ { memcpy(buff,ptr,length); }
+ inline void set_image(char *buff,uint length)
+ { memcpy(ptr,buff,length); }
+ virtual void get_key_image(char *buff,uint length)
+ { get_image(buff,length); }
+ virtual void set_key_image(char *buff,uint length)
+ { set_image(buff,length); }
+ inline int cmp_image(char *buff,uint length)
+ {
+ if (binary())
+ return memcmp(ptr,buff,length);
+ else
+ return my_casecmp(ptr,buff,length);
+ }
+ inline longlong val_int_offset(uint row_offset)
+ {
+ ptr+=row_offset;
+ longlong tmp=val_int();
+ ptr-=row_offset;
+ return tmp;
+ }
+ bool send(String *packet);
+ virtual char *pack(char* to, const char *from, uint max_length=~(uint) 0)
+ {
+ uint length=pack_length();
+ memcpy(to,from,length);
+ return to+length;
+ }
+ virtual const char *unpack(char* to, const char *from)
+ {
+ uint length=pack_length();
+ memcpy(to,from,length);
+ return from+length;
+ }
+ virtual char *keypack(char* to, const char *from, uint max_length=~(uint) 0)
+ {
+ return pack(to,from,max_length);
+ }
+ virtual uint packed_col_length(const char *to)
+ { return pack_length();}
+ virtual uint max_packed_col_length(uint max_length)
+ { return pack_length();}
+
+ virtual int pack_cmp(const char *a,const char *b, uint key_length_arg)
+ { return cmp(a,b); }
+ 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);
+ virtual bool get_time(TIME *ltime);
+ friend bool reopen_table(THD *,struct st_table *,bool);
+ friend int cre_myisam(my_string name, register TABLE *form, uint options,
+ ulonglong auto_increment_value);
+ friend class Copy_field;
+ friend class Item_avg_field;
+ friend class Item_std_field;
+ friend class Item_sum_num;
+ friend class Item_sum_sum;
+ friend class Item_sum_str;
+ friend class Item_sum_count;
+ friend class Item_sum_avg;
+ friend class Item_sum_std;
+ friend class Item_sum_min;
+ friend class Item_sum_max;
+};
+
+
+class Field_num :public Field {
+public:
+ const uint8 dec;
+ 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,
+ const char *field_name_arg,
+ struct st_table *table_arg,
+ uint 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)
+ {
+ if (zerofill)
+ flags|=ZEROFILL_FLAG;
+ if (unsigned_flag)
+ flags|=UNSIGNED_FLAG;
+ }
+ Item_result result_type () const { return REAL_RESULT; }
+ void prepend_zeros(String *value);
+ void add_zerofill_and_unsigned(String &res) const;
+ friend class create_field;
+ void make_field(Send_field *);
+ uint decimals() const { return dec; }
+ uint size_of() const { return sizeof(*this); }
+ bool eq_def(Field *field);
+};
+
+
+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,
+ const char *field_name_arg,
+ struct st_table *table_arg)
+ :Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg, table_arg)
+ {}
+ Item_result result_type () const { return STRING_RESULT; }
+ uint decimals() const { return NOT_FIXED_DEC; }
+ friend class create_field;
+ void make_field(Send_field *);
+ uint size_of() const { return sizeof(*this); }
+};
+
+
+class Field_decimal :public Field_num {
+public:
+ Field_decimal(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+ uint 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)
+ :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)
+ {}
+ enum_field_types type() const { return FIELD_TYPE_DECIMAL;}
+ enum ha_base_keytype key_type() const
+ { return zerofill ? HA_KEYTYPE_BINARY : HA_KEYTYPE_NUM; }
+ void reset(void);
+ void store(const char *to,uint length);
+ void store(double nr);
+ void store(longlong nr);
+ double val_real(void);
+ longlong val_int(void);
+ String *val_str(String*,String *);
+ int cmp(const char *,const char*);
+ void sort_string(char *buff,uint length);
+ void overflow(bool negative);
+ bool zero_pack() const { return 0; }
+ void sql_type(String &str) const;
+};
+
+
+class Field_tiny :public Field_num {
+public:
+ Field_tiny(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+ uint null_bit_arg,
+ enum utype unireg_check_arg, const char *field_name_arg,
+ struct st_table *table_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,
+ 0, zero_arg,unsigned_arg)
+ {}
+ enum Item_result result_type () const { return INT_RESULT; }
+ enum_field_types type() const { return FIELD_TYPE_TINY;}
+ enum ha_base_keytype key_type() const
+ { return unsigned_flag ? HA_KEYTYPE_BINARY : HA_KEYTYPE_INT8; }
+ void store(const char *to,uint length);
+ void store(double nr);
+ void store(longlong nr);
+ double val_real(void);
+ longlong val_int(void);
+ String *val_str(String*,String *);
+ int cmp(const char *,const char*);
+ void sort_string(char *buff,uint length);
+ uint32 pack_length() const { return 1; }
+ void sql_type(String &str) const;
+};
+
+
+class Field_short :public Field_num {
+public:
+ Field_short(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+ uint null_bit_arg,
+ enum utype unireg_check_arg, const char *field_name_arg,
+ struct st_table *table_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,
+ 0, zero_arg,unsigned_arg)
+ {}
+ enum Item_result result_type () const { return INT_RESULT; }
+ enum_field_types type() const { return FIELD_TYPE_SHORT;}
+ enum ha_base_keytype key_type() const
+ { return unsigned_flag ? HA_KEYTYPE_USHORT_INT : HA_KEYTYPE_SHORT_INT;}
+ void store(const char *to,uint length);
+ void store(double nr);
+ void store(longlong nr);
+ double val_real(void);
+ longlong val_int(void);
+ String *val_str(String*,String *);
+ int cmp(const char *,const char*);
+ void sort_string(char *buff,uint length);
+ uint32 pack_length() const { return 2; }
+ void sql_type(String &str) const;
+};
+
+
+class Field_medium :public Field_num {
+public:
+ Field_medium(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+ uint null_bit_arg,
+ enum utype unireg_check_arg, const char *field_name_arg,
+ struct st_table *table_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,
+ 0, zero_arg,unsigned_arg)
+ {}
+ enum Item_result result_type () const { return INT_RESULT; }
+ enum_field_types type() const { return FIELD_TYPE_INT24;}
+ enum ha_base_keytype key_type() const
+ { return unsigned_flag ? HA_KEYTYPE_UINT24 : HA_KEYTYPE_INT24; }
+ void store(const char *to,uint length);
+ void store(double nr);
+ void store(longlong nr);
+ double val_real(void);
+ longlong val_int(void);
+ String *val_str(String*,String *);
+ int cmp(const char *,const char*);
+ void sort_string(char *buff,uint length);
+ uint32 pack_length() const { return 3; }
+ void sql_type(String &str) const;
+};
+
+
+class Field_long :public Field_num {
+public:
+ Field_long(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+ uint null_bit_arg,
+ enum utype unireg_check_arg, const char *field_name_arg,
+ struct st_table *table_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,
+ 0, zero_arg,unsigned_arg)
+ {}
+ Field_long(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,unsigned_arg)
+ {}
+ enum Item_result result_type () const { return INT_RESULT; }
+ enum_field_types type() const { return FIELD_TYPE_LONG;}
+ enum ha_base_keytype key_type() const
+ { return unsigned_flag ? HA_KEYTYPE_ULONG_INT : HA_KEYTYPE_LONG_INT; }
+ void store(const char *to,uint length);
+ void store(double nr);
+ void store(longlong nr);
+ double val_real(void);
+ longlong val_int(void);
+ String *val_str(String*,String *);
+ int cmp(const char *,const char*);
+ void sort_string(char *buff,uint length);
+ uint32 pack_length() const { return 4; }
+ void sql_type(String &str) const;
+};
+
+
+#ifdef HAVE_LONG_LONG
+class Field_longlong :public Field_num {
+public:
+ Field_longlong(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+ uint null_bit_arg,
+ enum utype unireg_check_arg, const char *field_name_arg,
+ struct st_table *table_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,
+ 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_num((char*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0,0,
+ NONE, field_name_arg, table_arg,0,0,0)
+ {}
+ enum Item_result result_type () const { return INT_RESULT; }
+ enum_field_types type() const { return FIELD_TYPE_LONGLONG;}
+ enum ha_base_keytype key_type() const
+ { return unsigned_flag ? HA_KEYTYPE_ULONGLONG : HA_KEYTYPE_LONGLONG; }
+ void store(const char *to,uint length);
+ void store(double nr);
+ void store(longlong nr);
+ double val_real(void);
+ longlong val_int(void);
+ String *val_str(String*,String *);
+ int cmp(const char *,const char*);
+ void sort_string(char *buff,uint length);
+ uint32 pack_length() const { return 8; }
+ void sql_type(String &str) const;
+};
+#endif
+
+class Field_float :public Field_num {
+public:
+ Field_float(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+ uint 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)
+ :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)
+ {}
+ enum_field_types type() const { return FIELD_TYPE_FLOAT;}
+ enum ha_base_keytype key_type() const { return HA_KEYTYPE_FLOAT; }
+ void store(const char *to,uint length);
+ void store(double nr);
+ void store(longlong nr);
+ double val_real(void);
+ longlong val_int(void);
+ String *val_str(String*,String *);
+ int cmp(const char *,const char*);
+ void sort_string(char *buff,uint length);
+ uint32 pack_length() const { return sizeof(float); }
+ void sql_type(String &str) const;
+};
+
+
+class Field_double :public Field_num {
+public:
+ Field_double(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+ uint 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)
+ :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)
+ :Field_num((char*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0, (uint) 0,
+ NONE, field_name_arg, table_arg,dec_arg,0,0)
+ {}
+ enum_field_types type() const { return FIELD_TYPE_DOUBLE;}
+ enum ha_base_keytype key_type() const { return HA_KEYTYPE_DOUBLE; }
+ void store(const char *to,uint length);
+ void store(double nr);
+ void store(longlong nr);
+ double val_real(void);
+ longlong val_int(void);
+ String *val_str(String*,String *);
+ int cmp(const char *,const char*);
+ void sort_string(char *buff,uint length);
+ uint32 pack_length() const { return sizeof(double); }
+ void sql_type(String &str) const;
+};
+
+
+/* Everything saved in this will disapper. It will always return NULL */
+
+class Field_null :public Field_str {
+ static uchar null[1];
+public:
+ Field_null(char *ptr_arg, uint32 len_arg,
+ enum utype unireg_check_arg, const char *field_name_arg,
+ struct st_table *table_arg)
+ :Field_str(ptr_arg, len_arg, null, 1,
+ unireg_check_arg, field_name_arg, table_arg)
+ {}
+ enum_field_types type() const { return FIELD_TYPE_NULL;}
+ void store(const char *to, uint length) { null[0]=1; }
+ void store(double nr) { null[0]=1; }
+ void store(longlong nr) { null[0]=1; }
+ double val_real(void) { return 0.0;}
+ longlong val_int(void) { return 0;}
+ String *val_str(String *value,String *value2)
+ { value2->length(0); return value2;}
+ int cmp(const char *a, const char *b) { return 0;}
+ void sort_string(char *buff, uint length) {}
+ uint32 pack_length() const { return 0; }
+ void sql_type(String &str) const { str.set("null",4); }
+ uint size_of() const { return sizeof(*this); }
+};
+
+
+class Field_timestamp :public Field_num {
+public:
+ Field_timestamp(char *ptr_arg, uint32 len_arg,
+ enum utype unireg_check_arg, const char *field_name_arg,
+ struct st_table *table_arg);
+ enum Item_result result_type () const { return INT_RESULT; }
+ enum_field_types type() const { return FIELD_TYPE_TIMESTAMP;}
+ enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONG_INT; }
+ void store(const char *to,uint length);
+ void store(double nr);
+ void store(longlong nr);
+ double val_real(void);
+ longlong val_int(void);
+ String *val_str(String*,String *);
+ int cmp(const char *,const char*);
+ void sort_string(char *buff,uint length);
+ uint32 pack_length() const { return 4; }
+ void sql_type(String &str) const;
+ bool store_for_compare() { return 1; }
+ bool zero_pack() const { return 0; }
+ void set_time();
+ inline long get_timestamp()
+ {
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ return sint4korr(ptr);
+#endif
+ long tmp;
+ longget(tmp,ptr);
+ return tmp;
+ }
+ void fill_and_store(char *from,uint len);
+ bool get_date(TIME *ltime,bool fuzzydate);
+ bool get_time(TIME *ltime);
+};
+
+
+class Field_year :public Field_tiny {
+public:
+ Field_year(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+ uint 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,
+ unireg_check_arg, field_name_arg, table_arg, 1, 1)
+ {}
+ enum_field_types type() const { return FIELD_TYPE_YEAR;}
+ void store(const char *to,uint length);
+ void store(double nr);
+ void store(longlong nr);
+ double val_real(void);
+ longlong val_int(void);
+ String *val_str(String*,String *);
+ void sql_type(String &str) const;
+};
+
+
+class Field_date :public Field_str {
+public:
+ Field_date(char *ptr_arg, uchar *null_ptr_arg, uint 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)
+ {}
+ 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; }
+ void store(const char *to,uint length);
+ void store(double nr);
+ void store(longlong nr);
+ double val_real(void);
+ longlong val_int(void);
+ String *val_str(String*,String *);
+ int cmp(const char *,const char*);
+ void sort_string(char *buff,uint length);
+ uint32 pack_length() const { return 4; }
+ void sql_type(String &str) const;
+ bool store_for_compare() { return 1; }
+ bool zero_pack() const { return 1; }
+};
+
+class Field_newdate :public Field_str {
+public:
+ Field_newdate(char *ptr_arg, uchar *null_ptr_arg, uint 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)
+ {}
+ enum_field_types type() const { return FIELD_TYPE_DATE;}
+ enum_field_types real_type() const { return FIELD_TYPE_NEWDATE; }
+ enum ha_base_keytype key_type() const { return HA_KEYTYPE_UINT24; }
+ enum Item_result cmp_type () const { return INT_RESULT; }
+ void store(const char *to,uint length);
+ void store(double nr);
+ void store(longlong nr);
+ void store_time(TIME *ltime,timestamp_type type);
+ double val_real(void);
+ longlong val_int(void);
+ String *val_str(String*,String *);
+ int cmp(const char *,const char*);
+ void sort_string(char *buff,uint length);
+ uint32 pack_length() const { return 3; }
+ void sql_type(String &str) const;
+ bool store_for_compare() { return 1; }
+ bool zero_pack() const { return 1; }
+ bool get_date(TIME *ltime,bool fuzzydate);
+ bool get_time(TIME *ltime);
+};
+
+
+class Field_time :public Field_str {
+public:
+ Field_time(char *ptr_arg, uchar *null_ptr_arg, uint 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)
+ {}
+ 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; }
+ void store(const char *to,uint length);
+ void store(double nr);
+ void store(longlong nr);
+ double val_real(void);
+ longlong val_int(void);
+ String *val_str(String*,String *);
+ bool get_time(TIME *ltime);
+ int cmp(const char *,const char*);
+ void sort_string(char *buff,uint length);
+ uint32 pack_length() const { return 3; }
+ void sql_type(String &str) const;
+ bool store_for_compare() { return 1; }
+ bool zero_pack() const { return 1; }
+};
+
+
+class Field_datetime :public Field_str {
+public:
+ Field_datetime(char *ptr_arg, uchar *null_ptr_arg, uint 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)
+ {}
+ enum_field_types type() const { return FIELD_TYPE_DATETIME;}
+#ifdef HAVE_LONG_LONG
+ enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONGLONG; }
+#endif
+ enum Item_result cmp_type () const { return INT_RESULT; }
+ void store(const char *to,uint length);
+ void store(double nr);
+ void store(longlong nr);
+ void store_time(TIME *ltime,timestamp_type type);
+ double val_real(void);
+ longlong val_int(void);
+ String *val_str(String*,String *);
+ int cmp(const char *,const char*);
+ 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; }
+ bool zero_pack() const { return 1; }
+ bool get_date(TIME *ltime,bool fuzzydate);
+ bool get_time(TIME *ltime);
+};
+
+
+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,
+ 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,
+ unireg_check_arg, field_name_arg, table_arg),
+ binary_flag(binary_arg)
+ {
+ if (binary_arg)
+ flags|=BINARY_FLAG;
+ }
+ Field_string(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg,
+ struct st_table *table_arg, bool binary_arg)
+ :Field_str((char*) 0,len_arg, maybe_null_arg ? (uchar*) "": 0,0,
+ NONE, field_name_arg, table_arg),
+ binary_flag(binary_arg)
+ {
+ if (binary_arg)
+ flags|=BINARY_FLAG;
+ }
+
+ enum_field_types type() const
+ {
+ return ((table && table->db_create_options & HA_OPTION_PACK_RECORD &&
+ field_length >= 4) ?
+ FIELD_TYPE_VAR_STRING : FIELD_TYPE_STRING);
+ }
+ enum ha_base_keytype key_type() const
+ { return binary_flag ? HA_KEYTYPE_BINARY : HA_KEYTYPE_TEXT; }
+ bool zero_pack() const { return 0; }
+ bool binary() const { return binary_flag; }
+ void reset(void) { bfill(ptr,field_length,' '); }
+ void store(const char *to,uint length);
+ void store(double nr);
+ void store(longlong nr);
+ double val_real(void);
+ longlong val_int(void);
+ String *val_str(String*,String *);
+ int cmp(const char *,const char*);
+ void sort_string(char *buff,uint length);
+ void sql_type(String &str) const;
+ char *pack(char *to, const char *from, uint max_length=~(uint) 0);
+ const char *unpack(char* to, const char *from);
+ int pack_cmp(const char *a,const char *b,uint key_length);
+ uint packed_col_length(const char *to);
+ uint max_packed_col_length(uint max_length);
+ uint size_of() const { return sizeof(*this); }
+ enum_field_types real_type() const { return FIELD_TYPE_STRING; }
+};
+
+
+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,
+ 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,
+ unireg_check_arg, field_name_arg, table_arg),
+ binary_flag(binary_arg)
+ {
+ if (binary_arg)
+ flags|=BINARY_FLAG;
+ }
+ Field_varstring(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg,
+ struct st_table *table_arg, bool binary_arg)
+ :Field_str((char*) 0,len_arg, maybe_null_arg ? (uchar*) "": 0,0,
+ NONE, field_name_arg, table_arg),
+ binary_flag(binary_arg)
+ {
+ if (binary_arg)
+ flags|=BINARY_FLAG;
+ }
+
+ enum_field_types type() const { return FIELD_TYPE_VAR_STRING; }
+ enum ha_base_keytype key_type() const
+ { return binary_flag ? HA_KEYTYPE_VARBINARY : HA_KEYTYPE_VARTEXT; }
+ bool zero_pack() const { return 0; }
+ bool binary() const { return binary_flag; }
+ void reset(void) { bzero(ptr,field_length+2); }
+ uint32 pack_length() const { return (uint32) field_length+2; }
+ uint32 key_length() const { return (uint32) field_length; }
+ void store(const char *to,uint length);
+ void store(double nr);
+ void store(longlong nr);
+ double val_real(void);
+ longlong val_int(void);
+ String *val_str(String*,String *);
+ int cmp(const char *,const char*);
+ void sort_string(char *buff,uint length);
+ void sql_type(String &str) const;
+ char *pack(char *to, const char *from, uint max_length=~(uint) 0);
+ const char *unpack(char* to, const char *from);
+ int pack_cmp(const char *a, const char *b, uint key_length);
+ uint packed_col_length(const char *to);
+ uint max_packed_col_length(uint max_length);
+ uint size_of() const { return sizeof(*this); }
+ enum_field_types real_type() const { return FIELD_TYPE_VAR_STRING; }
+};
+
+
+class Field_blob :public Field_str {
+ uint packlength;
+ String value; // For temporaries
+ bool binary_flag;
+public:
+ Field_blob(char *ptr_arg, uchar *null_ptr_arg, uint 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);
+ Field_blob(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg,
+ struct st_table *table_arg, bool binary_arg)
+ :Field_str((char*) 0,len_arg, maybe_null_arg ? (uchar*) "": 0,0,
+ NONE, field_name_arg, table_arg),
+ packlength(3),binary_flag(binary_arg)
+ {
+ flags|= BLOB_FLAG;
+ if (binary_arg)
+ flags|=BINARY_FLAG;
+ }
+ enum_field_types type() const { return FIELD_TYPE_BLOB;}
+ enum ha_base_keytype key_type() const
+ { return binary_flag ? HA_KEYTYPE_VARBINARY : HA_KEYTYPE_VARTEXT; }
+ void store(const char *to,uint length);
+ void store(double nr);
+ void store(longlong nr);
+ double val_real(void);
+ 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_offset(uint offset);
+ int cmp_binary(const char *a,const char *b, ulong 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*)); }
+ void reset_fields() { bzero((char*) &value,sizeof(value)); }
+ void store_length(ulong number);
+ inline ulong get_length(uint row_offset=0)
+ { return get_length(ptr+row_offset); }
+ ulong get_length(const char *ptr);
+ bool binary() const { return binary_flag; }
+ inline void get_ptr(char **str)
+ {
+ memcpy_fixed(str,ptr+packlength,sizeof(char*));
+ }
+ inline void set_ptr(char *length,char *data)
+ {
+ memcpy(ptr,length,packlength);
+ memcpy_fixed(ptr+packlength,&data,sizeof(char*));
+ }
+ inline void set_ptr(ulong length,char *data)
+ {
+ store_length(length);
+ memcpy_fixed(ptr+packlength,&data,sizeof(char*));
+ }
+ void get_key_image(char *buff,uint length);
+ void set_key_image(char *buff,uint length);
+ void sql_type(String &str) const;
+ inline bool copy()
+ { char *tmp;
+ get_ptr(&tmp);
+ if (value.copy(tmp,get_length()))
+ {
+ Field_blob::reset();
+ return 1;
+ }
+ tmp=(char*) value.ptr(); memcpy_fixed(ptr+packlength,&tmp,sizeof(char*));
+ return 0;
+ }
+ char *pack(char *to, const char *from, uint max_length= ~(uint) 0)
+ {
+ ulong length=get_length();
+ if (length > max_length)
+ {
+ length=max_length;
+ char *save=ptr;
+ ptr=to;
+ store_length(length);
+ ptr=save;
+ }
+ else
+ memcpy(to,from,packlength);
+ if (length)
+ {
+ get_ptr((char**) &from);
+ memcpy(to+packlength, from,length);
+ return to+packlength+length;
+ }
+ return to+packlength;
+ }
+ const char *unpack(char *to, const char *from)
+ {
+ memcpy(to,from,packlength);
+ from+=packlength;
+ ulong length=get_length();
+ if (length)
+ memcpy_fixed(to+packlength, &from, sizeof(from));
+ else
+ bzero(to+packlength,sizeof(from));
+ return from+length;
+ }
+ char *pack_key(char *to, const char *from, uint max_length=~(uint) 0);
+ int pack_cmp(const char *a, const char *b, uint key_length);
+ uint packed_col_length(const char *col_ptr)
+ { return get_length(col_ptr)+packlength;}
+ virtual uint max_packed_col_length(uint max_length)
+ { return packlength+max_length; }
+
+ inline void free() { value.free(); }
+ inline void clear_temporary() { bzero((char*) &value,sizeof(value)); }
+ friend void field_conv(Field *to,Field *from);
+ uint size_of() const { return sizeof(*this); }
+};
+
+
+class Field_enum :public Field_str {
+protected:
+ uint packlength;
+public:
+ TYPELIB *typelib;
+ Field_enum(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+ uint 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;
+ void store(const char *to,uint length);
+ void store(double nr);
+ void store(longlong nr);
+ double val_real(void);
+ longlong val_int(void);
+ String *val_str(String*,String *);
+ int cmp(const char *,const char*);
+ void sort_string(char *buff,uint length);
+ uint32 pack_length() const { return (uint32) packlength; }
+ void store_type(ulonglong value);
+ void sql_type(String &str) const;
+ 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 binary() const { return 0; }
+ bool eq_def(Field *field);
+};
+
+
+class Field_set :public Field_enum {
+public:
+ Field_set(char *ptr_arg, uint32 len_arg, uchar *null_ptr_arg,
+ uint null_bit_arg,
+ enum utype unireg_check_arg, const char *field_name_arg,
+ struct st_table *table_arg,uint32 packlength_arg,
+ TYPELIB *typelib_arg)
+ :Field_enum(ptr_arg, len_arg, null_ptr_arg, null_bit_arg,
+ unireg_check_arg, field_name_arg,
+ table_arg, packlength_arg,
+ typelib_arg)
+ {
+ flags=(flags & ~ENUM_FLAG) | SET_FLAG;
+ }
+ void store(const char *to,uint length);
+ void store(double nr) { Field_set::store((longlong) nr); }
+ void store(longlong nr);
+ virtual bool zero_pack() const { return 1; }
+ String *val_str(String*,String *);
+ void sql_type(String &str) const;
+ enum_field_types real_type() const { return FIELD_TYPE_SET; }
+};
+
+
+/*
+** 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
+ 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
+
+ uint8 row,col,sc_length,interval_id; // For rea_create_table
+ uint offset,pack_flag;
+ create_field() :after(0) {}
+ create_field(Field *field,bool ignore_default=0);
+};
+
+
+/*
+** A class for sending info to the client
+*/
+
+class Send_field {
+ public:
+ const char *table_name,*col_name;
+ uint length,flags,decimals;
+ enum_field_types type;
+ Send_field() {}
+};
+
+
+/*
+** A class for quick copying data to fields
+*/
+
+class Copy_field :public Sql_alloc {
+ void (*get_copy_func(Field *to,Field *from))(Copy_field *);
+public:
+ char *from_ptr,*to_ptr;
+ uchar *from_null_ptr,*to_null_ptr;
+ my_bool *null_row;
+ uint from_bit,to_bit;
+ uint from_length,to_length;
+ Field *from_field,*to_field;
+ String tmp; // For items
+
+ Copy_field() {}
+ ~Copy_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
+};
+
+
+Field *make_field(char *ptr, uint32 field_length,
+ uchar *null_pos, uint null_bit,
+ uint pack_flag, 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);
+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
+*/
+
+#define FIELDFLAG_DECIMAL 1
+#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_LEFT_FULLSCREEN 8192
+#define FIELDFLAG_RIGHT_FULLSCREEN 16384
+#define FIELDFLAG_FORMAT_NUMBER 16384 // predit: ###,,## in output
+#define FIELDFLAG_SUM ((uint) 32768)// predit: +#fieldflag
+#define FIELDFLAG_MAYBE_NULL ((uint) 32768)// sql
+#define FIELDFLAG_PACK_SHIFT 3
+#define FIELDFLAG_DEC_SHIFT 8
+#define FIELDFLAG_MAX_DEC 31
+#define FIELDFLAG_NUM_SCREEN_TYPE 0x7F01
+#define FIELDFLAG_ALFA_SCREEN_TYPE 0x7800
+
+#define FIELD_SORT_REVERSE 16384
+
+#define MTYP_TYPENR(type) (type & 127) /* Remove bits from type */
+
+#define f_is_dec(x) ((x) & FIELDFLAG_DECIMAL)
+#define f_is_num(x) ((x) & FIELDFLAG_NUMBER)
+#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_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_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
new file mode 100644
index 00000000000..aae9f18e6a0
--- /dev/null
+++ b/sql/field_conv.cc
@@ -0,0 +1,528 @@
+/* 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 */
+
+
+/*
+ Functions to copy data to or from fields
+ This could be done with a single short function but opencooding this
+ gives much more speed.
+ */
+
+#include "mysql_priv.h"
+#include <m_ctype.h>
+
+static void do_field_eq(Copy_field *copy)
+{
+ memcpy(copy->to_ptr,copy->from_ptr,copy->from_length);
+}
+
+static void do_field_1(Copy_field *copy)
+{
+ copy->to_ptr[0]=copy->from_ptr[0];
+}
+
+static void do_field_2(Copy_field *copy)
+{
+ copy->to_ptr[0]=copy->from_ptr[0];
+ copy->to_ptr[1]=copy->from_ptr[1];
+}
+
+static void do_field_3(Copy_field *copy)
+{
+ copy->to_ptr[0]=copy->from_ptr[0];
+ copy->to_ptr[1]=copy->from_ptr[1];
+ copy->to_ptr[2]=copy->from_ptr[2];
+}
+
+static void do_field_4(Copy_field *copy)
+{
+ copy->to_ptr[0]=copy->from_ptr[0];
+ copy->to_ptr[1]=copy->from_ptr[1];
+ copy->to_ptr[2]=copy->from_ptr[2];
+ copy->to_ptr[3]=copy->from_ptr[3];
+}
+
+static void do_field_6(Copy_field *copy)
+{ // For blob field
+ copy->to_ptr[0]=copy->from_ptr[0];
+ copy->to_ptr[1]=copy->from_ptr[1];
+ copy->to_ptr[2]=copy->from_ptr[2];
+ copy->to_ptr[3]=copy->from_ptr[3];
+ copy->to_ptr[4]=copy->from_ptr[4];
+ copy->to_ptr[5]=copy->from_ptr[5];
+}
+
+static void do_field_8(Copy_field *copy)
+{
+ copy->to_ptr[0]=copy->from_ptr[0];
+ copy->to_ptr[1]=copy->from_ptr[1];
+ copy->to_ptr[2]=copy->from_ptr[2];
+ copy->to_ptr[3]=copy->from_ptr[3];
+ copy->to_ptr[4]=copy->from_ptr[4];
+ copy->to_ptr[5]=copy->from_ptr[5];
+ copy->to_ptr[6]=copy->from_ptr[6];
+ copy->to_ptr[7]=copy->from_ptr[7];
+}
+
+
+static void do_field_to_null_str(Copy_field *copy)
+{
+ if (*copy->from_null_ptr & copy->from_bit)
+ {
+ bzero(copy->to_ptr,copy->from_length);
+ copy->to_null_ptr[0]=1; // Always bit 1
+ }
+ else
+ {
+ copy->to_null_ptr[0]=0;
+ memcpy(copy->to_ptr,copy->from_ptr,copy->from_length);
+ }
+}
+
+
+static void do_outer_field_to_null_str(Copy_field *copy)
+{
+ if (*copy->null_row ||
+ copy->from_null_ptr && (*copy->from_null_ptr & copy->from_bit))
+ {
+ bzero(copy->to_ptr,copy->from_length);
+ copy->to_null_ptr[0]=1; // Always bit 1
+ }
+ else
+ {
+ copy->to_null_ptr[0]=0;
+ memcpy(copy->to_ptr,copy->from_ptr,copy->from_length);
+ }
+}
+
+
+bool
+set_field_to_null(Field *field)
+{
+ if (field->maybe_null())
+ {
+ field->set_null();
+ field->reset();
+ }
+ else
+ {
+ if (field->type() == FIELD_TYPE_TIMESTAMP)
+ {
+ ((Field_timestamp*) field)->set_time();
+ return 0; // Ok to set time to NULL
+ }
+ field->reset();
+ if (field == field->table->next_number_field)
+ return 0; // field is set in handler.cc
+ 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;
+ }
+ return 0;
+}
+
+
+static void do_skip(Copy_field *copy __attribute__((unused)))
+{
+}
+
+
+static void do_copy_null(Copy_field *copy)
+{
+ if (*copy->from_null_ptr & copy->from_bit)
+ {
+ *copy->to_null_ptr|=copy->to_bit;
+ copy->to_field->reset();
+ }
+ else
+ {
+ *copy->to_null_ptr&= ~copy->to_bit;
+ (copy->do_copy2)(copy);
+ }
+}
+
+
+static void do_outer_field_null(Copy_field *copy)
+{
+ if (*copy->null_row ||
+ copy->from_null_ptr && (*copy->from_null_ptr & copy->from_bit))
+ {
+ *copy->to_null_ptr|=copy->to_bit;
+ copy->to_field->reset();
+ }
+ else
+ {
+ *copy->to_null_ptr&= ~copy->to_bit;
+ (copy->do_copy2)(copy);
+ }
+}
+
+
+static void do_copy_not_null(Copy_field *copy)
+{
+ if (*copy->from_null_ptr & copy->from_bit)
+ {
+ current_thd->cuted_fields++;
+ copy->to_field->reset();
+ }
+ else
+ (copy->do_copy2)(copy);
+}
+
+
+static void do_copy_maybe_null(Copy_field *copy)
+{
+ *copy->to_null_ptr&= ~copy->to_bit;
+ (copy->do_copy2)(copy);
+}
+
+/* timestamp and next_number has special handling in case of NULL values */
+
+static void do_copy_timestamp(Copy_field *copy)
+{
+ if (*copy->from_null_ptr & copy->from_bit)
+ {
+ ((Field_timestamp*) copy->to_field)->set_time();// Same as set_field_to_null
+ }
+ else
+ (copy->do_copy2)(copy);
+}
+
+
+static void do_copy_next_number(Copy_field *copy)
+{
+ if (*copy->from_null_ptr & copy->from_bit)
+ copy->to_field->reset(); // Same as set_field_to_null
+ else
+ (copy->do_copy2)(copy);
+}
+
+
+static void do_copy_blob(Copy_field *copy)
+{
+ ulong length=((Field_blob*) copy->from_field)->get_length();
+ ((Field_blob*) copy->to_field)->store_length(length);
+ memcpy_fixed(copy->to_ptr,copy->from_ptr,sizeof(char*));
+}
+
+static void do_conv_blob(Copy_field *copy)
+{
+ copy->from_field->val_str(&copy->tmp,&copy->tmp);
+ ((Field_blob *) copy->to_field)->store(copy->tmp.ptr(),
+ copy->tmp.length());
+}
+
+/* Save blob in copy->tmp for GROUP BY */
+
+static void do_save_blob(Copy_field *copy)
+{
+ char buff[MAX_FIELD_WIDTH];
+ String res(buff,sizeof(buff));
+ copy->from_field->val_str(&res,&res);
+ copy->tmp.copy(res);
+ ((Field_blob *) copy->to_field)->store(copy->tmp.ptr(),
+ copy->tmp.length());
+}
+
+
+static void do_field_string(Copy_field *copy)
+{
+ char buff[MAX_FIELD_WIDTH];
+ copy->tmp.set_quick(buff,sizeof(buff));
+ copy->from_field->val_str(&copy->tmp,&copy->tmp);
+ copy->to_field->store(copy->tmp.c_ptr_quick(),copy->tmp.length());
+}
+
+
+static void do_field_int(Copy_field *copy)
+{
+ longlong value=copy->from_field->val_int();
+ copy->to_field->store(value);
+}
+
+static void do_field_real(Copy_field *copy)
+{
+ double value=copy->from_field->val_real();
+ copy->to_field->store(value);
+}
+
+
+static void do_cut_string(Copy_field *copy)
+{ // Shorter string field
+ memcpy(copy->to_ptr,copy->from_ptr,copy->to_length);
+
+ /* Check if we loosed any important characters */
+ char *ptr,*end;
+ for (ptr=copy->from_ptr+copy->to_length,end=copy->from_ptr+copy->from_length ;
+ ptr != end ;
+ ptr++)
+ {
+ if (!isspace(*ptr))
+ {
+ current_thd->cuted_fields++; // Give a warning
+ break;
+ }
+ }
+}
+
+
+static void do_expand_string(Copy_field *copy)
+{
+ memcpy(copy->to_ptr,copy->from_ptr,copy->from_length);
+ bfill(copy->to_ptr+copy->from_length,copy->to_length-copy->from_length,' ');
+}
+
+static void do_varstring(Copy_field *copy)
+{
+ uint length=uint2korr(copy->from_ptr);
+ if (length > copy->to_length-2)
+ {
+ length=copy->to_length-2;
+ if (current_thd->count_cuted_fields)
+ current_thd->cuted_fields++; // Increment error counter
+ }
+ int2store(copy->to_ptr,length);
+ memcpy(copy->to_ptr+2, copy->from_ptr,length);
+}
+
+/***************************************************************************
+** The different functions that fills in a Copy_field class
+***************************************************************************/
+
+/*
+ copy of field to maybe null string.
+ If field is null then the all bytes are set to 0.
+ if field is not null then the first byte is set to 1 and the rest of the
+ string is the field value.
+ The 'to' buffer should have a size of field->pack_length()+1
+*/
+
+void Copy_field::set(char *to,Field *from)
+{
+ from_ptr=from->ptr;
+ to_ptr=to;
+ from_length=from->pack_length();
+ if (from->maybe_null())
+ {
+ from_null_ptr=from->null_ptr;
+ from_bit= from->null_bit;
+ to_ptr[0]= 1; // Null as default value
+ to_null_ptr= (uchar*) to_ptr++;
+ to_bit= 1;
+ if (from->table->maybe_null)
+ {
+ null_row= &from->table->null_row;
+ do_copy= do_outer_field_to_null_str;
+ }
+ else
+ do_copy= do_field_to_null_str;
+ }
+ else
+ {
+ to_null_ptr= 0; // For easy debugging
+ do_copy= do_field_eq;
+ }
+}
+
+
+
+void Copy_field::set(Field *to,Field *from,bool save)
+{
+ if (to->type() == FIELD_TYPE_NULL)
+ {
+ to_null_ptr=0; // For easy debugging
+ to_ptr=0;
+ do_copy=do_skip;
+ return;
+ }
+ from_field=from;
+ to_field=to;
+ from_ptr=from->ptr;
+ from_length=from->pack_length();
+ to_ptr= to->ptr;
+ to_length=to_field->pack_length();
+
+ // set up null handling
+ from_null_ptr=to_null_ptr=0;
+ if (from->maybe_null())
+ {
+ from_null_ptr= from->null_ptr;
+ from_bit= from->null_bit;
+ if (to_field->real_maybe_null())
+ {
+ to_null_ptr= to->null_ptr;
+ to_bit= to->null_bit;
+ if (from_null_ptr)
+ do_copy= do_copy_null;
+ else
+ {
+ null_row= &from->table->null_row;
+ do_copy= do_outer_field_null;
+ }
+ }
+ else
+ do_copy=do_copy_not_null;
+ }
+ else if (to_field->real_maybe_null())
+ {
+ to_null_ptr= to->null_ptr;
+ to_bit= to->null_bit;
+ if (to_field->type() == FIELD_TYPE_TIMESTAMP)
+ do_copy=do_copy_timestamp; // Automatic timestamp
+ else if (to_field == to_field->table->next_number_field)
+ do_copy=do_copy_next_number;
+ else
+ do_copy=do_copy_maybe_null;
+ }
+ else
+ do_copy=0;
+
+ if ((to->flags & BLOB_FLAG) && save)
+ do_copy2= do_save_blob;
+ else
+ do_copy2= get_copy_func(to,from);
+ if (!do_copy) // Not null
+ do_copy=do_copy2;
+}
+
+
+void (*Copy_field::get_copy_func(Field *to,Field *from))(Copy_field*)
+{
+ if (to->flags & BLOB_FLAG)
+ {
+ if (!(from->flags & BLOB_FLAG))
+ return do_conv_blob;
+ if (from_length != to_length ||
+ to->table->db_low_byte_first != from->table->db_low_byte_first)
+ {
+ // Correct pointer to point at char pointer
+ to_ptr+=to_length - to->table->blob_ptr_size;
+ from_ptr+=from_length- from->table->blob_ptr_size;
+ return do_copy_blob;
+ }
+ }
+ else
+ {
+ // Check if identical fields
+ if (from->result_type() == STRING_RESULT)
+ {
+ if (to->real_type() != from->real_type() ||
+ to->table->db_low_byte_first != from->table->db_low_byte_first)
+ {
+ if (from->real_type() == FIELD_TYPE_ENUM ||
+ from->real_type() == FIELD_TYPE_SET)
+ if (to->result_type() != STRING_RESULT)
+ return do_field_int; // Convert SET to number
+ return do_field_string;
+ }
+ if (to->real_type() == FIELD_TYPE_ENUM ||
+ to->real_type() == FIELD_TYPE_SET)
+ {
+ if (!to->eq_def(from))
+ return do_field_string;
+ }
+ else if (to->real_type() == FIELD_TYPE_VAR_STRING && to_length !=
+ from_length)
+ return do_varstring;
+ else if (to_length < from_length)
+ return do_cut_string;
+ else if (to_length > from_length)
+ return do_expand_string;
+ }
+ else if (to->real_type() != from->real_type() ||
+ to_length != from_length ||
+ to->table->db_low_byte_first != from->table->db_low_byte_first)
+ {
+ if (to->real_type() == FIELD_TYPE_DECIMAL ||
+ to->result_type() == STRING_RESULT)
+ return do_field_string;
+ if (to->result_type() == INT_RESULT)
+ return do_field_int;
+ return do_field_real;
+ }
+ else
+ {
+ if (!to->eq_def(from) ||
+ to->table->db_low_byte_first != from->table->db_low_byte_first)
+ {
+ if (to->real_type() == FIELD_TYPE_DECIMAL)
+ return do_field_string;
+ if (to->result_type() == INT_RESULT)
+ return do_field_int;
+ else
+ return do_field_real;
+ }
+ }
+ }
+ /* Eq fields */
+ switch (to_length) {
+ case 1: return do_field_1;
+ case 2: return do_field_2;
+ case 3: return do_field_3;
+ case 4: return do_field_4;
+ case 6: return do_field_6;
+ case 8: return do_field_8;
+ }
+ return do_field_eq;
+}
+
+
+/* Simple quick field convert that is called on insert */
+
+void field_conv(Field *to,Field *from)
+{
+ if (to->real_type() == from->real_type())
+ {
+ if (to->pack_length() == from->pack_length() &&
+ to->real_type() != FIELD_TYPE_ENUM &&
+ to->real_type() != FIELD_TYPE_SET &&
+ to->table->db_low_byte_first == from->table->db_low_byte_first)
+ { // Identical fields
+ memcpy(to->ptr,from->ptr,to->pack_length());
+ return;
+ }
+ }
+ if (to->type() == FIELD_TYPE_BLOB)
+ { // Be sure the value is stored
+ Field_blob *blob=(Field_blob*) to;
+ from->val_str(&blob->value,&blob->value);
+ if (!blob->value.is_alloced() &&
+ from->real_type() != FIELD_TYPE_STRING)
+ blob->value.copy();
+ blob->store(blob->value.ptr(),blob->value.length());
+ return;
+ }
+ if ((from->result_type() == STRING_RESULT &&
+ (to->result_type() == STRING_RESULT ||
+ (from->real_type() != FIELD_TYPE_ENUM &&
+ from->real_type() != FIELD_TYPE_SET))) ||
+ to->type() == FIELD_TYPE_DECIMAL)
+ {
+ char buff[MAX_FIELD_WIDTH];
+ String result(buff,sizeof(buff));
+ from->val_str(&result,&result);
+ to->store(result.c_ptr_quick(),result.length());
+ }
+ else if (from->result_type() == REAL_RESULT)
+ to->store(from->val_real());
+ else
+ to->store(from->val_int());
+}
diff --git a/sql/filesort.cc b/sql/filesort.cc
new file mode 100644
index 00000000000..44b7a7ab42e
--- /dev/null
+++ b/sql/filesort.cc
@@ -0,0 +1,957 @@
+/* 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 */
+
+
+/* Sorts a database */
+
+#include "mysql_priv.h"
+#ifdef HAVE_STDDEF_H
+#include <stddef.h> /* for macro offsetof */
+#endif
+#include <m_ctype.h>
+#ifndef THREAD
+#define SKIPP_DBUG_IN_FILESORT
+#endif
+ /* static variabels */
+
+#define MERGEBUFF 7
+#define MERGEBUFF2 15
+
+ /* How to write record_ref. */
+
+#define WRITE_REF(file,from) \
+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 */
+ ha_rows count; /* Number of rows in table */
+ uchar *base,*key; /* key pointers */
+ 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;
+ 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 ha_rows find_all_keys(SORTPARAM *param,SQL_SELECT *select,
+ uchar * *sort_keys,
+ BUFFPEK *buffpek,uint *maxbuffer,
+ 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,
+ BUFFPEK *buffpek,
+ uint maxbuffer,IO_CACHE *tempfile,
+ IO_CACHE *outfile);
+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 */
+
+ha_rows filesort(TABLE **table, SORT_FIELD *sortorder, uint s_length,
+ SQL_SELECT *select, ha_rows special, ha_rows max_rows)
+{
+ int error;
+ uint memavl,old_memavl,maxbuffer,skr;
+ BUFFPEK *buffpek;
+ ha_rows records;
+ uchar **sort_keys;
+ gptr save_1,save_2;
+ IO_CACHE tempfile,*selected_records_file,*outfile;
+ SORTPARAM param;
+ DBUG_ENTER("filesort");
+ DBUG_EXECUTE("info",TEST_filesort(table,sortorder,s_length,special););
+#ifdef SKIPP_DBUG_IN_FILESORT
+ DBUG_PUSH(""); /* No DBUG here */
+#endif
+
+ outfile= table[0]->io_cache;
+ my_b_clear(&tempfile);
+ save_1=save_2=0;
+ buffpek= (BUFFPEK *) NULL; sort_keys= (uchar **) NULL; error= 1;
+ maxbuffer=1;
+ param.ref_length= table[0]->file->ref_length;
+ param.sort_length=sortlength(sortorder,s_length)+ param.ref_length;
+ param.max_rows= max_rows;
+
+ if (select && my_b_inited(&select->file))
+ {
+ records=special=select->records; /* purecov: deadcode */
+ selected_records_file= &select->file; /* purecov: deadcode */
+ reinit_io_cache(selected_records_file,READ_CACHE,0L,0,0); /* purecov: deadcode */
+ }
+ else if (special)
+ {
+ records=special; /* purecov: deadcode */
+ selected_records_file= outfile; /* purecov: deadcode */
+ reinit_io_cache(selected_records_file,READ_CACHE,0L,0,0); /* purecov: deadcode */
+ }
+#ifdef CAN_TRUST_RANGE
+ else if (select && select->quick && select->quick->records > 0L)
+ {
+ VOID(ha_info(&table[0]->form,0)); /* Get record-count */
+ records=min((ha_rows) (select->quick->records*2+EXTRA_RECORDS*2),
+ table[0]->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->records+EXTRA_RECORDS;
+ selected_records_file= 0;
+ }
+ if (param.sort_length == param.ref_length && records > param.max_rows)
+ records=param.max_rows; /* purecov: inspected */
+
+#ifdef USE_STRCOLL
+ if (use_strcoll(default_charset_info) &&
+ !(param.tmp_buffer=my_malloc(param.sort_length,MYF(MY_WME))))
+ goto err;
+#endif
+
+ /* Reserve memory for IO_CACHE files */
+ if (! (save_1=my_malloc(DISK_BUFFER_SIZE,MYF(MY_WME))) ||
+ ! (save_2=my_malloc(DISK_BUFFER_SIZE,MYF(MY_WME))))
+ goto err;
+
+ memavl=sortbuff_size;
+ while (memavl >= MIN_SORT_MEMORY)
+ {
+ if ((records+1)*(param.sort_length+sizeof(char*))+sizeof(BUFFPEK)*10 <
+ (ulong) 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,
+ MYF(0))))
+ if ((buffpek = (BUFFPEK*) my_malloc((uint) sizeof(BUFFPEK)*
+ (maxbuffer+10),
+ MYF(0))))
+ break;
+ else
+ my_free((gptr) sort_keys,MYF(0));
+ 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);
+ goto err;
+ }
+ my_free(save_1,MYF(0)); /* Free for later use */
+ my_free(save_2,MYF(0));
+ save_1=save_2=0;
+
+ param.sort_form= table[0];
+ param.end=(param.local_sortorder=sortorder)+s_length;
+ if ((records=find_all_keys(&param,select,sort_keys,buffpek,&maxbuffer,
+ &tempfile, selected_records_file)) ==
+ HA_POS_ERROR)
+ goto err;
+ if (maxbuffer == 0)
+ {
+ if (save_index(&param,sort_keys,(uint) records))
+ goto err;
+ }
+ else
+ {
+ /* Open cached file if it isn't open */
+ if (! my_b_inited(outfile) &&
+ open_cached_file(outfile,mysql_tmpdir,TEMP_PREFIX,READ_RECORD_BUFFER,
+ MYF(MY_WME)))
+ goto err;
+ reinit_io_cache(outfile,WRITE_CACHE,0L,0,0);
+
+ param.keys=((param.keys*(param.sort_length+sizeof(char*))) /
+ param.sort_length-1);
+ if (merge_many_buff(&param,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(&param,sort_keys,buffpek,maxbuffer,&tempfile,outfile))
+ goto err;
+ }
+ if (records > param.max_rows)
+ records=param.max_rows;
+ error =0;
+
+ err:
+#ifdef USE_STRCOLL
+ if (use_strcoll(default_charset_info))
+ x_free(param.tmp_buffer);
+#endif
+ x_free((gptr) sort_keys);
+ x_free((gptr) buffpek);
+ x_free(save_1);
+ x_free(save_2);
+ close_cached_file(&tempfile);
+ if (my_b_inited(outfile))
+ {
+ if (flush_io_cache(outfile))
+ error=1;
+ {
+ my_off_t save_pos=outfile->pos_in_file;
+ /* For following reads */
+ if (reinit_io_cache(outfile,READ_CACHE,0L,0,0))
+ error=1;
+ outfile->end_of_file=save_pos;
+ }
+ }
+ if (error)
+ my_error(ER_FILSORT_ABORT,MYF(ME_ERROR+ME_WAITTANG));
+
+#ifdef SKIPP_DBUG_IN_FILESORT
+ DBUG_POP(); /* Ok to DBUG */
+#endif
+ DBUG_PRINT("exit",("records: %ld",records));
+ DBUG_RETURN(error ? HA_POS_ERROR : records);
+} /* filesort */
+
+
+ /* Make a array of string pointers */
+
+static char **make_char_array(register uint fields, uint length, myf my_flag)
+{
+ register char **pos;
+ char **old_pos,*char_pos;
+ DBUG_ENTER("make_char_array");
+
+ if ((old_pos= (char**) my_malloc((uint) fields*(length+sizeof(char*)),
+ my_flag)))
+ {
+ pos=old_pos; char_pos=((char*) (pos+fields)) -length;
+ while (fields--) *(pos++) = (char_pos+= length);
+ }
+
+ DBUG_RETURN(old_pos);
+} /* make_char_array */
+
+
+ /* 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 *tempfile, IO_CACHE *indexfile)
+{
+ int error,flag,quick_select;
+ uint idx,indexpos,ref_length;
+ byte *ref_pos,*next_pos,ref_buff[MAX_REFLENGTH];
+ my_off_t record;
+ TABLE *sort_form;
+ volatile bool *killed= &current_thd->killed;
+ handler *file;
+ DBUG_ENTER("find_all_keys");
+
+ idx=indexpos=0;
+ error=quick_select=0;
+ sort_form=param->sort_form;
+ file=sort_form->file;
+ ref_length=param->ref_length;
+ ref_pos= ref_buff;
+ quick_select=select && select->quick;
+ record=0;
+ flag= ((!indexfile && file->option_flag() & HA_REC_NOT_IN_SEQ)
+ || quick_select);
+ if (indexfile || flag)
+ ref_pos= &file->ref[0];
+ next_pos=ref_pos;
+ if (! indexfile && ! quick_select)
+ {
+ file->reset();
+ next_pos=(byte*) 0; /* Find records in sequence */
+ file->rnd_init();
+ file->extra(HA_EXTRA_CACHE); /* Quicker reads */
+ }
+
+ for (;;)
+ {
+ if (quick_select)
+ {
+ if ((error=select->quick->get_next()))
+ break;
+ file->position(sort_form->record[0]);
+ }
+ else /* Not quick-select */
+ {
+ if (indexfile)
+ {
+ if (my_b_read(indexfile,(byte*) ref_pos,ref_length)) /* purecov: deadcode */
+ {
+ error= my_errno ? my_errno : -1; /* Abort */
+ break;
+ }
+ if (TEST_IF_LASTREF(ref_pos,ref_length))
+ {
+ error=HA_ERR_END_OF_FILE;
+ break;
+ }
+ error=file->rnd_pos(sort_form->record[0],next_pos);
+ }
+ else
+ {
+ error=file->rnd_next(sort_form->record[0]);
+ if (!flag)
+ {
+ ha_store_ptr(ref_pos,ref_length,record); // Position to row
+ record+=sort_form->db_record_offset;
+ }
+ else
+ file->position(sort_form->record[0]);
+ }
+ if (error && error != HA_ERR_RECORD_DELETED)
+ break;
+ }
+ if (*killed)
+ {
+ (void) file->extra(HA_EXTRA_NO_CACHE);
+ file->rnd_end();
+ DBUG_RETURN(HA_POS_ERROR); /* purecov: inspected */
+ }
+ if (error == 0 && (!select || select->skipp_record() == 0))
+ {
+ if (idx == param->keys)
+ {
+ if (indexpos >= *maxbuffer ||
+ write_keys(param,sort_keys,idx,buffpek+indexpos,tempfile))
+ DBUG_RETURN(HA_POS_ERROR);
+ idx=0; indexpos++;
+ if (param->ref_length == param->sort_length &&
+ my_b_tell(tempfile)/param->sort_length >= param->max_rows)
+ {
+ error=HA_ERR_END_OF_FILE;
+ break; /* Found enough records */
+ }
+ }
+ make_sortkey(param,sort_keys[idx++],ref_pos);
+ }
+ }
+ (void) file->extra(HA_EXTRA_NO_CACHE); /* End cacheing of records */
+ file->rnd_end();
+ DBUG_PRINT("test",("error: %d indexpos: %d",error,indexpos));
+ if (error != HA_ERR_END_OF_FILE)
+ {
+ file->print_error(error,MYF(ME_ERROR | ME_WAITTANG)); /* purecov: inspected */
+ DBUG_RETURN(HA_POS_ERROR); /* purecov: inspected */
+ }
+ if (indexpos)
+ if (indexpos >= *maxbuffer ||
+ write_keys(param,sort_keys,idx,buffpek+indexpos,tempfile))
+ DBUG_RETURN(HA_POS_ERROR); /* purecov: inspected */
+ *maxbuffer=indexpos;
+ DBUG_RETURN(my_b_inited(tempfile) ?
+ (ha_rows) (my_b_tell(tempfile)/param->sort_length) :
+ idx);
+} /* find_all_keys */
+
+
+ /* Skriver en buffert med nycklar till filen */
+
+static int write_keys(SORTPARAM *param, register uchar **sort_keys, uint count,
+ BUFFPEK *buffpek, IO_CACHE *tempfile)
+{
+ uint sort_length;
+ DBUG_ENTER("write_keys");
+
+ sort_length=param->sort_length;
+#ifdef MC68000
+ quicksort(sort_keys,count,sort_length);
+#else
+ my_string_ptr_sort((gptr) sort_keys,(uint) count,sort_length);
+#endif
+ 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);
+ 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++)
+ if (my_b_write(tempfile,(byte*) *sort_keys,(uint) sort_length))
+ DBUG_RETURN(1);
+ DBUG_RETURN(0);
+} /* write_keys */
+
+
+ /* makes a sort-key from record */
+
+static void make_sortkey(register SORTPARAM *param,
+ register uchar *to, byte *ref_pos)
+{
+ reg3 Field *field;
+ reg1 SORT_FIELD *sort_field;
+ reg5 uint length;
+
+ for (sort_field=param->local_sortorder ;
+ sort_field != param->end ;
+ sort_field++)
+ {
+ if ((field=sort_field->field))
+ { // Field
+ if (field->maybe_null())
+ {
+ if (field->is_null())
+ {
+ if (sort_field->reverse)
+ bfill(to,sort_field->length+1,(char) 255);
+ else
+ bzero((char*) to,sort_field->length+1);
+ to+= sort_field->length+1;
+ continue;
+ }
+ else
+ *to++=1;
+ }
+ field->sort_string((char*) to,sort_field->length);
+ }
+ else
+ { // Item
+ Item *item=sort_field->item;
+ switch (sort_field->result_type) {
+ case STRING_RESULT:
+ {
+ if (item->maybe_null)
+ *to++=1;
+ /* All item->str() to use some extra byte for end null.. */
+ String tmp((char*) to,sort_field->length+4);
+ String *res=item->val_str(&tmp);
+ if (!res)
+ {
+ if (item->maybe_null)
+ bzero((char*) to-1,sort_field->length+1);
+ else
+ {
+ DBUG_PRINT("warning",
+ ("Got null on something that shouldn't be null"));
+ bzero((char*) to,sort_field->length); // Avoid crash
+ }
+ break;
+ }
+ length=res->length();
+ int diff=(int) (sort_field->length-length);
+ if (diff < 0)
+ {
+ diff=0; /* purecov: inspected */
+ length=sort_field->length;
+ }
+#ifdef USE_STRCOLL
+ if (use_strcoll(default_charset_info))
+ {
+ if (item->binary)
+ {
+ if (res->ptr() != (char*) to)
+ memcpy(to,res->ptr(),length);
+ bzero((char*) to+length,diff);
+ }
+ else
+ {
+ char *from=(char*) res->ptr();
+ if ((unsigned char *)from == to)
+ {
+ set_if_smaller(length,sort_field->length);
+ memcpy(param->tmp_buffer,from,length);
+ from=param->tmp_buffer;
+ }
+ uint tmp_length=my_strnxfrm(default_charset_info,
+ to,(unsigned char *) from,
+ sort_field->length,
+ length);
+ if (tmp_length < sort_field->length)
+ bzero((char*) to+tmp_length,sort_field->length-tmp_length);
+ }
+ }
+ else
+ {
+#endif
+ if (res->ptr() != (char*) to)
+ memcpy(to,res->ptr(),length);
+ bzero((char *)to+length,diff);
+ if (!item->binary)
+ case_sort((char*) to,length);
+#ifdef USE_STRCOLL
+ }
+#endif
+ break;
+ }
+ case INT_RESULT:
+ {
+ longlong value=item->val_int();
+ if (item->maybe_null)
+ *to++=1; /* purecov: inspected */
+ if (item->null_value)
+ {
+ if (item->maybe_null)
+ bzero((char*) to-1,sort_field->length+1);
+ else
+ {
+ DBUG_PRINT("warning",
+ ("Got null on something that shouldn't be null"));
+ bzero((char*) to,sort_field->length);
+ }
+ break;
+ }
+#if SIZEOF_LONG_LONG > 4
+ to[7]= (uchar) value;
+ to[6]= (uchar) (value >> 8);
+ to[5]= (uchar) (value >> 16);
+ to[4]= (uchar) (value >> 24);
+ to[3]= (uchar) (value >> 32);
+ to[2]= (uchar) (value >> 40);
+ to[1]= (uchar) (value >> 48);
+ to[0]= (uchar) (value >> 56) ^ 128; // Fix sign
+#else
+ to[3]= (uchar) value;
+ to[2]= (uchar) (value >> 8);
+ to[1]= (uchar) (value >> 16);
+ to[0]= (uchar) (value >> 24) ^ 128; // Fix sign
+#endif
+ break;
+ }
+ case REAL_RESULT:
+ {
+ double value=item->val();
+ if (item->null_value)
+ {
+ bzero((char*) to,sort_field->length+1);
+ to++;
+ break;
+ }
+ if (item->maybe_null)
+ *to++=1;
+ change_double_for_sort(value,(byte*) to);
+ break;
+ }
+ }
+ }
+ if (sort_field->reverse)
+ { /* Revers key */
+ length=sort_field->length;
+ while (length--)
+ {
+ *to = (uchar) (~ *to);
+ to++;
+ }
+ }
+ else
+ to+= sort_field->length;
+ }
+ memcpy((byte*) to,ref_pos,(size_s) param->ref_length);/* Save filepos last */
+ return;
+}
+
+
+static bool save_index(SORTPARAM *param, uchar **sort_keys, uint count)
+{
+ uint offset,ref_length;
+ byte *to;
+ DBUG_ENTER("save_index");
+
+ my_string_ptr_sort((gptr) sort_keys,(uint) count,param->sort_length);
+ ref_length=param->ref_length;
+ offset=param->sort_length-ref_length;
+ if ((ha_rows) count > param->max_rows)
+ count=(uint) param->max_rows;
+ if (!(to=param->sort_form->record_pointers=
+ (byte*) my_malloc(ref_length*count,MYF(MY_WME))))
+ DBUG_RETURN(1); /* purecov: inspected */
+ for (uchar **end=sort_keys+count ; sort_keys != end ; sort_keys++)
+ {
+ memcpy(to,*sort_keys+offset,ref_length);
+ to+=ref_length;
+ }
+ DBUG_RETURN(0);
+}
+
+
+ /* Merge buffers to make < MERGEBUFF2 buffers */
+
+static int merge_many_buff(SORTPARAM *param, uchar **sort_keys,
+ BUFFPEK *buffpek, uint *maxbuffer, IO_CACHE *t_file)
+{
+ register int i;
+ IO_CACHE t_file2,*from_file,*to_file,*temp;
+ BUFFPEK *lastbuff;
+ DBUG_ENTER("merge_many_buff");
+
+ if (*maxbuffer < MERGEBUFF2)
+ DBUG_RETURN(0); /* purecov: inspected */
+ if (flush_io_cache(t_file) ||
+ open_cached_file(&t_file2,mysql_tmpdir,TEMP_PREFIX,DISK_BUFFER_SIZE,
+ MYF(MY_WME)))
+ DBUG_RETURN(1); /* purecov: inspected */
+
+ from_file= t_file ; to_file= &t_file2;
+ while (*maxbuffer >= MERGEBUFF2)
+ {
+ reinit_io_cache(from_file,READ_CACHE,0L,0,0);
+ reinit_io_cache(to_file,WRITE_CACHE,0L,0,0);
+ lastbuff=buffpek;
+ for (i=0 ; i <= (int) *maxbuffer-MERGEBUFF*3/2 ; i+=MERGEBUFF)
+ {
+ if (merge_buffers(param,from_file,to_file,sort_keys,lastbuff++,
+ buffpek+i,buffpek+i+MERGEBUFF-1,0))
+ break; /* purecov: inspected */
+ }
+ if (merge_buffers(param,from_file,to_file,sort_keys,lastbuff++,
+ buffpek+i,buffpek+ *maxbuffer,0))
+ break; /* purecov: inspected */
+ if (flush_io_cache(to_file))
+ break; /* purecov: inspected */
+ temp=from_file; from_file=to_file; to_file=temp;
+ *maxbuffer= (uint) (lastbuff-buffpek)-1;
+ }
+ close_cached_file(to_file); // This holds old result
+ if (to_file == t_file)
+ *t_file=t_file2; // Copy result file
+
+ DBUG_RETURN(*maxbuffer >= MERGEBUFF2); /* Return 1 if interrupted */
+} /* merge_many_buff */
+
+
+ /* 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)
+{
+ register uint count;
+ uint length;
+
+ if ((count=(uint) min((ha_rows) buffpek->max_keys,buffpek->count)))
+ {
+ if (my_pread(fromfile->file,(byte*) buffpek->base,
+ (length= sort_length*count),buffpek->file_pos,MYF_RW))
+ return((uint) -1); /* purecov: inspected */
+ buffpek->key=buffpek->base;
+ buffpek->file_pos+= length; /* New filepos */
+ buffpek->count-= count;
+ buffpek->mem_count= count;
+ }
+ return (count*sort_length);
+} /* read_to_buffer */
+
+
+ /* 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 error;
+ uint sort_length,offset;
+ ulong maxcount;
+ ha_rows count,max_rows;
+ my_off_t to_start_filepos;
+ uchar *strpos;
+ BUFFPEK *buffpek,**refpek;
+ QUEUE queue;
+ volatile bool *killed= &current_thd->killed;
+ DBUG_ENTER("merge_buffers");
+
+ count=error=0;
+ offset=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;
+
+ if (init_queue(&queue,(uint) (Tb-Fb)+1,offsetof(BUFFPEK,key),0,
+ (int (*) (void *, byte *,byte*))
+ 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,
+ sort_length));
+ if (error == -1)
+ goto err; /* purecov: inspected */
+ queue_insert(&queue,(byte*) buffpek);
+ }
+
+ while (queue.elements > 1)
+ {
+ if (*killed)
+ {
+ error=1; goto err; /* purecov: inspected */
+ }
+ for (;;)
+ {
+ buffpek=(BUFFPEK*) queue_top(&queue);
+ if (flag == 0)
+ {
+ if (my_b_write(to_file,(byte*) buffpek->key, sort_length))
+ {
+ error=1; goto err; /* purecov: inspected */
+ }
+ }
+ else
+ {
+ WRITE_REF(to_file,(byte*) buffpek->key+offset);
+ }
+ if (!--max_rows)
+ {
+ error=0; /* purecov: inspected */
+ goto end; /* purecov: inspected */
+ }
+ buffpek->key+=sort_length;
+ if (! --buffpek->mem_count)
+ {
+ if (!(error=(int) read_to_buffer(from_file,buffpek,
+ sort_length)))
+ {
+ uchar *base=buffpek->base;
+ ulong max_keys=buffpek->max_keys;
+
+ VOID(queue_remove(&queue,0));
+
+ /* Put room used by buffer to use in other buffer */
+ for (refpek= (BUFFPEK**) &queue_top(&queue);
+ refpek <= (BUFFPEK**) &queue_end(&queue);
+ refpek++)
+ {
+ buffpek= *refpek;
+ if (buffpek->base+buffpek->max_keys*sort_length == base)
+ {
+ buffpek->max_keys+=max_keys;
+ break;
+ }
+ else if (base+max_keys*sort_length == buffpek->base)
+ {
+ buffpek->base=base;
+ buffpek->max_keys+=max_keys;
+ break;
+ }
+ }
+ break; /* One buffer have been removed */
+ }
+ else if (error == -1)
+ goto err; /* purecov: inspected */
+ }
+ queue_replaced(&queue); /* Top element has been replaced */
+ }
+ }
+ buffpek=(BUFFPEK*) queue_top(&queue);
+ buffpek->base=(uchar *) sort_keys;
+ buffpek->max_keys=param->keys;
+ do
+ {
+ if ((ha_rows) buffpek->mem_count > max_rows)
+ { /* Don't write too many records */
+ buffpek->mem_count=(uint) max_rows;
+ buffpek->count=0; /* Don't read more */
+ }
+ if (flag == 0)
+ {
+ if (my_b_write(to_file,(byte*) buffpek->key,
+ (sort_length*buffpek->mem_count)))
+ {
+ error=1; goto err; /* purecov: inspected */
+ }
+ }
+ else
+ {
+ register uchar *end;
+ strpos= buffpek->key+offset;
+ for (end=strpos+buffpek->mem_count*sort_length;
+ strpos != end ;
+ strpos+=sort_length)
+ {
+ WRITE_REF(to_file,strpos);
+ }
+ }
+ }
+ while ((error=(int) read_to_buffer(from_file,buffpek,sort_length))
+ != -1 && error != 0);
+
+end:
+ lastbuff->count=min(count,param->max_rows);
+ lastbuff->file_pos=to_start_filepos;
+err:
+ delete_queue(&queue);
+ DBUG_RETURN(error);
+} /* merge_buffers */
+
+
+ /* Do a merge to output-file (save only positions) */
+
+static int merge_index(SORTPARAM *param, uchar **sort_keys,
+ BUFFPEK *buffpek, uint maxbuffer,
+ IO_CACHE *tempfile, IO_CACHE *outfile)
+{
+ DBUG_ENTER("merge_index");
+ if (merge_buffers(param,tempfile,outfile,sort_keys,buffpek,buffpek,
+ buffpek+maxbuffer,1))
+ DBUG_RETURN(1); /* purecov: inspected */
+ DBUG_RETURN(0);
+ /* Was: DBUG_RETURN(my_b_write(outfile,last_ref,param->ref_length)); */
+} /* merge_index */
+
+
+ /* Calculate length of sort key */
+
+static uint
+sortlength(SORT_FIELD *sortorder, uint s_length)
+{
+ reg2 uint length;
+
+ length=0;
+ for (; s_length-- ; sortorder++)
+ {
+ if (sortorder->field)
+ {
+ if (sortorder->field->type() == FIELD_TYPE_BLOB)
+ sortorder->length=max_item_sort_length;
+ else
+ {
+ sortorder->length=sortorder->field->pack_length();
+#ifdef USE_STRCOLL
+ if (use_strcoll(default_charset_info) && !sortorder->field->binary())
+ sortorder->length= sortorder->length*MY_STRXFRM_MULTIPLY;
+#endif
+ }
+ if (sortorder->field->maybe_null())
+ length++; // Place for NULL marker
+ }
+ else
+ {
+ switch ((sortorder->result_type=sortorder->item->result_type())) {
+ case STRING_RESULT:
+ sortorder->length=sortorder->item->max_length;
+#ifdef USE_STRCOLL
+ if (use_strcoll(default_charset_info) && !sortorder->item->binary)
+ sortorder->length= sortorder->length*MY_STRXFRM_MULTIPLY;
+#endif
+ break;
+ case INT_RESULT:
+#if SIZEOF_LONG_LONG > 4
+ sortorder->length=8; // Size of intern longlong
+#else
+ sortorder->length=4;
+#endif
+ break;
+ case REAL_RESULT:
+ sortorder->length=sizeof(double);
+ break;
+ }
+ if (sortorder->item->maybe_null)
+ length++; // Place for NULL marker
+ }
+ set_if_smaller(sortorder->length,max_item_sort_length);
+ length+=sortorder->length;
+ }
+ sortorder->field= (Field*) 0; // end marker
+ DBUG_PRINT("info",("sort_length: %d",length));
+ return length;
+}
+
+
+/*
+** functions to change a double or float to a sortable string
+** The following should work for IEEE
+*/
+
+#define DBL_EXP_DIG (sizeof(double)*8-DBL_MANT_DIG)
+
+void change_double_for_sort(double nr,byte *to)
+{
+ uchar *tmp=(uchar*) to;
+ if (nr == 0.0)
+ { /* Change to zero string */
+ tmp[0]=(uchar) 128;
+ bzero((char*) tmp+1,sizeof(nr)-1);
+ }
+ else
+ {
+#ifdef WORDS_BIGENDIAN
+ memcpy_fixed(tmp,&nr,sizeof(nr));
+#else
+ {
+ uchar *ptr= (uchar*) &nr;
+ tmp[0]= ptr[7]; tmp[1]=ptr[6]; tmp[2]= ptr[5]; tmp[3]=ptr[4];
+ tmp[4]= ptr[3]; tmp[5]=ptr[2]; tmp[6]= ptr[1]; tmp[7]=ptr[0];
+ }
+#endif
+ if (tmp[0] & 128) /* Negative */
+ { /* make complement */
+ uint i;
+ for (i=0 ; i < sizeof(nr); i++)
+ tmp[i]=tmp[i] ^ (uchar) 255;
+ }
+ else
+ { /* Set high and move exponent one up */
+ ushort exp_part=(((ushort) tmp[0] << 8) | (ushort) tmp[1] |
+ (ushort) 32768);
+ exp_part+= (ushort) 1 << (16-1-DBL_EXP_DIG);
+ tmp[0]= (uchar) (exp_part >> 8);
+ tmp[1]= (uchar) exp_part;
+ }
+ }
+}
diff --git a/sql/frm_crypt.cc b/sql/frm_crypt.cc
new file mode 100644
index 00000000000..629e4ffab95
--- /dev/null
+++ b/sql/frm_crypt.cc
@@ -0,0 +1,37 @@
+/* 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 */
+
+
+/*
+** change the following to the output of password('our password')
+** split into 2 parts of 8 characters each.
+** This is done to make it impossible to search after a text string in the
+** mysql binary.
+*/
+
+#include "mysql_priv.h"
+
+#ifdef HAVE_CRYPTED_FRM
+
+/* password('test') */
+ulong password_seed[2]={0x378b243e, 0x220ca493};
+
+SQL_CRYPT *get_crypt_for_frm(void)
+{
+ return new SQL_CRYPT(password_seed);
+}
+
+#endif
diff --git a/sql/gen_lex_hash.cc b/sql/gen_lex_hash.cc
new file mode 100644
index 00000000000..3e1f6f15a6b
--- /dev/null
+++ b/sql/gen_lex_hash.cc
@@ -0,0 +1,560 @@
+/* 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 NO_YACC_SYMBOLS
+#include <global.h>
+#include <my_sys.h>
+#include <m_string.h>
+#ifndef __GNU_LIBRARY__
+#define __GNU_LIBRARY__ // Skipp warnings in getopt.h
+#endif
+#include <getopt.h>
+#include "mysql_version.h"
+#include "lex.h"
+
+bool opt_search=0,opt_verbose=0;
+
+#define max_allowed_array 8000 // Don't generate bigger arrays than this
+#define max_symbol 32767 // Use this for 'not found'
+#define how_much_for_plus 8 // 2-8
+#define type_count 1 // 1-5
+#define char_table_count 5
+#define total_symbols (sizeof(symbols)/sizeof(SYMBOL) +\
+ sizeof(sql_functions)/sizeof(SYMBOL))
+
+#define how_much_and INT_MAX24
+
+/*
+ The following only have to work with characters in the set
+ used by SQL commands
+*/
+
+#undef tolower
+#define tolower(a) ((a) >= 'A' && (a) <= 'Z') ? ((a)- 'A' + 'a') : (a)
+
+static uint how_long_symbols,function_plus,function_mod,function_type;
+static uint char_table[256];
+static uchar unique_length[256];
+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;
+
+struct rand_struct {
+ unsigned long seed1,seed2,max_value;
+ double max_value_dbl;
+};
+
+void randominit(struct rand_struct *rand_st,ulong seed1, ulong seed2)
+{ /* For mysql 3.21.# */
+ rand_st->max_value= 0x3FFFFFFFL;
+ rand_st->max_value_dbl=(double) rand_st->max_value;
+ rand_st->seed1=seed1%rand_st->max_value ;
+ rand_st->seed2=seed2%rand_st->max_value;
+}
+
+double rnd(struct rand_struct *rand_st)
+{
+ rand_st->seed1=(rand_st->seed1*3+rand_st->seed2) % rand_st->max_value;
+ rand_st->seed2=(rand_st->seed1+rand_st->seed2+33) % rand_st->max_value;
+ return (((double) rand_st->seed1)/rand_st->max_value_dbl);
+}
+
+
+static void make_char_table(ulong t1,ulong t2,int type)
+{
+ uint i;
+ struct rand_struct rand_st;
+ randominit(&rand_st,t1,t2);
+
+ for (i=0 ; i < 256 ; i++)
+ {
+ switch (type) {
+ case 0: char_table[i]= i + (i << 8); break;
+ case 1: char_table[i]= i + ((i ^255 ) << 8); break;
+ case 2: char_table[i]= i; break;
+ case 3: char_table[i]= i + ((uint) (rnd(&rand_st)*255) << 8); break;
+ case 4: char_table[i]= (uint) (rnd(&rand_st)*255) + (i << 8); break;
+ }
+ }
+ char_table[0]|=1+257; // Avoid problems with 0
+ for (i=0 ; i < 256 ; i++)
+ {
+ uint tmp=(uint) (rnd(&rand_st)*255);
+ swap(uint,char_table[i],char_table[tmp]);
+ }
+ /* lower characters should be mapped to upper */
+ for (i= 'a' ; i <= 'z' ; i++)
+ {
+ /* This loop is coded with extra variables to avoid a bug in gcc 2.96 */
+ uchar tmp= (uchar) (i - 'a'); // Assume ascii
+ tmp+='A';
+ char_table[i]=char_table[tmp];
+ }
+}
+
+/* Fill array primes with primes between start and 'max_allowed_array' */
+
+static void make_prime_array(uint start)
+{
+ uint i,j,*to;
+ uint max_index=(uint) sqrt((double) max_allowed_array);
+
+ bzero((char*) primes,sizeof(primes[0])*max_allowed_array);
+
+ i=2;
+ while (i < max_index)
+ {
+ for (j=i+i ; j <= max_allowed_array ; j+=i)
+ primes[j]=1;
+ while (primes[++i]) ;
+ }
+
+ to=primes;
+ for (i=start ; i <= max_allowed_array ; i++)
+ if (!primes[i])
+ *to++=i;
+ *to=0; // end marker
+}
+
+#define USE_char_table
+
+static ulong tab_index_function(const char *s,uint add, uint type)
+{
+ register ulong nr=start_value+char_table[(uchar) *s]; // Nice value
+ ulong pos=3;
+ uint tmp_length=unique_length[(uchar) *s]-1;
+ while (*++s && tmp_length-- > 0)
+ {
+ switch (type) {
+ case 0:
+ nr= (nr ^ (char_table[(uchar) *s] + (nr << add)));
+ break;
+ case 1:
+ nr= (nr + (char_table[(uchar) *s] + (nr << add)));
+ break;
+ case 2:
+ nr= (nr ^ (char_table[(uchar) *s] ^ (nr << add)));
+ break;
+ case 3:
+ nr= (char_table[(uchar) *s] ^ (nr << add));
+ break;
+ case 4:
+ nr+= nr+nr+((nr & 63)+pos)*((ulong) char_table[(uchar) *s]);
+ pos+=add;
+ break;
+ }
+ }
+ return nr & INT_MAX24;
+}
+
+static int search(bool write_warning)
+{
+ uint size_symbols = sizeof(symbols)/sizeof(SYMBOL);
+ uint size_functions = sizeof(sql_functions)/sizeof(SYMBOL);
+ uint size=size_symbols + size_functions;
+ uint i=0,found,*prime,type;
+ int igra[max_allowed_array],test_count=INT_MAX;
+ uint possible_plus[how_much_for_plus*type_count+type_count];
+
+ how_long_symbols = sizeof(symbols)/sizeof(SYMBOL);
+
+ bzero((char*) possible_plus,sizeof(possible_plus));
+ found=0;
+
+ /* Check first which function_plus are possible */
+ for (type=0 ; type < type_count ; type ++)
+ {
+ for (function_plus = 1;
+ function_plus <= how_much_for_plus;
+ function_plus++)
+ {
+ bzero((char*) bits,sizeof(bits));
+ for (i=0; i < size; i++)
+ {
+ ulong order= tab_index_function ((i < how_long_symbols) ?
+ symbols[i].name :
+ sql_functions[i-how_long_symbols].name,
+ function_plus, type);
+ hash_results[type][function_plus][i]=order;
+ uint pos=order/8;
+ uint bit=order & 7;
+ if (bits[pos] & (1 << bit))
+ break;
+ bits[pos]|=1 << bit;
+ }
+ if (i == size)
+ {
+ possible_plus[found++]=function_plus;
+ }
+ }
+ possible_plus[found++]=0; // End marker
+ }
+ if (found == type_count)
+ {
+ if (write_warning)
+ fprintf(stderr,"\
+The hash function didn't return a unique value for any parameter\n\
+You have to change gen_lex_code.cc, function 'tab_index_function' to\n\
+generate unique values for some parameter. When you have succeeded in this,\n\
+you have to change 'main' to print out the new function\n");
+ return(1);
+ }
+
+ if (opt_verbose)
+ fprintf (stderr,"Info: Possible add values: %d\n",found-type_count);
+
+ for (prime=primes; (function_mod=*prime) ; prime++)
+ {
+ uint *plus_ptr=possible_plus;
+ for (type=0 ; type < type_count ; type++ )
+ {
+ while ((function_plus= *plus_ptr++))
+ {
+ ulong *order_pos= &hash_results[type][function_plus][0];
+ if (test_count++ == INT_MAX)
+ {
+ test_count=1;
+ bzero((char*) igra,sizeof(igra));
+ }
+ for (i=0; i<size ;i++)
+ {
+ ulong order;
+ order = *order_pos++ % function_mod;
+ if (igra[order] == test_count)
+ break;
+ igra[order] = test_count;
+ }
+ if (i == size)
+ {
+ *prime=0; // Mark this used
+ function_type=type;
+ return 0; // Found ok value
+ }
+ }
+ }
+ }
+
+ function_mod=max_allowed_array;
+ if (write_warning)
+ fprintf (stderr,"Fatal error when generating hash for symbols\n\
+Didn't find suitable values for perfect hashing:\n\
+You have to edit gen_lex_hase.cc to generate a new hashing function.\n\
+You can try running gen_lex_hash with --search to find a suitable value\n\
+Symbol array size = %d\n",function_mod);
+ return -1;
+}
+
+
+void print_arrays()
+{
+ uint size_symbols = sizeof(symbols)/sizeof(SYMBOL);
+ uint size_functions = sizeof(sql_functions)/sizeof(SYMBOL);
+ uint size=size_symbols + size_functions;
+ uint i;
+
+ fprintf(stderr,"Symbols: %d Functions: %d; Total: %d\nShifts per char: %d, Array size: %d\n",
+ size_symbols,size_functions,size_symbols+size_functions,
+ function_plus,function_mod);
+
+ int *prva= (int*) my_alloca(sizeof(int)*function_mod);
+ for (i=0 ; i <= function_mod; i++)
+ prva[i]= max_symbol;
+
+ for (i=0;i<size;i++)
+ {
+ ulong order = tab_index_function ((i < how_long_symbols) ? symbols[i].name : sql_functions[i - how_long_symbols].name,function_plus,function_type);
+ order %= function_mod;
+ prva [order] = i;
+ }
+
+#ifdef USE_char_table
+ printf("static uint16 char_table[] = {\n");
+ for (i=0; i < 255 ;i++) // < 255 is correct
+ {
+ printf("%u,",char_table[i]);
+ if (((i+1) & 15) == 0)
+ puts("");
+ }
+ printf("%d\n};\n\n\n",char_table[i]);
+#endif
+
+ printf("static uchar unique_length[] = {\n");
+ for (i=0; i < 255 ;i++) // < 255 is correct
+ {
+ printf("%u,",unique_length[i]);
+ if (((i+1) & 15) == 0)
+ puts("");
+ }
+ printf("%d\n};\n\n\n",unique_length[i]);
+
+ printf("static uint16 my_function_table[] = {\n");
+ for (i=0; i < function_mod-1 ;i++)
+ {
+ printf("%d,",prva[i]);
+ if (((i+1) % 12) == 0)
+ puts("");
+ }
+ printf("%d\n};\n\n\n",prva[i]);
+ my_afree((gptr) prva);
+}
+
+
+static struct option long_options[] =
+{
+ {"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.0 Distrib %s, for %s (%s)\n",
+ my_progname, MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE);
+ if (version)
+ return;
+ puts("Copyright (C) 2000 MySQL AB & MySQL Finland 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\
+-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");
+
+}
+
+static uint best_type;
+static ulong best_t1,best_t2;
+
+static int get_options(int argc, char **argv)
+{
+ int c,option_index=0;
+
+ while ((c=getopt_long(argc,argv,"?SvVr:R:t:",
+ long_options, &option_index)) != EOF)
+ {
+ switch(c) {
+ 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=1;
+ 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)
+ {
+ usage(0);
+ exit(1);
+ }
+ return(0);
+}
+
+static uint max_prefix(const char *name)
+{
+ uint i;
+ uint max_length=1;
+ for (i=0 ; i < sizeof(symbols)/sizeof(SYMBOL) ; i++)
+ {
+ const char *str=symbols[i].name;
+ if (str != name)
+ {
+ const char *str2=name;
+ uint length;
+ while (*str && *str == *str2)
+ {
+ str++;
+ str2++;
+ }
+ length=(uint) (str2 - name)+1;
+ if (length > max_length)
+ max_length=length;
+ }
+ }
+ for (i=0 ; i < sizeof(sql_functions)/sizeof(SYMBOL) ; i++)
+ {
+ const char *str=sql_functions[i].name;
+ if (str != name)
+ {
+ const char *str2=name;
+ uint length;
+ while (*str && *str == *str2)
+ {
+ str++;
+ str2++;
+ }
+ length=(uint) (str2 - name)+1;
+ if (length > max_length)
+ max_length=length;
+ }
+ }
+ return max_length;
+}
+
+
+static void make_max_length_table(void)
+{
+ uint i;
+ for (i=0 ; i < sizeof(symbols)/sizeof(SYMBOL) ; i++)
+ {
+ uint length=max_prefix(symbols[i].name);
+ if (length > unique_length[(uchar) symbols[i].name[0]])
+ {
+ unique_length[(uchar) symbols[i].name[0]]=length;
+ unique_length[(uchar) tolower(symbols[i].name[0])]=length;
+ }
+ }
+ for (i=0 ; i < sizeof(sql_functions)/sizeof(SYMBOL) ; i++)
+ {
+ uint length=max_prefix(sql_functions[i].name);
+ if (length > unique_length[(uchar) sql_functions[i].name[0]])
+ {
+ unique_length[(uchar) sql_functions[i].name[0]]=length;
+ unique_length[(uchar) tolower(sql_functions[i].name[0])]=length;
+ }
+ }
+}
+
+
+int main(int argc,char **argv)
+{
+ struct rand_struct rand_st;
+ static uint best_mod,best_add,best_functype;
+ int error;
+
+ MY_INIT(argv[0]);
+ start_value=1277803L; best_t1=331678L; best_t2=4097229L; best_type=1;
+ /* mode=5791 add=6 func_type: 0 */
+ if (get_options(argc,(char **) argv))
+ exit(1);
+
+ make_max_length_table();
+ make_char_table(best_t1,best_t2,best_type);
+ make_prime_array(sizeof(symbols)/sizeof(SYMBOL) +
+ sizeof(sql_functions)/sizeof(SYMBOL));
+
+ if ((error=search(1)) > 0 || error && !opt_search)
+ exit(1); // This should work
+ best_mod=function_mod; best_add=function_plus; best_functype=function_type;
+
+ if (opt_search)
+ {
+ time_t start_time=time((time_t*) 0);
+ randominit(&rand_st,start_time,start_time/2); // Some random values
+ 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);
+
+ for (uint i=1 ; i <= 100000 ; i++)
+ {
+ if (i % 10 == 0)
+ {
+ putchar('.');
+ fflush(stdout);
+ }
+ ulong t1=(ulong) (rnd(&rand_st)*INT_MAX24);
+ ulong t2=(ulong) (rnd(&rand_st)*INT_MAX24);
+ uint type=(int) (rnd(&rand_st)*char_table_count);
+ start_value=(ulong) (rnd(&rand_st)*INT_MAX24);
+ make_char_table(t1,t2,type);
+ if (!search(0))
+ {
+ best_mod=function_mod; best_add=function_plus;
+ best_functype=function_type;
+ best_t1=t1; best_t2=t2; best_type=type;
+ printf("\nstart_value=%ldL; best_t1=%ldL; best_t2=%ldL; best_type=%d; /* mode=%d add=%d func_type: %d */\n",
+ start_value,best_t1,best_t2,best_type,best_mod,best_add,best_functype);
+ }
+ }
+ }
+
+ function_mod=best_mod; function_plus=best_add;
+ make_char_table(best_t1,best_t2,best_type);
+
+ printf("/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB\n\
+ This program is free software; you can redistribute it and/or modify\n\
+ it under the terms of the GNU General Public License as published by\n\
+ the Free Software Foundation; either version 2 of the License, or\n\
+ (at your option) any later version.\n\n\
+ This program is distributed in the hope that it will be useful,\n\
+ but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\
+ GNU General Public License for more details.\n\n\
+ You should have received a copy of the GNU General Public License\n\
+ along with this program; if not, write to the Free Software\n\
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */\n\n");
+
+printf("/* This code is generated by gen_lex_hash.cc that seeks for a perfect\nhash function */\n\n");
+ printf("#include \"lex.h\"\n\n");
+
+ print_arrays();
+
+ printf("/* t1= %lu t2=%lu type= %d */\n\n",best_t1,best_t2,best_type);
+
+ printf("inline SYMBOL *get_hash_symbol(const char *s,unsigned int length,bool function)\n\
+{\n\
+ ulong idx = %lu+char_table[(uchar) *s];\n\
+ SYMBOL *sim;\n\
+ const char *start=s;\n\
+ int i=unique_length[(uchar) *s++];\n\
+ if (i > (int) length) i=(int) length;\n\
+ while (--i > 0)\n\
+ idx= (idx ^ (char_table[(uchar) *s++] + (idx << %d)));\n\
+ idx=my_function_table[(idx & %d) %% %d];\n\
+ if (idx >= %d)\n\
+ {\n\
+ if (!function || idx >= %d) return (SYMBOL*) 0;\n\
+ sim=sql_functions + (idx - %d);\n\
+ }\n\
+ else\n\
+ sim=symbols + idx;\n\
+ if ((length != sim->length) || lex_casecmp(start,sim->name,length))\n\
+ return (SYMBOL *)0;\n\
+ return sim;\n\
+}\n",(ulong) start_value,(int) function_plus,(int) how_much_and,function_mod,how_long_symbols,max_symbol,how_long_symbols);
+ exit(0);
+ return 0;
+}
diff --git a/sql/ha_berkeley.cc b/sql/ha_berkeley.cc
new file mode 100644
index 00000000000..f9cafd44839
--- /dev/null
+++ b/sql/ha_berkeley.cc
@@ -0,0 +1,1398 @@
+/* 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 */
+
+
+/*
+ TODO:
+ - Not compressed keys should use cmp_fix_length_key
+ - Don't automaticly pack all string keys (To do this we need to modify
+ CREATE TABLE so that one can use the pack_keys argument per key).
+ - An argument to pack_key that we don't want compression.
+ - Interaction with LOCK TABLES (Monty will fix this)
+ - DB_DBT_USERMEN should be used for fixed length tables
+ We will need an updated Berkeley DB version for this.
+ - Killing threads that has got a 'deadlock'
+ - SHOW TABLE STATUS should give more information about the table.
+ - Get a more accurate count of the number of rows.
+ - Introduce hidden primary keys for tables without a primary key
+ - We will need a manager thread that calls flush_logs, removes old
+ logs and makes checkpoints at given intervals.
+ - When not using UPDATE IGNORE, don't make a sub transaction but abort
+ the main transaction on errors.
+ - Handling of drop table during autocommit=0 ?
+ (Should we just give an error in this case if there is a pending
+ transaction ?)
+ - When using ALTER TABLE IGNORE, we should not start an transaction, but do
+ everything wthout transactions.
+
+ Testing of:
+ - ALTER TABLE
+ - LOCK TABLES
+ - CHAR keys
+ - BLOBS
+ - delete from t1;
+*/
+
+
+#ifdef __GNUC__
+#pragma implementation // gcc: Class implementation
+#endif
+
+#include "mysql_priv.h"
+#ifdef HAVE_BERKELEY_DB
+#include <m_ctype.h>
+#include <myisampack.h>
+#include <assert.h>
+#include <hash.h>
+#include "ha_berkeley.h"
+
+#define HA_BERKELEY_ROWS_IN_TABLE 10000 /* to get optimization right */
+#define HA_BERKELEY_RANGE_COUNT 100
+
+const char *ha_berkeley_ext=".db";
+bool berkeley_skip=0;
+u_int32_t berkeley_init_flags=0,berkeley_lock_type=DB_LOCK_DEFAULT;
+ulong berkeley_cache_size;
+char *berkeley_home, *berkeley_tmpdir, *berkeley_logdir;
+long berkeley_lock_scan_time=0;
+ulong berkeley_trans_retry=5;
+pthread_mutex_t bdb_mutex;
+
+static DB_ENV *db_env;
+static HASH bdb_open_tables;
+
+const char *berkeley_lock_names[] =
+{ "DEFAULT", "OLDEST","RANDOM","YOUNGEST" };
+u_int32_t berkeley_lock_types[]=
+{ DB_LOCK_DEFAULT, DB_LOCK_OLDEST, DB_LOCK_RANDOM };
+TYPELIB berkeley_lock_typelib= {array_elements(berkeley_lock_names),"",
+ berkeley_lock_names};
+
+static void berkeley_print_error(const char *db_errpfx, char *buffer);
+static byte* bdb_get_key(BDB_SHARE *share,uint *length,
+ my_bool not_used __attribute__((unused)));
+static BDB_SHARE *get_share(const char *table_name);
+static void free_share(BDB_SHARE *share);
+
+
+/* General functions */
+
+bool berkeley_init(void)
+{
+ char buff[1024],*config[10], **conf_pos, *str_pos;
+ conf_pos=config; str_pos=buff;
+ DBUG_ENTER("berkeley_init");
+
+ if (!berkeley_tmpdir)
+ berkeley_tmpdir=mysql_tmpdir;
+ if (!berkeley_home)
+ berkeley_home=mysql_real_data_home;
+
+ if (db_env_create(&db_env,0))
+ DBUG_RETURN(1);
+ db_env->set_errcall(db_env,berkeley_print_error);
+ db_env->set_errpfx(db_env,"bdb");
+ db_env->set_tmp_dir(db_env, berkeley_tmpdir);
+ db_env->set_data_dir(db_env, mysql_data_home);
+ if (berkeley_logdir)
+ db_env->set_lg_dir(db_env, berkeley_logdir);
+
+ if (opt_endinfo)
+ db_env->set_verbose(db_env,
+ DB_VERB_CHKPOINT | DB_VERB_DEADLOCK | DB_VERB_RECOVERY,
+ 1);
+
+ db_env->set_cachesize(db_env, 0, berkeley_cache_size, 0);
+ db_env->set_lk_detect(db_env, berkeley_lock_type);
+ if (db_env->open(db_env,
+ berkeley_home,
+ berkeley_init_flags | DB_INIT_LOCK |
+ DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN |
+ DB_CREATE | DB_THREAD | DB_PRIVATE, 0666))
+ {
+ db_env->close(db_env,0);
+ db_env=0;
+ }
+ (void) hash_init(&bdb_open_tables,32,0,0,
+ (hash_get_key) bdb_get_key,0,0);
+ pthread_mutex_init(&bdb_mutex,NULL);
+ DBUG_RETURN(db_env == 0);
+}
+
+
+bool berkeley_end(void)
+{
+ int error;
+ DBUG_ENTER("berkeley_end");
+ if (!db_env)
+ return 1;
+ error=db_env->close(db_env,0); // Error is logged
+ db_env=0;
+ hash_free(&bdb_open_tables);
+ pthread_mutex_destroy(&bdb_mutex);
+ DBUG_RETURN(error != 0);
+}
+
+bool berkeley_flush_logs()
+{
+ int error;
+ bool result=0;
+ DBUG_ENTER("berkeley_flush_logs");
+ if ((error=log_flush(db_env,0)))
+ {
+ my_error(ER_ERROR_DURING_FLUSH_LOGS,MYF(0),error);
+ result=1;
+ }
+ if ((error=txn_checkpoint(db_env,0,0,0)))
+ {
+ my_error(ER_ERROR_DURING_CHECKPOINT,MYF(0),error);
+ result=1;
+ }
+ DBUG_RETURN(result);
+}
+
+
+int berkeley_commit(THD *thd)
+{
+ DBUG_ENTER("berkeley_commit");
+ DBUG_PRINT("trans",("ending transaction"));
+ int error=txn_commit((DB_TXN*) thd->transaction.bdb_tid,0);
+#ifndef DBUG_OFF
+ if (error)
+ DBUG_PRINT("error",("error: %d",error));
+#endif
+ thd->transaction.bdb_tid=0;
+ DBUG_RETURN(error);
+}
+
+int berkeley_rollback(THD *thd)
+{
+ DBUG_ENTER("berkeley_rollback");
+ DBUG_PRINT("trans",("aborting transaction"));
+ int error=txn_abort((DB_TXN*) thd->transaction.bdb_tid);
+ thd->transaction.bdb_tid=0;
+ DBUG_RETURN(error);
+}
+
+
+static void berkeley_print_error(const char *db_errpfx, char *buffer)
+{
+ sql_print_error("%s: %s",db_errpfx,buffer);
+}
+
+
+
+/*****************************************************************************
+** Berkeley DB tables
+*****************************************************************************/
+
+const char **ha_berkeley::bas_ext() const
+{ static const char *ext[]= { ha_berkeley_ext, NullS }; return ext; }
+
+
+static int
+berkeley_cmp_packed_key(const DBT *new_key, const DBT *saved_key)
+{
+ KEY *key= (KEY*) new_key->app_private;
+ char *new_key_ptr= (char*) new_key->data;
+ char *saved_key_ptr=(char*) saved_key->data;
+ 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++)
+ {
+ int cmp;
+ if (key_part->null_bit)
+ {
+ if (*new_key_ptr++ != *saved_key_ptr++)
+ return ((int) new_key_ptr[-1] - (int) saved_key_ptr[-1]);
+ }
+ if ((cmp=key_part->field->pack_cmp(new_key_ptr,saved_key_ptr,
+ key_part->length)))
+ return cmp;
+ uint length=key_part->field->packed_col_length(new_key_ptr);
+ new_key_ptr+=length;
+ key_length-=length;
+ saved_key_ptr+=key_part->field->packed_col_length(saved_key_ptr);
+ }
+ return 0;
+}
+
+
+static int
+berkeley_cmp_fix_length_key(const DBT *new_key, const DBT *saved_key)
+{
+ KEY *key=(KEY*) (new_key->app_private);
+ char *new_key_ptr= (char*) new_key->data;
+ char *saved_key_ptr=(char*) saved_key->data;
+ 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++)
+ {
+ int cmp;
+ if ((cmp=key_part->field->pack_cmp(new_key_ptr,saved_key_ptr,0)))
+ return cmp;
+ new_key_ptr+=key_part->length;
+ key_length-= key_part->length;
+ saved_key_ptr+=key_part->length;
+ }
+ return 0;
+}
+
+
+int ha_berkeley::open(const char *name, int mode, int test_if_locked)
+{
+ char name_buff[FN_REFLEN];
+ uint open_mode=(mode == O_RDONLY ? DB_RDONLY : 0) | DB_THREAD;
+ int error;
+ DBUG_ENTER("ha_berkeley::open");
+
+ /* Need some extra memory in case of packed keys */
+ uint max_key_length= table->max_key_length + MAX_REF_PARTS*2;
+ if (!(alloc_ptr=
+ my_multi_malloc(MYF(MY_WME),
+ &key_file, table->keys*sizeof(*key_file),
+ &key_type, table->keys*sizeof(u_int32_t),
+ &key_buff, max_key_length,
+ &key_buff2, max_key_length,
+ &primary_key_buff,
+ table->key_info[table->primary_key].key_length,
+ NullS)))
+ DBUG_RETURN(1);
+ if (!(rec_buff=my_malloc((alloced_rec_buff_length=table->reclength),
+ MYF(MY_WME))))
+ {
+ my_free(alloc_ptr,MYF(0));
+ DBUG_RETURN(1);
+ }
+
+ /* Init table lock structure */
+ if (!(share=get_share(name)))
+ {
+ my_free(rec_buff,MYF(0));
+ my_free(alloc_ptr,MYF(0));
+ DBUG_RETURN(1);
+ }
+ thr_lock_data_init(&share->lock,&lock,(void*) 0);
+
+ if ((error=db_create(&file, db_env, 0)))
+ {
+ free_share(share);
+ my_free(rec_buff,MYF(0));
+ my_free(alloc_ptr,MYF(0));
+ my_errno=error;
+ DBUG_RETURN(1);
+ }
+
+ /* Open primary key */
+ file->set_bt_compare(file, berkeley_cmp_packed_key);
+ if ((error=(file->open(file, fn_format(name_buff,name,"", ha_berkeley_ext,
+ 2 | 4),
+ "main", DB_BTREE, open_mode,0))))
+ {
+ free_share(share);
+ my_free(rec_buff,MYF(0));
+ my_free(alloc_ptr,MYF(0));
+ my_errno=error;
+ DBUG_RETURN(1);
+ }
+
+ info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST);
+ transaction=0;
+ cursor=0;
+
+ fixed_length_row=!(table->db_create_options & HA_OPTION_PACK_RECORD);
+
+ /* Open other keys */
+ bzero((char*) key_file,sizeof(*key_file)*table->keys);
+ key_used_on_scan=primary_key=table->primary_key;
+ key_file[primary_key]=file;
+ bzero((char*) &current_row,sizeof(current_row));
+
+ DB **ptr=key_file;
+ for (uint i=0, used_keys=0; i < table->keys ; i++, ptr++)
+ {
+ char part[7];
+ key_type[i]=table->key_info[i].flags & HA_NOSAME ? DB_NOOVERWRITE : 0;
+ if (i != primary_key)
+ {
+ if ((error=db_create(ptr, db_env, 0)))
+ {
+ close();
+ my_errno=error;
+ DBUG_RETURN(1);
+ }
+ sprintf(part,"key%02d",++used_keys);
+ (*ptr)->set_bt_compare(*ptr, berkeley_cmp_packed_key);
+ if (!(table->key_info[i].flags & HA_NOSAME))
+ (*ptr)->set_flags(*ptr, DB_DUP);
+ if ((error=((*ptr)->open(*ptr, name_buff, part, DB_BTREE,
+ open_mode, 0))))
+ {
+ close();
+ my_errno=error;
+ DBUG_RETURN(1);
+ }
+ }
+ }
+ DBUG_RETURN(0);
+}
+
+
+void ha_berkeley::initialize(void)
+{
+ /* Calculate pack_length of primary key */
+ 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++)
+ ref_length+= key_part->field->max_packed_col_length(key_part->length);
+ fixed_length_primary_key=
+ (ref_length == table->key_info[primary_key].key_length);
+}
+
+int ha_berkeley::close(void)
+{
+ int error,result=0;
+ DBUG_ENTER("ha_berkeley::close");
+
+ for (uint i=0; i < table->keys; i++)
+ {
+ if (key_file[i] && (error=key_file[i]->close(key_file[i],0)))
+ result=error;
+ }
+ free_share(share);
+ my_free(rec_buff,MYF(MY_ALLOW_ZERO_PTR));
+ my_free(alloc_ptr,MYF(MY_ALLOW_ZERO_PTR));
+ if (result)
+ my_errno=result;
+ DBUG_RETURN(result);
+}
+
+
+/* Reallocate buffer if needed */
+
+bool ha_berkeley::fix_rec_buff_for_blob(ulong length)
+{
+ uint extra;
+ if (! rec_buff || length > alloced_rec_buff_length)
+ {
+ byte *newptr;
+ if (!(newptr=(byte*) my_realloc((gptr) rec_buff, length,
+ MYF(MY_ALLOW_ZERO_PTR))))
+ return 1;
+ rec_buff=newptr;
+ alloced_rec_buff_length=length;
+ }
+ return 0;
+}
+
+
+/* Calculate max length needed for row */
+
+ulong ha_berkeley::max_row_length(const byte *buf)
+{
+ ulong length=table->reclength + table->fields*2;
+ for (Field_blob **ptr=table->blob_field ; *ptr ; ptr++)
+ length+= (*ptr)->get_length(buf+(*ptr)->offset())+2;
+ return length;
+}
+
+
+/*
+ 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_berkeley::pack_row(DBT *row, const byte *record)
+{
+ bzero((char*) row,sizeof(*row));
+ if (fixed_length_row)
+ {
+ row->data=(void*) record;
+ row->size=table->reclength;
+ return 0;
+ }
+ if (table->blob_fields)
+ {
+ if (fix_rec_buff_for_blob(max_row_length(record)))
+ return HA_ERR_OUT_OF_MEM;
+ }
+
+ /* Copy null bits */
+ memcpy(rec_buff, record, table->null_bytes);
+ byte *ptr=rec_buff + table->null_bytes;
+
+ for (Field **field=table->field ; *field ; field++)
+ ptr=(byte*) (*field)->pack((char*) ptr,record + (*field)->offset());
+ row->data=rec_buff;
+ row->size= (size_t) (ptr - rec_buff);
+ return 0;
+}
+
+
+void ha_berkeley::unpack_row(char *record, DBT *row)
+{
+ if (fixed_length_row)
+ memcpy(record,row->data,table->reclength);
+ else
+ {
+ /* Copy null bits */
+ const char *ptr= (const char*) row->data;
+ memcpy(record, ptr, table->null_bytes);
+ ptr+=table->null_bytes;
+ for (Field **field=table->field ; *field ; field++)
+ ptr= (*field)->unpack(record + (*field)->offset(), ptr);
+ }
+}
+
+
+/*
+ Create a packed key from from a row
+ This will never fail as the key buffer is pre allocated.
+*/
+
+DBT *ha_berkeley::pack_key(DBT *key, uint keynr, char *buff,
+ const byte *record)
+{
+ 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;
+ DBUG_ENTER("pack_key");
+
+ bzero((char*) key,sizeof(*key));
+ key->data=buff;
+ key->app_private= key_info;
+
+ for ( ; key_part != end ; key_part++)
+ {
+ if (key_part->null_bit)
+ {
+ /* Store 0 if the key part is a NULL part */
+ if (record[key_part->null_offset] & key_part->null_bit)
+ {
+ *buff++ =0;
+ key->flags|=DB_DBT_DUPOK;
+ continue;
+ }
+ *buff++ = 1; // Store NOT NULL marker
+ }
+ buff=key_part->field->pack(buff,record + key_part->offset,
+ key_part->length);
+ }
+ key->size= (buff - (char*) key->data);
+ DBUG_DUMP("key",(char*) key->data, key->size);
+ DBUG_RETURN(key);
+}
+
+
+/*
+ Create a packed key from from a MySQL unpacked key
+*/
+
+DBT *ha_berkeley::pack_key(DBT *key, uint keynr, char *buff,
+ 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;
+ DBUG_ENTER("pack_key2");
+
+ bzero((char*) key,sizeof(*key));
+ key->data=buff;
+ key->app_private= key_info;
+
+ for (; key_part != end && (int) key_length > 0 ; key_part++)
+ {
+ uint offset=0;
+ if (key_part->null_bit)
+ {
+ offset=1;
+ if (!(*buff++ = (*key_ptr == 0))) // Store 0 if NULL
+ {
+ key_length-= key_part->store_length;
+ key_ptr+= key_part->store_length;
+ key->flags|=DB_DBT_DUPOK;
+ continue;
+ }
+ key_ptr++;
+ }
+ buff=key_part->field->keypack(buff,key_ptr+offset,key_part->length);
+ key_ptr+=key_part->store_length;
+ key_length-=key_part->store_length;
+ }
+ key->size= (buff - (char*) key->data);
+ DBUG_DUMP("key",(char*) key->data, key->size);
+ DBUG_RETURN(key);
+}
+
+
+int ha_berkeley::write_row(byte * record)
+{
+ DBT row,prim_key,key;
+ int error;
+ DBUG_ENTER("write_row");
+
+ statistic_increment(ha_write_count,&LOCK_status);
+ if (table->time_stamp)
+ update_timestamp(record+table->time_stamp-1);
+ if (table->next_number_field && record == table->record[0])
+ update_auto_increment();
+ if ((error=pack_row(&row, record)))
+ DBUG_RETURN(error);
+
+ if (table->keys == 1)
+ {
+ error=file->put(file, transaction, pack_key(&prim_key, primary_key,
+ key_buff, record),
+ &row, key_type[primary_key]);
+ }
+ else
+ {
+ for (uint retry=0 ; retry < berkeley_trans_retry ; retry++)
+ {
+ uint keynr;
+ DB_TXN *sub_trans;
+ if ((error=txn_begin(db_env, transaction, &sub_trans, 0)))
+ break;
+ DBUG_PRINT("trans",("starting subtransaction"));
+ if (!(error=file->put(file, sub_trans, pack_key(&prim_key, primary_key,
+ key_buff, record),
+ &row, key_type[primary_key])))
+ {
+ for (keynr=0 ; keynr < table->keys ; keynr++)
+ {
+ if (keynr == primary_key)
+ continue;
+ if ((error=key_file[keynr]->put(key_file[keynr], sub_trans,
+ pack_key(&key, keynr, key_buff2,
+ record),
+ &prim_key, key_type[keynr])))
+ {
+ last_dup_key=keynr;
+ break;
+ }
+ }
+ }
+ if (!error)
+ {
+ DBUG_PRINT("trans",("committing subtransaction"));
+ error=txn_commit(sub_trans, 0);
+ }
+ else
+ {
+ /* Remove inserted row */
+ int new_error;
+ DBUG_PRINT("error",("Got error %d",error));
+ DBUG_PRINT("trans",("aborting subtransaction"));
+ if ((new_error=txn_abort(sub_trans)))
+ {
+ error=new_error; // This shouldn't happen
+ break;
+ }
+ }
+ if (error != DB_LOCK_DEADLOCK)
+ break;
+ }
+ }
+ if (error == DB_KEYEXIST)
+ error=HA_ERR_FOUND_DUPP_KEY;
+ DBUG_RETURN(error);
+}
+
+
+/* Compare if a key in a row has changed */
+
+int ha_berkeley::key_cmp(uint keynr, const byte * old_row,
+ const byte * new_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++)
+ {
+ 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))
+ return 1;
+ }
+ if (key_part->key_part_flag & (HA_BLOB_PART | HA_VAR_LENGTH))
+ {
+
+ if (key_part->field->cmp_binary(old_row + key_part->offset,
+ new_row + key_part->offset,
+ (ulong) key_part->length))
+ return 1;
+ }
+ else
+ {
+ if (memcmp(old_row+key_part->offset, new_row+key_part->offset,
+ key_part->length))
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+/*
+ Update a row from one value to another.
+*/
+
+int ha_berkeley::update_primary_key(DB_TXN *trans, bool primary_key_changed,
+ const byte * old_row,
+ const byte * new_row, DBT *prim_key)
+{
+ DBT row, old_key;
+ int error,new_error;
+ DBUG_ENTER("update_primary_key");
+
+ if (primary_key_changed)
+ {
+ // Primary key changed or we are updating a key that can have duplicates.
+ // Delete the old row and add a new one
+ pack_key(&old_key, primary_key, key_buff2, old_row);
+ if ((error=remove_key(trans, primary_key, old_row, (DBT *) 0, &old_key)))
+ DBUG_RETURN(error); // This should always succeed
+ if ((error=pack_row(&row, new_row)))
+ {
+ // Out of memory (this shouldn't happen!)
+ (void) file->put(file, trans, &old_key, &row,
+ key_type[primary_key]);
+ DBUG_RETURN(error);
+ }
+ // Write new key
+ if ((error=file->put(file, trans, prim_key, &row, key_type[primary_key])))
+ {
+ // Probably a duplicated key; Return the error and let the caller
+ // abort.
+ last_dup_key=primary_key;
+ DBUG_RETURN(error);
+ }
+ }
+ else
+ {
+ // Primary key didn't change; just update the row data
+ if ((error=pack_row(&row, new_row)))
+ DBUG_RETURN(error);
+ error=file->put(file, trans, prim_key, &row, 0);
+ if (error)
+ DBUG_RETURN(error); // Fatal error
+ }
+ DBUG_RETURN(0);
+}
+
+
+
+int ha_berkeley::update_row(const byte * old_row, byte * new_row)
+{
+ DBT row, prim_key, key, old_prim_key;
+ int error;
+ uint keynr;
+ DB_TXN *sub_trans;
+ bool primary_key_changed;
+ DBUG_ENTER("update_row");
+
+ statistic_increment(ha_update_count,&LOCK_status);
+ if (table->time_stamp)
+ update_timestamp(new_row+table->time_stamp-1);
+ pack_key(&prim_key, primary_key, key_buff, new_row);
+
+ if ((primary_key_changed=key_cmp(primary_key, old_row, new_row)))
+ pack_key(&old_prim_key, primary_key, primary_key_buff, old_row);
+ else
+ old_prim_key=prim_key;
+
+ LINT_INIT(error);
+ for (uint retry=0 ; retry < berkeley_trans_retry ; retry++)
+ {
+ if ((error=txn_begin(db_env, transaction, &sub_trans, 0)))
+ break;
+ DBUG_PRINT("trans",("starting subtransaction"));
+ /* Start by updating the primary key */
+ if (!(error=update_primary_key(sub_trans, primary_key_changed,
+ old_row, new_row, &prim_key)))
+ {
+ // Update all other keys
+ for (uint keynr=0 ; keynr < table->keys ; keynr++)
+ {
+ if (keynr == primary_key)
+ continue;
+ if (key_cmp(keynr, old_row, new_row) || primary_key_changed)
+ {
+ if ((error=remove_key(sub_trans, keynr, old_row, (DBT*) 0,
+ &old_prim_key)) ||
+ (error=key_file[keynr]->put(key_file[keynr], sub_trans,
+ pack_key(&key, keynr, key_buff2,
+ new_row),
+ &prim_key, key_type[keynr])))
+ {
+ last_dup_key=keynr;
+ break;
+ }
+ }
+ }
+ }
+ if (!error)
+ {
+ DBUG_PRINT("trans",("committing subtransaction"));
+ error=txn_commit(sub_trans, 0);
+ }
+ else
+ {
+ /* Remove inserted row */
+ int new_error;
+ DBUG_PRINT("error",("Got error %d",error));
+ DBUG_PRINT("trans",("aborting subtransaction"));
+ if ((new_error=txn_abort(sub_trans)))
+ {
+ error=new_error; // This shouldn't happen
+ break;
+ }
+ }
+ if (error != DB_LOCK_DEADLOCK)
+ break;
+ }
+ if (error == DB_KEYEXIST)
+ error=HA_ERR_FOUND_DUPP_KEY;
+ DBUG_RETURN(error);
+}
+
+
+/*
+ Delete one key
+ This uses key_buff2, when keynr != primary key, so it's important that
+ a function that calls this doesn't use this buffer for anything else.
+ packed_record may be NULL if the key is unique
+*/
+
+int ha_berkeley::remove_key(DB_TXN *sub_trans, uint keynr, const byte *record,
+ DBT *packed_record,
+ DBT *prim_key)
+{
+ int error;
+ DBT key;
+ DBUG_ENTER("remove_key");
+ DBUG_PRINT("enter",("index: %d",keynr));
+
+ if ((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);
+ error=key_file[keynr]->del(key_file[keynr], sub_trans,
+ keynr == primary_key ?
+ prim_key :
+ pack_key(&key, keynr, key_buff2, record),
+ 0);
+ }
+ else
+ {
+ /*
+ To delete the not duplicated key, we need to open an cursor on the
+ 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);
+ DBC *cursor;
+ if (!(error=file->cursor(key_file[keynr], sub_trans, &cursor, 0)))
+ {
+ if (!(error=cursor->c_get(cursor,
+ (keynr == primary_key ?
+ prim_key :
+ pack_key(&key, keynr, key_buff2, record)),
+ (keynr == primary_key ?
+ packed_record : prim_key),
+ DB_GET_BOTH)))
+ { // This shouldn't happen
+ error=cursor->c_del(cursor,0);
+ }
+ int result=cursor->c_close(cursor);
+ if (!error)
+ error=result;
+ }
+ }
+ DBUG_RETURN(error);
+}
+
+
+/* Delete all keys for new_record */
+
+int ha_berkeley::remove_keys(DB_TXN *trans, const byte *record,
+ DBT *new_record, DBT *prim_key, key_map keys,
+ int result)
+{
+ for (uint keynr=0 ; keys ;keynr++, keys>>=1)
+ {
+ if (keys & 1)
+ {
+ int new_error=remove_key(trans, keynr, record, new_record, prim_key);
+ if (new_error)
+ {
+ result=new_error; // Return last error
+ if (trans)
+ break; // Let rollback correct things
+ }
+ }
+ }
+ return result;
+}
+
+
+int ha_berkeley::delete_row(const byte * record)
+{
+ int error;
+ DBT row, prim_key;
+ key_map keys=table->keys_in_use;
+ DBUG_ENTER("delete_row");
+ statistic_increment(ha_delete_count,&LOCK_status);
+
+ if ((error=pack_row(&row, record)))
+ DBUG_RETURN((error));
+ pack_key(&prim_key, primary_key, key_buff, record);
+ for (uint retry=0 ; retry < berkeley_trans_retry ; retry++)
+ {
+ DB_TXN *sub_trans;
+ if ((error=txn_begin(db_env, transaction, &sub_trans, 0)))
+ break;
+ DBUG_PRINT("trans",("starting sub transaction"));
+ if (!error)
+ error=remove_keys(sub_trans, record, &row, &prim_key, keys,0);
+ if (!error)
+ {
+ DBUG_PRINT("trans",("ending sub transaction"));
+ error=txn_commit(sub_trans, 0);
+ }
+ if (error)
+ {
+ /* retry */
+ int new_error;
+ DBUG_PRINT("error",("Got error %d",error));
+ DBUG_PRINT("trans",("aborting subtransaction"));
+ if ((new_error=txn_abort(sub_trans)))
+ {
+ error=new_error; // This shouldn't happen
+ break;
+ }
+ }
+ if (error != DB_LOCK_DEADLOCK)
+ break;
+ }
+ DBUG_RETURN(0);
+}
+
+
+int ha_berkeley::index_init(uint keynr)
+{
+ int error;
+ DBUG_ENTER("index_init");
+ active_index=keynr;
+ dbug_assert(cursor == 0);
+ if ((error=file->cursor(key_file[keynr], transaction, &cursor,
+ table->reginfo.lock_type > TL_WRITE_ALLOW_READ ?
+ 0 : 0)))
+ cursor=0; // Safety
+ bzero((char*) &last_key,sizeof(last_key));
+ DBUG_RETURN(error);
+}
+
+int ha_berkeley::index_end()
+{
+ int error=0;
+ DBUG_ENTER("index_end");
+ if (cursor)
+ {
+ error=cursor->c_close(cursor);
+ cursor=0;
+ }
+ DBUG_RETURN(error);
+}
+
+
+/* What to do after we have read a row based on an index */
+
+int ha_berkeley::read_row(int error, char *buf, uint keynr, DBT *row,
+ bool read_next)
+{
+ DBUG_ENTER("read_row");
+ if (error)
+ {
+ if (error == DB_NOTFOUND || error == DB_KEYEMPTY)
+ error=read_next ? HA_ERR_END_OF_FILE : HA_ERR_KEY_NOT_FOUND;
+ table->status=STATUS_NOT_FOUND;
+ DBUG_RETURN(error);
+ }
+
+ if (keynr != primary_key)
+ {
+ DBT key;
+ bzero((char*) &key,sizeof(key));
+ key.data=key_buff2;
+ key.size=row->size;
+ key.app_private=table->key_info+primary_key;
+ memcpy(key_buff2,row->data,row->size);
+ /* Read the data into current_row */
+ current_row.flags=DB_DBT_REALLOC;
+ if ((error=file->get(file, transaction, &key, &current_row, 0)))
+ {
+ table->status=STATUS_NOT_FOUND;
+ DBUG_RETURN(error == DB_NOTFOUND ? HA_ERR_CRASHED : error);
+ }
+ row= &current_row;
+ }
+ unpack_row(buf,row);
+ table->status=0;
+ DBUG_RETURN(0);
+}
+
+
+int ha_berkeley::index_read_idx(byte * buf, uint keynr, const byte * key,
+ uint key_len, enum ha_rkey_function find_flag)
+{
+ statistic_increment(ha_read_key_count,&LOCK_status);
+ DBUG_ENTER("index_read_idx");
+ current_row.flags=DB_DBT_REALLOC;
+ DBUG_RETURN(read_row(file->get(file, transaction,
+ pack_key(&last_key, keynr, key_buff, key,
+ key_len),
+ &current_row,0),
+ buf, keynr, &current_row, 0));
+}
+
+
+int ha_berkeley::index_read(byte * buf, const byte * key,
+ uint key_len, enum ha_rkey_function find_flag)
+{
+ DBT row;
+ DBUG_ENTER("index_read");
+ statistic_increment(ha_read_key_count,&LOCK_status);
+ bzero((char*) &row,sizeof(row));
+ DBUG_RETURN(read_row(cursor->c_get(cursor,
+ pack_key(&last_key, active_index,
+ key_buff, key, key_len),
+ &row, DB_SET),
+ buf, active_index, &row, 0));
+}
+
+
+int ha_berkeley::index_next(byte * buf)
+{
+ DBT row;
+ DBUG_ENTER("index_next");
+ statistic_increment(ha_read_next_count,&LOCK_status);
+ bzero((char*) &row,sizeof(row));
+ DBUG_RETURN(read_row(cursor->c_get(cursor, &last_key, &row, DB_NEXT),
+ buf, active_index, &row ,1));
+}
+
+int ha_berkeley::index_next_same(byte * buf, const byte *key, uint keylen)
+{
+ DBT row;
+ int error;
+ DBUG_ENTER("index_next_same");
+ statistic_increment(ha_read_next_count,&LOCK_status);
+ bzero((char*) &row,sizeof(row));
+ if (keylen == table->key_info[active_index].key_length)
+ error=read_row(cursor->c_get(cursor, &last_key, &row, DB_NEXT_DUP),
+ buf, active_index, &row,1);
+ else
+ {
+ error=read_row(cursor->c_get(cursor, &last_key, &row, DB_NEXT),
+ buf, active_index, &row,1);
+ if (!error && ::key_cmp(table, key, active_index, keylen))
+ error=HA_ERR_END_OF_FILE;
+ }
+ DBUG_RETURN(error);
+}
+
+
+int ha_berkeley::index_prev(byte * buf)
+{
+ DBT row;
+ DBUG_ENTER("index_prev");
+ statistic_increment(ha_read_prev_count,&LOCK_status);
+ bzero((char*) &row,sizeof(row));
+ DBUG_RETURN(read_row(cursor->c_get(cursor, &last_key, &row, DB_PREV),
+ buf, active_index, &row,1));
+}
+
+
+int ha_berkeley::index_first(byte * buf)
+{
+ DBT row;
+ DBUG_ENTER("index_first");
+ statistic_increment(ha_read_first_count,&LOCK_status);
+ bzero((char*) &row,sizeof(row));
+ DBUG_RETURN(read_row(cursor->c_get(cursor, &last_key, &row, DB_FIRST),
+ buf, active_index, &row,0));
+}
+
+int ha_berkeley::index_last(byte * buf)
+{
+ DBT row;
+ DBUG_ENTER("index_last");
+ statistic_increment(ha_read_last_count,&LOCK_status);
+ bzero((char*) &row,sizeof(row));
+ DBUG_RETURN(read_row(cursor->c_get(cursor, &last_key, &row, DB_LAST),
+ buf, active_index, &row,0));
+}
+
+int ha_berkeley::rnd_init(bool scan)
+{
+ current_row.flags=DB_DBT_REALLOC;
+ return index_init(primary_key);
+}
+
+int ha_berkeley::rnd_end()
+{
+ return index_end();
+}
+
+int ha_berkeley::rnd_next(byte *buf)
+{
+ DBT row;
+ DBUG_ENTER("rnd_next");
+ statistic_increment(ha_read_rnd_next_count,&LOCK_status);
+ bzero((char*) &row,sizeof(row));
+ DBUG_RETURN(read_row(cursor->c_get(cursor, &last_key, &row, DB_NEXT),
+ buf, active_index, &row, 1));
+}
+
+
+DBT *ha_berkeley::get_pos(DBT *to, byte *pos)
+{
+ bzero((char*) to,sizeof(*to));
+
+ to->data=pos;
+ to->app_private=table->key_info+primary_key;
+ if (fixed_length_primary_key)
+ to->size=ref_length;
+ else
+ {
+ 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++)
+ pos+=key_part->field->packed_col_length(pos);
+ to->size= (uint) (pos- (byte*) to->data);
+ }
+ return to;
+}
+
+
+int ha_berkeley::rnd_pos(byte * buf, byte *pos)
+{
+ DBT db_pos;
+ statistic_increment(ha_read_rnd_count,&LOCK_status);
+
+ return read_row(file->get(file, transaction,
+ get_pos(&db_pos, pos),
+ &current_row, 0),
+ buf, active_index, &current_row,0);
+}
+
+void ha_berkeley::position(const byte *record)
+{
+ DBT key;
+ pack_key(&key, primary_key, ref, record);
+}
+
+
+void ha_berkeley::info(uint flag)
+{
+ DBUG_ENTER("info");
+ if (flag & HA_STATUS_VARIABLE)
+ {
+ records = HA_BERKELEY_ROWS_IN_TABLE; // Just to get optimisations right
+ deleted = 0;
+ }
+ else if (flag & HA_STATUS_ERRKEY)
+ errkey=last_dup_key;
+ DBUG_VOID_RETURN;
+}
+
+
+int ha_berkeley::extra(enum ha_extra_function operation)
+{
+ return 0;
+}
+
+int ha_berkeley::reset(void)
+{
+ 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_berkeley::external_lock(THD *thd, int lock_type)
+{
+ int error=0;
+ DBUG_ENTER("ha_berkeley::external_lock");
+ if (lock_type != F_UNLCK)
+ {
+ if (!thd->transaction.bdb_lock_count++ && !thd->transaction.bdb_tid)
+ {
+ /* Found first lock, start transaction */
+ DBUG_PRINT("trans",("starting transaction"));
+ if ((error=txn_begin(db_env, 0,
+ (DB_TXN**) &thd->transaction.bdb_tid,
+ 0)))
+ thd->transaction.bdb_lock_count--;
+ }
+ transaction= (DB_TXN*) thd->transaction.bdb_tid;
+ }
+ else
+ {
+ lock.type=TL_UNLOCK; // Unlocked
+ if (current_row.flags & (DB_DBT_MALLOC | DB_DBT_REALLOC))
+ {
+ current_row.flags=0;
+ if (current_row.data)
+ {
+ free(current_row.data);
+ current_row.data=0;
+ }
+ }
+ current_row.data=0;
+ if (!--thd->transaction.bdb_lock_count)
+ {
+ if (thd->transaction.bdb_tid && (thd->options &
+ (OPTION_AUTO_COMMIT | OPTION_BEGIN)))
+ {
+ /*
+ F_UNLOCK is done without a transaction commit / rollback. This
+ means that something went wrong.
+ We can in this case silenty abort the transaction.
+ */
+ DBUG_PRINT("trans",("aborting transaction"));
+ error=txn_abort((DB_TXN*) thd->transaction.bdb_tid);
+ thd->transaction.bdb_tid=0;
+ }
+ }
+ }
+ DBUG_RETURN(error);
+}
+
+
+THR_LOCK_DATA **ha_berkeley::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;
+ }
+ *to++= &lock;
+ return to;
+}
+
+
+static int create_sub_table(const char *table_name, const char *sub_name,
+ DBTYPE type, int flags)
+{
+ int error,error2;
+ DB *file;
+ DBUG_ENTER("create_sub_table");
+ DBUG_PRINT("enter",("sub_name: %s",sub_name));
+
+ if (!(error=db_create(&file, db_env, 0)))
+ {
+ file->set_flags(file, flags);
+ error=(file->open(file, table_name, sub_name, type,
+ DB_THREAD | DB_CREATE, my_umask));
+ if (error)
+ {
+ DBUG_PRINT("error",("Got error: %d when opening table '%s'",error,
+ table_name));
+ (void) file->remove(file,table_name,NULL,0);
+ }
+ else
+ (void) file->close(file,0);
+ }
+ else
+ {
+ DBUG_PRINT("error",("Got error: %d when creting table",error));
+ }
+ if (error)
+ my_errno=error;
+ DBUG_RETURN(error);
+}
+
+
+int ha_berkeley::create(const char *name, register TABLE *form,
+ HA_CREATE_INFO *create_info)
+{
+ char name_buff[FN_REFLEN];
+ char part[7];
+ DBUG_ENTER("ha_berkeley::create");
+
+ fn_format(name_buff,name,"", ha_berkeley_ext,2 | 4);
+
+ /* Create the main table that will hold the real rows */
+ if (create_sub_table(name_buff,"main",DB_BTREE,0))
+ DBUG_RETURN(1);
+
+ /* Create the keys */
+ for (uint i=1; i < form->keys; i++)
+ {
+ sprintf(part,"key%02d",i);
+ if (create_sub_table(name_buff, part, DB_BTREE,
+ (table->key_info[i].flags & HA_NOSAME) ? 0 :
+ DB_DUP))
+ DBUG_RETURN(1);
+ }
+
+ /* Create the status block to save information from last status command */
+ /* Is DB_BTREE the best option here ? (QUEUE can't be used in sub tables) */
+ if (create_sub_table(name_buff,"status",DB_BTREE,0))
+ DBUG_RETURN(1);
+ DBUG_RETURN(0);
+}
+
+
+int ha_berkeley::delete_table(const char *name)
+{
+ int error;
+ char name_buff[FN_REFLEN];
+ if ((error=db_create(&file, db_env, 0)))
+ {
+ my_errno=error;
+ file=0;
+ return 1;
+ }
+ error=file->remove(file,fn_format(name_buff,name,"",ha_berkeley_ext,2 | 4),
+ NULL,0);
+ file=0; // Safety
+ 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
+ that we can decide if we should scan the table or use keys.
+*/
+
+double ha_berkeley::scan_time()
+{
+ return records/3;
+ }
+
+ha_rows ha_berkeley::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)
+{
+ DBT key;
+ DB_KEY_RANGE start_range, end_range;
+ double start_pos,end_pos,rows;
+ DBUG_ENTER("records_in_range");
+ if ((start_key && file->key_range(file,transaction,
+ pack_key(&key, keynr, key_buff, start_key,
+ start_key_len),
+ &start_range,0)) ||
+ (end_key && file->key_range(file,transaction,
+ pack_key(&key, keynr, key_buff, end_key,
+ end_key_len),
+ &end_range,0)))
+ DBUG_RETURN(HA_BERKELEY_RANGE_COUNT); // Better than returning an error
+
+ if (!start_key)
+ start_pos=0.0;
+ else if (start_search_flag == HA_READ_KEY_EXACT)
+ start_pos=start_range.less;
+ else
+ start_pos=start_range.less+start_range.equal;
+
+ if (!end_key)
+ end_pos=1.0;
+ else if (end_search_flag == HA_READ_BEFORE_KEY)
+ end_pos=end_range.less;
+ else
+ end_pos=end_range.less+end_range.equal;
+ rows=(end_pos-start_pos)*records;
+ DBUG_PRINT("exit",("rows: %g",rows));
+ DBUG_RETURN(rows <= 1.0 ? (ha_rows) 1 : (ha_rows) rows);
+}
+
+/****************************************************************************
+ Handling the shared BDB_SHARE structure that is needed to provide table
+ locking.
+****************************************************************************/
+
+static byte* bdb_get_key(BDB_SHARE *share,uint *length,
+ my_bool not_used __attribute__((unused)))
+{
+ *length=share->table_name_length;
+ return (byte*) share->table_name;
+}
+
+static BDB_SHARE *get_share(const char *table_name)
+{
+ BDB_SHARE *share;
+ pthread_mutex_lock(&bdb_mutex);
+ uint length=strlen(table_name);
+ if (!(share=(BDB_SHARE*) hash_search(&bdb_open_tables, table_name, length)))
+ {
+ if ((share=(BDB_SHARE *) my_malloc(sizeof(*share)+length+1,
+ MYF(MY_WME | MY_ZEROFILL))))
+ {
+ // pthread_mutex_init(&share->mutex);
+ // pthread_cond_init(&share->cond);
+ share->table_name_length=length;
+ share->table_name=(char*) (share+1);
+ strmov(share->table_name,table_name);
+ if (hash_insert(&bdb_open_tables, (char*) share))
+ {
+ pthread_mutex_unlock(&bdb_mutex);
+ my_free((gptr) share,0);
+ return 0;
+ }
+ thr_lock_init(&share->lock);
+ }
+ }
+ share->use_count++;
+ pthread_mutex_unlock(&bdb_mutex);
+ return share;
+}
+
+static void free_share(BDB_SHARE *share)
+{
+ pthread_mutex_lock(&bdb_mutex);
+ if (!--share->use_count)
+ {
+ hash_delete(&bdb_open_tables, (gptr) share);
+ thr_lock_delete(&share->lock);
+ // pthread_mutex_destroy(&share->mutex);
+ my_free((gptr) share, MYF(0));
+ }
+ pthread_mutex_unlock(&bdb_mutex);
+}
+
+#endif /* HAVE_BERKELEY_DB */
diff --git a/sql/ha_berkeley.h b/sql/ha_berkeley.h
new file mode 100644
index 00000000000..d8e55995635
--- /dev/null
+++ b/sql/ha_berkeley.h
@@ -0,0 +1,141 @@
+/* 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 __GNUC__
+#pragma interface /* gcc class implementation */
+#endif
+
+/* class for the the myisam handler */
+
+#include <db.h>
+
+typedef struct st_berkeley_share {
+ THR_LOCK lock;
+ char *table_name;
+ uint table_name_length,use_count;
+} BDB_SHARE;
+
+class ha_berkeley: public handler
+{
+ THR_LOCK_DATA lock;
+ DBT last_key,current_row;
+ gptr alloc_ptr;
+ byte *rec_buff;
+ char *key_buff, *key_buff2, *primary_key_buff;
+ DB *file, **key_file;
+ DB_TXN *transaction;
+ u_int32_t *key_type;
+ DBC *cursor;
+ BDB_SHARE *share;
+ ulong int_option_flag;
+ ulong alloced_rec_buff_length;
+ uint primary_key,last_dup_key;
+ bool fixed_length_row, fixed_length_primary_key;
+
+ bool fix_rec_buff_for_blob(ulong length);
+ ulong max_row_length(const byte *buf);
+ int pack_row(DBT *row,const byte *record);
+ void unpack_row(char *record, DBT *row);
+ DBT *pack_key(DBT *key, uint keynr, char *buff, const byte *record);
+ DBT *pack_key(DBT *key, uint keynr, char *buff, const byte *key,
+ uint key_length);
+ int remove_key(DB_TXN *trans, uint keynr, const byte *record,
+ DBT *packed_record, DBT *prim_key);
+ int remove_keys(DB_TXN *trans,const byte *record, DBT *new_record,
+ DBT *prim_key, key_map keys, int result);
+ int key_cmp(uint keynr, const byte * old_row, const byte * new_row);
+ int update_primary_key(DB_TXN *trans, bool primary_key_changed,
+ const byte * old_row, const byte * new_row,
+ DBT *prim_key);
+ int read_row(int error, char *buf, uint keynr, DBT *row, bool);
+ DBT *get_pos(DBT *to, byte *pos);
+
+ 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_KEYPOS_TO_RNDPOS+ HA_READ_ORDER+ HA_LASTKEY_ORDER+
+ HA_LONGLONG_KEYS+ HA_NULL_KEY +
+ HA_BLOB_KEY +
+ HA_REQUIRE_PRIMARY_KEY + HA_NOT_EXACT_COUNT +
+ HA_PRIMARY_KEY_IN_READ_INDEX + HA_DROP_BEFORE_CREATE),
+ last_dup_key((uint) -1)
+ {
+ }
+ ~ha_berkeley() {}
+ const char *table_type() const { return "BerkeleyDB"; }
+ const char **bas_ext() const;
+ ulong option_flag() const { return int_option_flag; }
+ 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; }
+ uint max_key_length() const { return MAX_KEY_LENGTH; }
+ bool fast_key_read() { return 1;}
+ bool has_transactions() { return 1;}
+
+ int open(const char *name, int mode, int test_if_locked);
+ void initialize(void);
+ 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 external_lock(THD *thd, int lock_type);
+ void ha_berkeley::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);
+
+ int ha_berkeley::create(const char *name, register TABLE *form,
+ HA_CREATE_INFO *create_info);
+ int ha_berkeley::delete_table(const char *name);
+ THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to,
+ enum thr_lock_type lock_type);
+};
+
+extern bool berkeley_skip;
+extern u_int32_t berkeley_init_flags,berkeley_lock_type,berkeley_lock_types[];
+extern ulong berkeley_cache_size;
+extern char *berkeley_home, *berkeley_tmpdir, *berkeley_logdir;
+extern long berkeley_lock_scan_time;
+extern TYPELIB berkeley_lock_typelib;
+
+bool berkeley_init(void);
+bool berkeley_end(void);
+bool berkeley_flush_logs(void);
+int berkeley_commit(THD *thd);
+int berkeley_rollback(THD *thd);
diff --git a/sql/ha_hash.h b/sql/ha_hash.h
new file mode 100644
index 00000000000..80416611406
--- /dev/null
+++ b/sql/ha_hash.h
@@ -0,0 +1,31 @@
+
+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
new file mode 100644
index 00000000000..989497cee6e
--- /dev/null
+++ b/sql/ha_heap.cc
@@ -0,0 +1,261 @@
+/* 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 __GNUC__
+#pragma implementation // gcc: Class implementation
+#endif
+
+#include "mysql_priv.h"
+#include <myisampack.h>
+#include "ha_heap.h"
+
+/*****************************************************************************
+** HEAP tables
+*****************************************************************************/
+
+const char **ha_heap::bas_ext() const
+{ static const char *ext[1]= { NullS }; return ext; }
+
+
+int ha_heap::open(const char *name, int mode, int test_if_locked)
+{
+ uint key,part,parts;
+ HP_KEYDEF *keydef;
+ HP_KEYSEG *seg;
+
+ for (key=parts=0 ; key < table->keys ; key++)
+ parts+=table->key_info[key].key_parts;
+
+ if (!(keydef=(HP_KEYDEF*) my_malloc(table->keys*sizeof(HP_KEYDEF)+
+ parts*sizeof(HP_KEYSEG),MYF(MY_WME))))
+ return my_errno;
+ seg=my_reinterpret_cast(HP_KEYSEG*) (keydef+table->keys);
+ for (key=0 ; key < table->keys ; key++)
+ {
+ KEY *pos=table->key_info+key;
+
+ keydef[key].keysegs=(uint) pos->key_parts;
+ keydef[key].flag = (pos->flags & HA_NOSAME);
+ keydef[key].seg=seg;
+
+ for (part=0 ; part < pos->key_parts ; part++)
+ {
+ uint flag=pos->key_part[part].key_type;
+ 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++;
+ }
+ }
+ file=heap_open(table->path,mode,
+ table->keys,keydef,
+ table->reclength,table->max_rows,
+ table->min_rows);
+ my_free((gptr) keydef,MYF(0));
+ info(HA_STATUS_NO_LOCK | HA_STATUS_CONST | HA_STATUS_VARIABLE);
+ ref_length=sizeof(HEAP_PTR);
+ return (!file ? errno : 0);
+}
+
+int ha_heap::close(void)
+{
+ return heap_close(file);
+}
+
+int ha_heap::write_row(byte * buf)
+{
+ statistic_increment(ha_write_count,&LOCK_status);
+ if (table->time_stamp)
+ update_timestamp(buf+table->time_stamp-1);
+ return heap_write(file,buf);
+}
+
+int ha_heap::update_row(const byte * old_data, byte * new_data)
+{
+ statistic_increment(ha_update_count,&LOCK_status);
+ if (table->time_stamp)
+ update_timestamp(new_data+table->time_stamp-1);
+ return heap_update(file,old_data,new_data);
+}
+
+int ha_heap::delete_row(const byte * buf)
+{
+ statistic_increment(ha_delete_count,&LOCK_status);
+ return heap_delete(file,buf);
+}
+
+int ha_heap::index_read(byte * buf, const byte * key,
+ uint key_len __attribute__((unused)),
+ enum ha_rkey_function find_flag
+ __attribute__((unused)))
+{
+ statistic_increment(ha_read_key_count,&LOCK_status);
+ int error=heap_rkey(file,buf,active_index, key);
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+int ha_heap::index_read_idx(byte * buf, uint index, const byte * key,
+ uint key_len __attribute__((unused)),
+ enum ha_rkey_function find_flag
+ __attribute__((unused)))
+{
+ statistic_increment(ha_read_key_count,&LOCK_status);
+ int error=heap_rkey(file, buf, index, key);
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+
+int ha_heap::index_next(byte * buf)
+{
+ statistic_increment(ha_read_next_count,&LOCK_status);
+ int error=heap_rnext(file,buf);
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+int ha_heap::index_prev(byte * buf)
+{
+ statistic_increment(ha_read_prev_count,&LOCK_status);
+ int error=heap_rprev(file,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);
+ int error=heap_rfirst(file, buf);
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+int ha_heap::index_last(byte * buf)
+{
+ statistic_increment(ha_read_last_count,&LOCK_status);
+ int error=heap_rlast(file, buf);
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+int ha_heap::rnd_init(bool scan)
+{
+ return scan ? heap_scan_init(file) : 0;
+}
+
+int ha_heap::rnd_next(byte *buf)
+{
+ statistic_increment(ha_read_rnd_next_count,&LOCK_status);
+ int error=heap_scan(file, buf);
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+int ha_heap::rnd_pos(byte * buf, byte *pos)
+{
+ int error;
+ HEAP_PTR position;
+ statistic_increment(ha_read_rnd_count,&LOCK_status);
+ memcpy_fixed((char*) &position,pos,sizeof(HEAP_PTR));
+ error=heap_rrnd(file, buf, position);
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+void ha_heap::position(const byte *record)
+{
+ *(HEAP_PTR*) ref= heap_position(file); // Ref is aligned
+}
+
+void ha_heap::info(uint flag)
+{
+ HEAPINFO info;
+ (void) heap_info(file,&info,flag);
+
+ records = info.records;
+ deleted = info.deleted;
+ errkey = info.errkey;
+ mean_rec_length=info.reclength;
+ data_file_length=info.data_length;
+ index_file_length=info.index_length;
+ max_data_file_length= info.max_records* info.reclength;
+ delete_length= info.deleted * info.reclength;
+}
+
+int ha_heap::extra(enum ha_extra_function operation)
+{
+ return heap_extra(file,operation);
+}
+
+int ha_heap::reset(void)
+{
+ return heap_extra(file,HA_EXTRA_RESET);
+}
+
+int ha_heap::delete_all_rows()
+{
+ heap_clear(file);
+ return 0;
+}
+
+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,
+ enum thr_lock_type lock_type)
+{
+ if (lock_type != TL_IGNORE && file->lock.type == TL_UNLOCK)
+ file->lock.type=lock_type;
+ *to++= &file->lock;
+ return to;
+}
+
+
+/*
+ We have to ignore ENOENT entries as the HEAP table is created on open and
+ not when doing a CREATE on the table.
+*/
+
+int ha_heap::delete_table(const char *name)
+{
+ int error=heap_delete_all(name);
+ return error == ENOENT ? 0 : error;
+}
+
+int ha_heap::rename_table(const char * from, const char * to)
+{
+ return heap_rename(from,to);
+}
+
+
+/* We can just delete the heap on creation */
+
+int ha_heap::create(const char *name, TABLE *form, HA_CREATE_INFO *create_info)
+
+{
+ char buff[FN_REFLEN];
+ return heap_create(fn_format(buff,name,"","",2));
+}
diff --git a/sql/ha_heap.h b/sql/ha_heap.h
new file mode 100644
index 00000000000..acbc0975f1e
--- /dev/null
+++ b/sql/ha_heap.h
@@ -0,0 +1,83 @@
+/* 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 __GNUC__
+#pragma interface /* gcc class implementation */
+#endif
+
+/* class for the the heap handler */
+
+#include <heap.h>
+
+class ha_heap: public handler
+{
+ HP_INFO *file;
+
+ public:
+ ha_heap(TABLE *table): handler(table), file(0) {}
+ ~ha_heap() {}
+ const char *table_type() const { return "HEAP"; }
+ const char **bas_ext() const;
+ ulong option_flag() const
+ { return (HA_READ_RND_SAME+HA_NO_INDEX+HA_BINARY_KEYS+HA_WRONG_ASCII_ORDER+
+ HA_KEYPOS_TO_RNDPOS+HA_NO_BLOBS+HA_REC_NOT_IN_SEQ); }
+ 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; }
+ uint max_key_length() const { return HA_MAX_REC_LENGTH; }
+ virtual double scan_time() { return (double) (records+deleted) / 100.0; }
+ virtual double read_time(ha_rows rows) { return (double) rows / 100.0; }
+ virtual bool fast_key_read() { return 1;}
+
+ int open(const char *name, int mode, int test_if_locked);
+ int close(void);
+ int write_row(byte * buf);
+ int update_row(const byte * old_data, byte * new_data);
+ int delete_row(const byte * buf);
+ int index_read(byte * buf, const byte * key,
+ 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_next(byte * buf);
+ int index_prev(byte * buf);
+ int index_first(byte * buf);
+ int index_last(byte * buf);
+ int rnd_init(bool scan=1);
+ 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 external_lock(THD *thd, int lock_type);
+ int delete_all_rows(void);
+
+ int delete_table(const char *from);
+ int rename_table(const char * from, const char * to);
+ 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_isam.cc b/sql/ha_isam.cc
new file mode 100644
index 00000000000..fc836977f23
--- /dev/null
+++ b/sql/ha_isam.cc
@@ -0,0 +1,390 @@
+/* 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 __GNUC__
+#pragma implementation // gcc: Class implementation
+#endif
+
+#include "mysql_priv.h"
+#include <m_ctype.h>
+#include <myisampack.h>
+#include "ha_isam.h"
+#ifndef MASTER
+#include "../srclib/isam/isamdef.h"
+#else
+#include "../isam/isamdef.h"
+#endif
+
+/*****************************************************************************
+** isam tables
+*****************************************************************************/
+
+const char **ha_isam::bas_ext() const
+{ static const char *ext[]= { ".ISD",".ISM", NullS }; return ext; }
+
+
+int ha_isam::open(const char *name, int mode, int test_if_locked)
+{
+ char name_buff[FN_REFLEN];
+ if (!(file=nisam_open(fn_format(name_buff,name,"","",2 | 4), mode,
+ test_if_locked)))
+ return (my_errno ? my_errno : -1);
+
+ if (!(test_if_locked == HA_OPEN_WAIT_IF_LOCKED ||
+ test_if_locked == HA_OPEN_ABORT_IF_LOCKED))
+ (void) nisam_extra(file,HA_EXTRA_NO_WAIT_LOCK);
+ info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST);
+ 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;
+ return (0);
+}
+
+int ha_isam::close(void)
+{
+ return !nisam_close(file) ? 0 : my_errno ? my_errno : -1;
+}
+
+uint ha_isam::min_record_length(uint options) const
+{
+ return (options & HA_OPTION_PACK_RECORD) ? 1 : 5;
+}
+
+
+int ha_isam::write_row(byte * buf)
+{
+ 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 !nisam_write(file,buf) ? 0 : my_errno ? my_errno : -1;
+}
+
+int ha_isam::update_row(const byte * old_data, byte * new_data)
+{
+ statistic_increment(ha_update_count,&LOCK_status);
+ if (table->time_stamp)
+ update_timestamp(new_data+table->time_stamp-1);
+ return !nisam_update(file,old_data,new_data) ? 0 : my_errno ? my_errno : -1;
+}
+
+int ha_isam::delete_row(const byte * buf)
+{
+ statistic_increment(ha_delete_count,&LOCK_status);
+ return !nisam_delete(file,buf) ? 0 : my_errno ? my_errno : -1;
+}
+
+int ha_isam::index_read(byte * buf, const byte * key,
+ uint key_len, enum ha_rkey_function find_flag)
+{
+ statistic_increment(ha_read_key_count,&LOCK_status);
+ int error=nisam_rkey(file, buf, active_index, key, key_len, find_flag);
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return !error ? 0 : my_errno ? my_errno : -1;
+}
+
+int ha_isam::index_read_idx(byte * buf, uint index, const byte * key,
+ uint key_len, enum ha_rkey_function find_flag)
+{
+ statistic_increment(ha_read_key_count,&LOCK_status);
+ int error=nisam_rkey(file, buf, index, key, key_len, find_flag);
+ 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);
+ int error=nisam_rnext(file,buf,active_index);
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return !error ? 0 : my_errno ? my_errno : HA_ERR_END_OF_FILE;
+}
+
+int ha_isam::index_prev(byte * buf)
+{
+ statistic_increment(ha_read_prev_count,&LOCK_status);
+ int error=nisam_rprev(file,buf, active_index);
+ 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);
+ int error=nisam_rfirst(file, buf, active_index);
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return !error ? 0 : my_errno ? my_errno : HA_ERR_END_OF_FILE;
+}
+
+int ha_isam::index_last(byte * buf)
+{
+ statistic_increment(ha_read_last_count,&LOCK_status);
+ int error=nisam_rlast(file, buf, active_index);
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return !error ? 0 : my_errno ? my_errno : HA_ERR_END_OF_FILE;
+}
+
+int ha_isam::rnd_init(bool scan)
+{
+ return nisam_extra(file,HA_EXTRA_RESET) ? 0 : my_errno ? my_errno : -1;;
+}
+
+int ha_isam::rnd_next(byte *buf)
+{
+ statistic_increment(ha_read_rnd_next_count,&LOCK_status);
+ int error=nisam_rrnd(file, buf, NI_POS_ERROR);
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return !error ? 0 : my_errno ? my_errno : -1;
+}
+
+int ha_isam::rnd_pos(byte * buf, byte *pos)
+{
+ statistic_increment(ha_read_rnd_count,&LOCK_status);
+ int error=nisam_rrnd(file, buf, (ulong) ha_get_ptr(pos,ref_length));
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return !error ? 0 : my_errno ? my_errno : -1;
+}
+
+void ha_isam::position(const byte *record)
+{
+ my_off_t position=nisam_position(file);
+ if (position == (my_off_t) ~ (ulong) 0)
+ position=HA_OFFSET_ERROR;
+ ha_store_ptr(ref, ref_length, position);
+}
+
+void ha_isam::info(uint flag)
+{
+ N_ISAMINFO info;
+ (void) nisam_info(file,&info,flag);
+ if (flag & HA_STATUS_VARIABLE)
+ {
+ records = info.records;
+ deleted = info.deleted;
+ data_file_length=info.data_file_length;
+ index_file_length=info.index_file_length;
+ delete_length = info.delete_length;
+ check_time = info.isamchk_time;
+ mean_rec_length=info.mean_reclength;
+ }
+ if (flag & HA_STATUS_CONST)
+ {
+ max_data_file_length=info.max_data_file_length;
+ max_index_file_length=info.max_index_file_length;
+ create_time = info.create_time;
+ sortkey = info.sortkey;
+ block_size=nisam_block_size;
+ table->keys = min(table->keys,info.keys);
+ table->keys_in_use= (((key_map) 1) << table->keys)- (key_map) 1;
+ table->db_options_in_use= info.options;
+ table->db_record_offset=
+ (table->db_options_in_use &
+ (HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD)) ? 0 :
+ table->reclength;
+ if (!table->tmp_table)
+ {
+ ulong *rec_per_key=info.rec_per_key;
+ for (uint i=0 ; i < table->keys ; i++)
+ {
+ table->key_info[i].rec_per_key[table->key_info[i].key_parts-1]=
+ *(rec_per_key++);
+ }
+ }
+ ref_length=4;
+ }
+ if (flag & HA_STATUS_ERRKEY)
+ {
+ errkey = info.errkey;
+ ha_store_ptr(dupp_ref, ref_length, info.dupp_key_pos);
+ }
+ if (flag & HA_STATUS_TIME)
+ update_time = info.update_time;
+}
+
+
+int ha_isam::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))
+ return 0;
+ return nisam_extra(file,operation);
+}
+
+int ha_isam::reset(void)
+{
+ return nisam_extra(file,HA_EXTRA_RESET);
+}
+
+int ha_isam::external_lock(THD *thd, int lock_type)
+{
+ return nisam_lock_database(file,lock_type);
+}
+
+
+THR_LOCK_DATA **ha_isam::store_lock(THD *thd,
+ THR_LOCK_DATA **to,
+ enum thr_lock_type lock_type)
+{
+ if (lock_type != TL_IGNORE && file->lock.type == TL_UNLOCK)
+ file->lock.type=lock_type;
+ *to++= &file->lock;
+ return to;
+}
+
+
+int ha_isam::create(const char *name, register TABLE *form,
+ HA_CREATE_INFO *create_info)
+
+{
+ uint options=form->db_options_in_use;
+ int error;
+ uint i,j,recpos,minpos,fieldpos,temp_length,length;
+ enum ha_base_keytype type;
+ char buff[FN_REFLEN];
+ KEY *pos;
+ N_KEYDEF keydef[MAX_KEY];
+ N_RECINFO *recinfo,*recinfo_pos;
+ DBUG_ENTER("ha_isam::create");
+
+ type=HA_KEYTYPE_BINARY; // Keep compiler happy
+ if (!(recinfo= (N_RECINFO*) my_malloc((form->fields*2+2)*sizeof(N_RECINFO),
+ MYF(MY_WME))))
+ DBUG_RETURN(1);
+
+ pos=form->key_info;
+ for (i=0; i < form->keys ; i++, pos++)
+ {
+ keydef[i].base.flag= (pos->flags & HA_NOSAME);
+ for (j=0 ; (int7) j < pos->key_parts ; j++)
+ {
+ keydef[i].seg[j].base.flag=pos->key_part[j].key_part_flag;
+ Field *field=pos->key_part[j].field;
+ type=field->key_type();
+
+ if ((options & HA_OPTION_PACK_KEYS ||
+ (pos->flags & (HA_PACK_KEY | HA_BINARY_PACK_KEY |
+ HA_SPACE_PACK_USED))) &&
+ pos->key_part[j].length > 8 &&
+ (type == HA_KEYTYPE_TEXT ||
+ type == HA_KEYTYPE_NUM ||
+ (type == HA_KEYTYPE_BINARY && !field->zero_pack())))
+ {
+ if (j == 0)
+ keydef[i].base.flag|=HA_PACK_KEY;
+ if (!(field->flags & ZEROFILL_FLAG) &&
+ (field->type() == FIELD_TYPE_STRING ||
+ field->type() == FIELD_TYPE_VAR_STRING ||
+ ((int) (pos->key_part[j].length - field->decimals()))
+ >= 4))
+ keydef[i].seg[j].base.flag|=HA_SPACE_PACK;
+ }
+ keydef[i].seg[j].base.type=(int) type;
+ keydef[i].seg[j].base.start= pos->key_part[j].offset;
+ keydef[i].seg[j].base.length= pos->key_part[j].length;
+ }
+ keydef[i].seg[j].base.type=(int) HA_KEYTYPE_END; /* End of key-parts */
+ }
+
+ recpos=0; recinfo_pos=recinfo;
+ while (recpos < (uint) form->reclength)
+ {
+ Field **field,*found=0;
+ minpos=form->reclength; length=0;
+
+ for (field=form->field ; *field ; field++)
+ {
+ if ((fieldpos=(*field)->offset()) >= recpos &&
+ fieldpos <= minpos)
+ {
+ /* skip null fields */
+ if (!(temp_length= (*field)->pack_length()))
+ continue; /* Skipp null-fields */
+ if (! found || fieldpos < minpos ||
+ (fieldpos == minpos && temp_length < length))
+ {
+ minpos=fieldpos; found= *field; length=temp_length;
+ }
+ }
+ }
+ DBUG_PRINT("loop",("found: %lx recpos: %d minpos: %d length: %d",
+ found,recpos,minpos,length));
+ if (recpos != minpos)
+ { // Reserved space (Null bits?)
+ recinfo_pos->base.type=(int) FIELD_NORMAL;
+ recinfo_pos++->base.length= (uint16) (minpos-recpos);
+ }
+ if (! found)
+ break;
+
+ if (found->flags & BLOB_FLAG)
+ {
+ /* ISAM can only handle blob pointers of sizeof(char(*)) */
+ recinfo_pos->base.type= (int) FIELD_BLOB;
+ if (options & HA_OPTION_LONG_BLOB_PTR)
+ length= length-portable_sizeof_char_ptr+sizeof(char*);
+ }
+ 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;
+ 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);
+ recinfo_pos++ ->base.length=(uint16) length;
+ recpos=minpos+length;
+ DBUG_PRINT("loop",("length: %d type: %d",
+ recinfo_pos[-1].base.length,recinfo_pos[-1].base.type));
+
+ if ((found->flags & BLOB_FLAG) && (options & HA_OPTION_LONG_BLOB_PTR) &&
+ sizeof(char*) != portable_sizeof_char_ptr)
+ { // Not used space
+ recinfo_pos->base.type=(int) FIELD_ZERO;
+ recinfo_pos++->base.length=
+ (uint16) (portable_sizeof_char_ptr-sizeof(char*));
+ recpos+= (portable_sizeof_char_ptr-sizeof(char*));
+ }
+ }
+ 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);
+ my_free((gptr) recinfo,MYF(0));
+ DBUG_RETURN(error);
+
+}
+
+
+ha_rows ha_isam::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) nisam_records_in_range(file,
+ inx,
+ start_key,start_key_len,
+ start_search_flag,
+ end_key,end_key_len,
+ end_search_flag);
+}
diff --git a/sql/ha_isam.h b/sql/ha_isam.h
new file mode 100644
index 00000000000..440f029451f
--- /dev/null
+++ b/sql/ha_isam.h
@@ -0,0 +1,87 @@
+/* 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 __GNUC__
+#pragma interface /* gcc class implementation */
+#endif
+
+/* class for the the myisam handler */
+
+#include <nisam.h>
+
+class ha_isam: public handler
+{
+ N_INFO *file;
+ uint int_option_flag;
+
+ 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_isam() {}
+ const char *table_type() const { return "ISAM"; }
+ const char **bas_ext() const;
+ ulong option_flag() const { return int_option_flag; }
+ 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; }
+ uint max_key_length() const { return N_MAX_KEY_LENGTH; }
+ uint min_record_length(uint options) const;
+ bool low_byte_first() const { return 0; }
+
+ int open(const char *name, int mode, int test_if_locked);
+ int close(void);
+ int write_row(byte * buf);
+ int update_row(const byte * old_data, byte * new_data);
+ int delete_row(const byte * buf);
+ int index_read(byte * buf, const byte * key,
+ 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_next(byte * buf);
+ int index_prev(byte * buf);
+ int index_first(byte * buf);
+ int index_last(byte * buf);
+ int rnd_init(bool scan=1);
+ int rnd_next(byte *buf);
+ int rnd_pos(byte * buf, byte *pos);
+ void position(const byte *record);
+ my_off_t row_position() { return nisam_position(file); }
+ void info(uint);
+ int extra(enum ha_extra_function operation);
+ int reset(void);
+ int external_lock(THD *thd, int lock_type);
+ 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);
+
+ 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
new file mode 100644
index 00000000000..66e310b5256
--- /dev/null
+++ b/sql/ha_isammrg.cc
@@ -0,0 +1,210 @@
+/* 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 __GNUC__
+#pragma implementation // gcc: Class implementation
+#endif
+
+#include "mysql_priv.h"
+#include <m_ctype.h>
+#ifndef MASTER
+#include "../srclib/merge/mrgdef.h"
+#else
+#include "../merge/mrgdef.h"
+#endif
+#include "ha_isammrg.h"
+
+/*****************************************************************************
+** ISAM MERGE tables
+*****************************************************************************/
+
+const char **ha_isammrg::bas_ext() const
+{ static const char *ext[]= { ".MRG", NullS }; return ext; }
+
+int ha_isammrg::open(const char *name, int mode, int test_if_locked)
+{
+ char name_buff[FN_REFLEN];
+ if (!(file=mrg_open(fn_format(name_buff,name,"","",2 | 4), mode,
+ test_if_locked)))
+ return (my_errno ? my_errno : -1);
+
+ if (!(test_if_locked == HA_OPEN_WAIT_IF_LOCKED ||
+ test_if_locked == HA_OPEN_ABORT_IF_LOCKED))
+ mrg_extra(file,HA_EXTRA_NO_WAIT_LOCK);
+ info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST);
+ if (!(test_if_locked & HA_OPEN_WAIT_IF_LOCKED))
+ mrg_extra(file,HA_EXTRA_WAIT_LOCK);
+ if (table->reclength != mean_rec_length)
+ {
+ DBUG_PRINT("error",("reclength: %d mean_rec_length: %d",
+ table->reclength, mean_rec_length));
+ mrg_close(file);
+ file=0;
+ return ER_WRONG_MRG_TABLE;
+ }
+ return (0);
+}
+
+int ha_isammrg::close(void)
+{
+ return !mrg_close(file) ? 0 : my_errno ? my_errno : -1;
+}
+
+uint ha_isammrg::min_record_length(uint options) const
+{
+ return (options & HA_OPTION_PACK_RECORD) ? 1 : 5;
+}
+
+int ha_isammrg::write_row(byte * buf)
+{
+ return (my_errno=HA_ERR_WRONG_COMMAND);
+}
+
+int ha_isammrg::update_row(const byte * old_data, byte * new_data)
+{
+ statistic_increment(ha_update_count,&LOCK_status);
+ if (table->time_stamp)
+ update_timestamp(new_data+table->time_stamp-1);
+ return !mrg_update(file,old_data,new_data) ? 0 : my_errno ? my_errno : -1;
+}
+
+int ha_isammrg::delete_row(const byte * buf)
+{
+ statistic_increment(ha_delete_count,&LOCK_status);
+ return !mrg_delete(file,buf) ? 0 : my_errno ? my_errno : -1;
+}
+
+int ha_isammrg::index_read(byte * buf, const byte * key,
+ uint key_len, enum ha_rkey_function find_flag)
+{
+ return (my_errno=HA_ERR_WRONG_COMMAND);
+}
+
+int ha_isammrg::index_read_idx(byte * buf, uint index, const byte * key,
+ uint key_len, enum ha_rkey_function find_flag)
+{
+ return (my_errno=HA_ERR_WRONG_COMMAND);
+}
+
+int ha_isammrg::index_next(byte * buf)
+{
+ return (my_errno=HA_ERR_WRONG_COMMAND);
+}
+
+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);
+}
+
+int ha_isammrg::index_last(byte * buf)
+{
+ return (my_errno=HA_ERR_WRONG_COMMAND);
+}
+
+int ha_isammrg::rnd_init(bool scan)
+{
+ return !mrg_extra(file,HA_EXTRA_RESET) ? 0 : my_errno ? my_errno : -1;
+}
+
+int ha_isammrg::rnd_next(byte *buf)
+{
+ statistic_increment(ha_read_rnd_next_count,&LOCK_status);
+ int error=mrg_rrnd(file, buf, ~(mrg_off_t) 0);
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return !error ? 0 : my_errno ? my_errno : -1;
+}
+
+int ha_isammrg::rnd_pos(byte * buf, byte *pos)
+{
+ statistic_increment(ha_read_rnd_count,&LOCK_status);
+ int error=mrg_rrnd(file, buf, (ulong) ha_get_ptr(pos,ref_length));
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return !error ? 0 : my_errno ? my_errno : -1;
+}
+
+void ha_isammrg::position(const byte *record)
+{
+ ulong position= mrg_position(file);
+ ha_store_ptr(ref, ref_length, (my_off_t) position);
+}
+
+
+void ha_isammrg::info(uint flag)
+{
+ MERGE_INFO info;
+ (void) mrg_info(file,&info,flag);
+ records = (ha_rows) info.records;
+ deleted = (ha_rows) info.deleted;
+ data_file_length=info.data_file_length;
+ errkey = info.errkey;
+ table->keys_in_use=0; // No keys yet
+ table->db_options_in_use = info.options;
+ mean_rec_length=info.reclength;
+ block_size=0;
+ update_time=0;
+ ref_length=4; // Should be big enough
+}
+
+
+int ha_isammrg::extra(enum ha_extra_function operation)
+{
+ return !mrg_extra(file,operation) ? 0 : my_errno ? my_errno : -1;
+}
+
+int ha_isammrg::reset(void)
+{
+ return !mrg_extra(file,HA_EXTRA_RESET) ? 0 : my_errno ? my_errno : -1;
+}
+
+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
+{
+ return file->tables;
+}
+
+THR_LOCK_DATA **ha_isammrg::store_lock(THD *thd,
+ THR_LOCK_DATA **to,
+ enum thr_lock_type lock_type)
+{
+ MRG_TABLE *table;
+
+ for (table=file->open_tables ; table != file->end_table ; table++)
+ {
+ *(to++)= &table->table->lock;
+ if (lock_type != TL_IGNORE && table->table->lock.type == TL_UNLOCK)
+ table->table->lock.type=lock_type;
+ }
+ return to;
+}
+
+
+int ha_isammrg::create(const char *name, register TABLE *form,
+ HA_CREATE_INFO *create_info)
+
+{
+ char buff[FN_REFLEN];
+ return mrg_create(fn_format(buff,name,"","",2+4+16),0);
+}
diff --git a/sql/ha_isammrg.h b/sql/ha_isammrg.h
new file mode 100644
index 00000000000..9fe1984d76f
--- /dev/null
+++ b/sql/ha_isammrg.h
@@ -0,0 +1,69 @@
+/* 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 __GNUC__
+#pragma interface /* gcc class implementation */
+#endif
+
+/* class for the the myisam merge handler */
+
+#include <merge.h>
+
+class ha_isammrg: public handler
+{
+ MRG_INFO *file;
+
+ public:
+ ha_isammrg(TABLE *table): handler(table), file(0) {}
+ ~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;}
+ uint max_record_length() const { return HA_MAX_REC_LENGTH; }
+ uint max_keys() const { return 0; }
+ uint max_key_parts() const { return 0; }
+ uint max_key_length() const { return 0; }
+ bool low_byte_first() const { return 0; }
+ uint min_record_length(uint options) const;
+
+ int open(const char *name, int mode, int test_if_locked);
+ int close(void);
+ int write_row(byte * buf);
+ int update_row(const byte * old_data, byte * new_data);
+ int delete_row(const byte * buf);
+ int index_read(byte * buf, const byte * key,
+ uint key_len, enum ha_rkey_function find_flag);
+ int index_read_idx(byte * buf, uint indx, const byte * key,
+ uint key_len, enum ha_rkey_function find_flag);
+ int index_next(byte * buf);
+ int index_prev(byte * buf);
+ int index_first(byte * buf);
+ int index_last(byte * buf);
+ int rnd_init(bool scan=1);
+ int rnd_next(byte *buf);
+ int rnd_pos(byte * buf, byte *pos);
+ void position(const byte *record);
+ my_off_t row_position() { return mrg_position(file); }
+ void info(uint);
+ int extra(enum ha_extra_function operation);
+ int reset(void);
+ int external_lock(THD *thd, int lock_type);
+ uint lock_count(void) const;
+ 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_myisam.cc b/sql/ha_myisam.cc
new file mode 100644
index 00000000000..513a5affcb2
--- /dev/null
+++ b/sql/ha_myisam.cc
@@ -0,0 +1,873 @@
+/* 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 __GNUC__
+#pragma implementation // gcc: Class implementation
+#endif
+
+#include "mysql_priv.h"
+#include <m_ctype.h>
+#include <myisampack.h>
+#include "ha_myisam.h"
+#include <stdarg.h>
+#ifndef MASTER
+#include "../srclib/myisam/myisamdef.h"
+#else
+#include "../myisam/myisamdef.h"
+#endif
+
+ulong myisam_sort_buffer_size;
+#if !defined(HAVE_PREAD)
+pthread_mutex_t THR_LOCK_keycache;
+#endif
+
+/*****************************************************************************
+** MyISAM tables
+*****************************************************************************/
+
+// 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)
+{
+ THD* thd = (THD*)param->thd;
+ String* packet = &thd->packet;
+ packet->length(0);
+ char msgbuf[MI_MAX_MSG_BUF];
+ msgbuf[0] = 0;
+
+ my_vsnprintf(msgbuf, sizeof(msgbuf), fmt, args);
+ msgbuf[sizeof(msgbuf) - 1] = 0; // healthy paranoia
+
+ if (thd->net.vio == 0)
+ {
+ sql_print_error(msgbuf);
+ return;
+ }
+
+ net_store_data(packet, param->table_name);
+ net_store_data(packet, param->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()))
+ fprintf(stderr,
+ "Failed on my_net_write, writing to stderr instead: %s\n",
+ msgbuf);
+ return;
+}
+
+extern "C" {
+
+void mi_check_print_error(MI_CHECK *param, const char *fmt,...)
+{
+ param->error_printed|=1;
+ va_list args;
+ va_start(args, fmt);
+ mi_check_print_msg(param, "error", fmt, args);
+ va_end(args);
+}
+
+void mi_check_print_info(MI_CHECK *param, const char *fmt,...)
+{
+ va_list args;
+ va_start(args, fmt);
+ mi_check_print_msg(param, "info", fmt, args);
+ va_end(args);
+}
+
+void mi_check_print_warning(MI_CHECK *param, const char *fmt,...)
+{
+ param->warning_printed=1;
+ va_list args;
+ va_start(args, fmt);
+ mi_check_print_msg(param, "warning", fmt, args);
+ va_end(args);
+}
+
+}
+
+const char **ha_myisam::bas_ext() const
+{ static const char *ext[]= { ".MYD",".MYI", NullS }; return ext; }
+
+
+int ha_myisam::net_read_dump(NET* net)
+{
+ MYISAM_SHARE* share = file->s;
+ int data_fd = file->dfile;
+ int error = 0;
+
+ my_seek(data_fd, 0L, MY_SEEK_SET, MYF(MY_WME));
+ for(;;)
+ {
+ uint packet_len = my_net_read(net);
+ if (!packet_len)
+ break ; // end of file
+ if (packet_len == packet_error)
+ {
+ sql_print_error("ha_myisam::net_read_dump - read error ");
+ error= -1;
+ goto err;
+ }
+ if (my_write(data_fd, (byte*)net->read_pos, packet_len,
+ MYF(MY_WME|MY_FNABP)))
+ {
+ error = errno;
+ goto err;
+ }
+ }
+
+err:
+ return error;
+}
+
+
+int ha_myisam::dump(THD* thd, int fd)
+{
+ MYISAM_SHARE* share = file->s;
+ NET* net = &thd->net;
+ uint blocksize = share->blocksize;
+ my_off_t bytes_to_read = share->state.state.data_file_length;
+ int data_fd = file->dfile;
+ byte * buf = (byte*) my_malloc(blocksize, MYF(MY_WME));
+ if(!buf)
+ return ENOMEM;
+
+ int error = 0;
+ my_seek(data_fd, 0L, MY_SEEK_SET, MYF(MY_WME));
+ for(; bytes_to_read > 0;)
+ {
+ uint bytes = my_read(data_fd, buf, blocksize, MYF(MY_WME));
+ if (bytes == MY_FILE_ERROR)
+ {
+ error = errno;
+ goto err;
+ }
+
+ if (fd >= 0)
+ {
+ if (my_write(fd, buf, bytes, MYF(MY_WME | MY_FNABP)))
+ {
+ error = errno ? errno : EPIPE;
+ goto err;
+ }
+ }
+ else
+ {
+ if (my_net_write(net, (char*) buf, bytes))
+ {
+ error = errno ? errno : EPIPE;
+ goto err;
+ }
+ }
+ bytes_to_read -= bytes;
+ }
+
+ if (fd < 0)
+ {
+ my_net_write(net, "", 0);
+ net_flush(net);
+ }
+
+err:
+ my_free((gptr) buf, MYF(0));
+ return error;
+}
+
+int ha_myisam::open(const char *name, int mode, int test_if_locked)
+{
+ char name_buff[FN_REFLEN];
+ if (!(file=mi_open(fn_format(name_buff,name,"","",2 | 4), mode,
+ test_if_locked)))
+ return (my_errno ? my_errno : -1);
+
+ if (!(test_if_locked == HA_OPEN_WAIT_IF_LOCKED ||
+ test_if_locked == HA_OPEN_ABORT_IF_LOCKED))
+ VOID(mi_extra(file,HA_EXTRA_NO_WAIT_LOCK));
+ 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));
+ if (!table->db_record_offset)
+ int_option_flag|=HA_REC_NOT_IN_SEQ;
+ return (0);
+}
+
+int ha_myisam::close(void)
+{
+ MI_INFO *tmp=file;
+ file=0;
+ return mi_close(tmp);
+}
+
+int ha_myisam::write_row(byte * buf)
+{
+ 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 mi_write(file,buf);
+}
+
+int ha_myisam::check(THD* thd, HA_CHECK_OPT* check_opt)
+{
+ if (!file) return HA_CHECK_INTERNAL_ERROR;
+ int error ;
+ MI_CHECK param;
+ MYISAM_SHARE* share = file->s;
+
+ myisamchk_init(&param);
+ param.thd = thd;
+ param.op_name = (char*)"check";
+ param.table_name = table->table_name;
+ param.testflag = check_opt->flags | T_CHECK | T_SILENT;
+ if (check_opt->quick)
+ param.testflag |= T_FAST;
+
+ if (!(table->db_stat & HA_READ_ONLY))
+ param.testflag|= T_STATISTICS;
+ param.using_global_keycache = 1;
+
+ error = chk_size(&param, file);
+ if (!((param.testflag & T_FAST) && share->state.open_count == 1 &&
+ !share->state.changed))
+ {
+ if (!error)
+ error |= chk_del(&param, file, param.testflag);
+ if (!error)
+ error = chk_key(&param, file);
+ if (!error)
+ {
+ if (!(param.testflag & T_FAST) ||
+ (share->options & (HA_OPTION_PACK_RECORD | HA_OPTION_COMPRESS_RECORD)))
+ {
+ init_io_cache(&param.read_cache, file->dfile,
+ my_default_record_cache_size, READ_CACHE,
+ share->pack.header_length, 1, MYF(MY_WME));
+ error |= chk_data_link(&param, file, param.testflag & T_EXTEND);
+ end_io_cache(&(param.read_cache));
+ }
+ }
+ }
+ if (!error)
+ {
+ if (share->state.changed)
+ {
+ file->update|=HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
+ pthread_mutex_lock(&share->intern_lock);
+#ifndef HAVE_PREAD
+ pthread_mutex_lock(&THR_LOCK_keycache); // QQ; Has to be removed!
+#endif
+ share->state.changed=0;
+ if (!(table->db_stat & HA_READ_ONLY))
+ error=update_state_info(&param,file,UPDATE_TIME | UPDATE_OPEN_COUNT);
+#ifndef HAVE_PREAD
+ pthread_mutex_unlock(&THR_LOCK_keycache);// QQ; Has to be removed!
+#endif
+ pthread_mutex_unlock(&share->intern_lock);
+ }
+ }
+ else if (!mi_is_crashed(file))
+ {
+ mi_mark_crashed(file);
+ file->update |= HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
+ }
+
+ return error ? HA_CHECK_CORRUPT : HA_CHECK_OK;
+}
+
+
+/*
+ analyze the key distribution in the table
+ As the table may be only locked for read, we have to take into account that
+ two threads may do an analyze at the same time!
+*/
+
+int ha_myisam::analyze(THD *thd)
+{
+ int error;
+ MI_CHECK param;
+ MYISAM_SHARE* share = file->s;
+
+ myisamchk_init(&param);
+ param.thd = thd;
+ param.op_name = (char*)" analyze";
+ param.table_name = table->table_name;
+ param.testflag=(T_FAST | T_CHECK | T_SILENT | T_STATISTICS |
+ T_DONT_CHECK_CHECKSUM);
+ param.using_global_keycache = 1;
+
+ error = chk_key(&param, file);
+ if (!error)
+ {
+ pthread_mutex_lock(&share->intern_lock);
+#ifndef HAVE_PREAD
+ pthread_mutex_lock(&THR_LOCK_keycache); // QQ; Has to be removed!
+#endif
+ error=update_state_info(&param,file,UPDATE_STAT);
+#ifndef HAVE_PREAD
+ pthread_mutex_unlock(&THR_LOCK_keycache);// QQ; Has to be removed!
+#endif
+ pthread_mutex_unlock(&share->intern_lock);
+ }
+ else if (!mi_is_crashed(file))
+ mi_mark_crashed(file);
+ return error ? HA_CHECK_CORRUPT : HA_CHECK_OK;
+}
+
+
+int ha_myisam::repair(THD* thd, HA_CHECK_OPT* check_opt)
+{
+ if (!file) return HA_CHECK_INTERNAL_ERROR;
+ int error ;
+ MI_CHECK param;
+ MYISAM_SHARE* share = file->s;
+ char fixed_name[FN_REFLEN];
+
+ myisamchk_init(&param);
+ param.thd = thd;
+ param.op_name = (char*) "repair";
+ param.table_name = table->table_name;
+ param.testflag = check_opt->flags | T_SILENT|T_FORCE_CREATE|T_REP_BY_SORT;
+ param.sort_buffer_length= check_opt->sort_buffer_size;
+ if (check_opt->quick)
+ param.opt_rep_quick++;
+ param.tmpfile_createflag = O_RDWR | O_TRUNC;
+ param.using_global_keycache = 1;
+
+ VOID(fn_format(fixed_name,file->filename,"",MI_NAME_IEXT,
+ 4+ (param.opt_follow_links ? 16 : 0)));
+
+ if (share->state.key_map)
+ error = mi_repair_by_sort(&param, file, fixed_name, param.opt_rep_quick);
+ else
+ error= mi_repair(&param, file, fixed_name, param.opt_rep_quick);
+ if (!error)
+ {
+ if (share->state.changed)
+ {
+ share->state.changed = 0;
+ file->update|=HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
+ }
+ file->save_state=file->s->state.state;
+ if (file->s->base.auto_key)
+ update_auto_increment_key(&param, file, 1);
+ error = update_state_info(&param, file, UPDATE_TIME|UPDATE_STAT);
+ }
+ else if (!mi_is_crashed(file))
+ {
+ mi_mark_crashed(file);
+ file->update |= HA_STATE_CHANGED | HA_STATE_ROW_CHANGED;
+ }
+ if (!error)
+ {
+ if (param.out_flag & (O_NEW_DATA | O_NEW_INDEX))
+ {
+ /*
+ We have to close all instances of this file to ensure that we can
+ do the rename safely and that all threads are using the new version.
+ */
+ VOID(pthread_mutex_lock(&LOCK_open));
+ if (close_cached_table(thd,table))
+ error=1;
+
+ if (param.out_flag & O_NEW_DATA)
+ error|=change_to_newfile(fixed_name,MI_NAME_DEXT,
+ DATA_TMP_EXT, 0);
+
+ if (param.out_flag & O_NEW_INDEX)
+ error|=change_to_newfile(fixed_name,MI_NAME_IEXT,
+ INDEX_TMP_EXT,0);
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ }
+ }
+ return error ? HA_REPAIR_FAILED : HA_REPAIR_OK;
+}
+
+
+
+int ha_myisam::update_row(const byte * old_data, byte * new_data)
+{
+ statistic_increment(ha_update_count,&LOCK_status);
+ if (table->time_stamp)
+ update_timestamp(new_data+table->time_stamp-1);
+ return mi_update(file,old_data,new_data);
+}
+
+int ha_myisam::delete_row(const byte * buf)
+{
+ statistic_increment(ha_delete_count,&LOCK_status);
+ return mi_delete(file,buf);
+}
+
+int ha_myisam::index_read(byte * buf, const byte * key,
+ uint key_len, enum ha_rkey_function find_flag)
+{
+ statistic_increment(ha_read_key_count,&LOCK_status);
+ int error=mi_rkey(file,buf,active_index, key, key_len, find_flag);
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+int ha_myisam::index_read_idx(byte * buf, uint index, const byte * key,
+ uint key_len, enum ha_rkey_function find_flag)
+{
+ statistic_increment(ha_read_key_count,&LOCK_status);
+ int error=mi_rkey(file,buf,index, key, key_len, find_flag);
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+int ha_myisam::index_next(byte * buf)
+{
+ statistic_increment(ha_read_next_count,&LOCK_status);
+ int error=mi_rnext(file,buf,active_index);
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+int ha_myisam::index_prev(byte * buf)
+{
+ statistic_increment(ha_read_prev_count,&LOCK_status);
+ int error=mi_rprev(file,buf, active_index);
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+int ha_myisam::index_first(byte * buf)
+{
+ statistic_increment(ha_read_first_count,&LOCK_status);
+ int error=mi_rfirst(file, buf, active_index);
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+int ha_myisam::index_last(byte * buf)
+{
+ statistic_increment(ha_read_last_count,&LOCK_status);
+ int error=mi_rlast(file, buf, active_index);
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+int ha_myisam::index_next_same(byte * buf,
+ const byte *key __attribute__((unused)),
+ uint length __attribute__((unused)))
+{
+ statistic_increment(ha_read_next_count,&LOCK_status);
+ int error=mi_rnext_same(file,buf);
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+
+int ha_myisam::rnd_init(bool scan)
+{
+ if (scan)
+ return mi_scan_init(file);
+ else
+ return mi_extra(file,HA_EXTRA_RESET);
+}
+
+int ha_myisam::rnd_next(byte *buf)
+{
+ statistic_increment(ha_read_rnd_next_count,&LOCK_status);
+ int error=mi_scan(file, buf);
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+int ha_myisam::restart_rnd_next(byte *buf, byte *pos)
+{
+ return rnd_pos(buf,pos);
+}
+
+int ha_myisam::rnd_pos(byte * buf, byte *pos)
+{
+ statistic_increment(ha_read_rnd_count,&LOCK_status);
+ int error=mi_rrnd(file, buf, ha_get_ptr(pos,ref_length));
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+void ha_myisam::position(const byte* record)
+{
+ my_off_t position=mi_position(file);
+ ha_store_ptr(ref, ref_length, position);
+}
+
+void ha_myisam::info(uint flag)
+{
+ MI_ISAMINFO info;
+ (void) mi_status(file,&info,flag);
+ if (flag & HA_STATUS_VARIABLE)
+ {
+ records = info.records;
+ deleted = info.deleted;
+ data_file_length=info.data_file_length;
+ index_file_length=info.index_file_length;
+ delete_length = info.delete_length;
+ check_time = info.check_time;
+ mean_rec_length=info.mean_reclength;
+ }
+ if (flag & HA_STATUS_CONST)
+ {
+ max_data_file_length=info.max_data_file_length;
+ max_index_file_length=info.max_index_file_length;
+ create_time = info.create_time;
+ sortkey = info.sortkey;
+ ref_length=info.reflength;
+ table->db_options_in_use = info.options;
+ block_size=myisam_block_size;
+ table->keys_in_use &= 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);
+ raid_type=info.raid_type;
+ raid_chunks=info.raid_chunks;
+ raid_chunksize=info.raid_chunksize;
+ }
+ if (flag & HA_STATUS_ERRKEY)
+ {
+ errkey = info.errkey;
+ ha_store_ptr(dupp_ref, ref_length, info.dupp_key_pos);
+ }
+ if (flag & HA_STATUS_TIME)
+ update_time = info.update_time;
+ if (flag & HA_STATUS_AUTO)
+ auto_increment_value= info.auto_increment;
+}
+
+
+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))
+ return 0;
+ return mi_extra(file,operation);
+}
+
+int ha_myisam::reset(void)
+{
+ return mi_extra(file,HA_EXTRA_RESET);
+}
+
+int ha_myisam::delete_all_rows()
+{
+ return mi_delete_all_rows(file);
+}
+
+int ha_myisam::delete_table(const char *name)
+{
+ return mi_delete_table(name);
+}
+
+int ha_myisam::external_lock(THD *thd, int lock_type)
+{
+ return mi_lock_database(file,lock_type);
+}
+
+
+THR_LOCK_DATA **ha_myisam::store_lock(THD *thd,
+ THR_LOCK_DATA **to,
+ enum thr_lock_type lock_type)
+{
+ if (lock_type != TL_IGNORE && file->lock.type == TL_UNLOCK)
+ file->lock.type=lock_type;
+ *to++= &file->lock;
+ return to;
+}
+
+void ha_myisam::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;
+ }
+ if (!(create_info->used_fields & HA_CREATE_USED_RAID))
+ {
+ create_info->raid_type= raid_type;
+ create_info->raid_chunks= raid_chunks;
+ create_info->raid_chunksize= raid_chunksize;
+ }
+}
+
+
+int ha_myisam::create(const char *name, register TABLE *form,
+ HA_CREATE_INFO *info)
+{
+ int error;
+ uint i,j,recpos,minpos,fieldpos,temp_length,length;
+ bool found_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;
+ 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),
+ &keyseg,
+ ((form->key_parts + form->keys) * sizeof(MI_KEYSEG)),
+ 0)))
+ DBUG_RETURN(1);
+
+ pos=form->key_info;
+ for (i=0; i < form->keys ; i++, pos++)
+ {
+ keydef[i].flag= (pos->flags & (HA_NOSAME | HA_FULLTEXT));
+ keydef[i].seg=keyseg;
+ keydef[i].keysegs=pos->key_parts;
+ for (j=0 ; j < pos->key_parts ; j++)
+ {
+ keydef[i].seg[j].flag=pos->key_part[j].key_part_flag;
+ Field *field=pos->key_part[j].field;
+ type=field->key_type();
+
+ if (options & HA_OPTION_PACK_KEYS ||
+ (pos->flags & (HA_PACK_KEY | HA_BINARY_PACK_KEY |
+ HA_SPACE_PACK_USED)))
+ {
+ if (pos->key_part[j].length > 8 &&
+ (type == HA_KEYTYPE_TEXT ||
+ type == HA_KEYTYPE_NUM ||
+ (type == HA_KEYTYPE_BINARY && !field->zero_pack())))
+ {
+ /* No blobs here */
+ if (j == 0)
+ keydef[i].flag|=HA_PACK_KEY;
+ if (!(field->flags & ZEROFILL_FLAG) &&
+ (field->type() == FIELD_TYPE_STRING ||
+ field->type() == FIELD_TYPE_VAR_STRING ||
+ ((int) (pos->key_part[j].length - field->decimals()))
+ >= 4))
+ keydef[i].seg[j].flag|=HA_SPACE_PACK;
+ }
+ else if (j == 0 && (!(pos->flags & HA_NOSAME) || pos->key_length > 16))
+ keydef[i].flag|= HA_BINARY_PACK_KEY;
+ }
+ keydef[i].seg[j].type= (int) type;
+ keydef[i].seg[j].start= pos->key_part[j].offset;
+ keydef[i].seg[j].length= pos->key_part[j].length;
+ keydef[i].seg[j].bit_start=keydef[i].seg[j].bit_end=0;
+ keydef[i].seg[j].language=MY_CHARSET_CURRENT;
+
+ if (field->null_ptr)
+ {
+ keydef[i].seg[j].null_bit=field->null_bit;
+ keydef[i].seg[j].null_pos= (uint) (field->null_ptr-
+ (uchar*) form->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)
+ {
+ keydef[i].flag|=HA_AUTO_KEY;
+ found_auto_increment=1;
+ }
+ 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);
+ }
+ }
+ keyseg+=pos->key_parts;
+ }
+
+ recpos=0; recinfo_pos=recinfo;
+ while (recpos < (uint) form->reclength)
+ {
+ Field **field,*found=0;
+ minpos=form->reclength; length=0;
+
+ for (field=form->field ; *field ; field++)
+ {
+ if ((fieldpos=(*field)->offset()) >= recpos &&
+ fieldpos <= minpos)
+ {
+ /* skip null fields */
+ if (!(temp_length= (*field)->pack_length()))
+ continue; /* Skipp null-fields */
+ if (! found || fieldpos < minpos ||
+ (fieldpos == minpos && temp_length < length))
+ {
+ minpos=fieldpos; found= *field; length=temp_length;
+ }
+ }
+ }
+ DBUG_PRINT("loop",("found: %lx recpos: %d minpos: %d length: %d",
+ found,recpos,minpos,length));
+ if (recpos != minpos)
+ { // Reserved space (Null bits?)
+ bzero((char*) recinfo_pos,sizeof(*recinfo_pos));
+ recinfo_pos->type=(int) FIELD_NORMAL;
+ recinfo_pos++->length= (uint16) (minpos-recpos);
+ }
+ if (! found)
+ break;
+
+ if (found->flags & BLOB_FLAG)
+ {
+ recinfo_pos->type= (int) FIELD_BLOB;
+ }
+ 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;
+ 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);
+ if (found->null_ptr)
+ {
+ recinfo_pos->null_bit=found->null_bit;
+ recinfo_pos->null_pos= (uint) (found->null_ptr-
+ (uchar*) form->record[0]);
+ }
+ else
+ {
+ recinfo_pos->null_bit=0;
+ recinfo_pos->null_pos=0;
+ }
+ (recinfo_pos++) ->length=(uint16) length;
+ recpos=minpos+length;
+ DBUG_PRINT("loop",("length: %d type: %d",
+ recinfo_pos[-1].length,recinfo_pos[-1].type));
+
+ }
+ 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.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.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,
+ (uint) (recinfo_pos-recinfo), recinfo,
+ 0, (MI_UNIQUEDEF*) 0,
+ &create_info,
+ (((options & HA_OPTION_PACK_RECORD) ? HA_PACK_RECORD : 0) |
+ ((options & HA_OPTION_CHECKSUM) ? HA_CREATE_CHECKSUM : 0) |
+ ((options & HA_OPTION_DELAY_KEY_WRITE) ?
+ HA_CREATE_DELAY_KEY_WRITE : 0)));
+
+
+ my_free((gptr) recinfo,MYF(0));
+ DBUG_RETURN(error);
+}
+
+
+int ha_myisam::rename_table(const char * from, const char * to)
+{
+ return mi_rename(from,to);
+}
+
+
+longlong ha_myisam::get_auto_increment()
+{
+ if (!table->next_number_key_offset)
+ { // Autoincrement at key-start
+ ha_myisam::info(HA_STATUS_AUTO);
+ return auto_increment_value;
+ }
+
+ longlong nr;
+ int error;
+ byte key[MAX_KEY_LENGTH];
+ (void) extra(HA_EXTRA_KEYREAD);
+ key_copy(key,table,table->next_number_index,
+ table->next_number_key_offset);
+ error=mi_rkey(file,table->record[1],(int) table->next_number_index,
+ 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;
+ extra(HA_EXTRA_NO_KEYREAD);
+ return nr;
+}
+
+
+ha_rows ha_myisam::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) mi_records_in_range(file,
+ inx,
+ start_key,start_key_len,
+ start_search_flag,
+ end_key,end_key_len,
+ end_search_flag);
+}
+
+int ha_myisam::ft_init(uint inx, const byte *key, uint keylen, bool presort)
+{
+ if (ft_handler)
+ return -1;
+
+ // Do the search!
+ ft_handler=ft_init_search(file,inx,(byte*) key,keylen,presort);
+
+ if (!ft_handler)
+ return (my_errno ? my_errno : -1);
+
+ return 0;
+}
+
+int ha_myisam::ft_read(byte * buf)
+{
+ int error;
+
+ thread_safe_increment(ha_read_next_count,&LOCK_status); // why ?
+ if (ft_handler)
+ ft_relevance=ft_read_next((FT_DOCLIST *) ft_handler,(char*) buf);
+ else
+ ft_relevance=0;
+
+ error=((ft_relevance == 0) ? HA_ERR_END_OF_FILE :
+ (ft_relevance > 0) ? 0 :
+ (int) -ft_relevance);
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
diff --git a/sql/ha_myisam.h b/sql/ha_myisam.h
new file mode 100644
index 00000000000..a25711d720b
--- /dev/null
+++ b/sql/ha_myisam.h
@@ -0,0 +1,96 @@
+/* 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 __GNUC__
+#pragma interface /* gcc class implementation */
+#endif
+
+/* class for the the myisam handler */
+
+#include <myisam.h>
+#include <ft_global.h>
+
+extern ulong myisam_sort_buffer_size;
+
+class ha_myisam: public handler
+{
+ MI_INFO *file;
+ uint int_option_flag;
+
+ 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)
+ {}
+ ~ha_myisam() { ft_close(); }
+ const char *table_type() const { return "MyISAM"; }
+ const char **bas_ext() const;
+ ulong option_flag() const { return int_option_flag; }
+ uint max_record_length() const { return HA_MAX_REC_LENGTH; }
+ uint max_keys() const { return 1; }
+ uint max_key_parts() const { return MAX_REF_PARTS; }
+ uint max_key_length() const { return MAX_KEY_LENGTH; }
+
+ int open(const char *name, int mode, int test_if_locked);
+ int close(void);
+ int write_row(byte * buf);
+ int update_row(const byte * old_data, byte * new_data);
+ int delete_row(const byte * buf);
+ int index_read(byte * buf, const byte * key,
+ 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_next(byte * buf);
+ int index_prev(byte * buf);
+ int index_first(byte * buf);
+ int index_last(byte * buf);
+ int index_next_same(byte *buf, const byte *key, uint keylen);
+ int ft_init(uint inx,const byte *key, uint keylen, bool presort);
+ int ft_read(byte *buf);
+ int ft_close() { if(ft_handler) ft_close_search(ft_handler); ft_handler=0; return 0;}
+ int rnd_init(bool scan=1);
+ int rnd_next(byte *buf);
+ int rnd_pos(byte * buf, byte *pos);
+ int restart_rnd_next(byte *buf, byte *pos);
+ void position(const byte *record);
+ my_off_t row_position() { return mi_position(file); }
+ void info(uint);
+ int extra(enum ha_extra_function operation);
+ int reset(void);
+ int external_lock(THD *thd, int lock_type);
+ int delete_all_rows(void);
+ 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, TABLE *form, HA_CREATE_INFO *create_info);
+ THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to,
+ enum thr_lock_type lock_type);
+ longlong get_auto_increment();
+ int rename_table(const char * from, const char * to);
+ int delete_table(const char *name);
+ int check(THD* thd, HA_CHECK_OPT* check_opt);
+ int analyze(THD* thd);
+ int repair(THD* thd, HA_CHECK_OPT* check_opt);
+ int dump(THD* thd, int fd);
+ int net_read_dump(NET* net);
+};
diff --git a/sql/ha_myisammrg.cc b/sql/ha_myisammrg.cc
new file mode 100644
index 00000000000..f43dd901e1f
--- /dev/null
+++ b/sql/ha_myisammrg.cc
@@ -0,0 +1,209 @@
+/* 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 __GNUC__
+#pragma implementation // gcc: Class implementation
+#endif
+
+#include "mysql_priv.h"
+#include <m_ctype.h>
+#include "ha_myisammrg.h"
+#ifndef MASTER
+#include "../srclib/myisammrg/mymrgdef.h"
+#else
+#include "../myisammrg/mymrgdef.h"
+#endif
+
+/*****************************************************************************
+** MyISAM MERGE tables
+*****************************************************************************/
+
+const char **ha_myisammrg::bas_ext() const
+{ static const char *ext[]= { ".MRG", NullS }; return ext; }
+
+int ha_myisammrg::open(const char *name, int mode, int test_if_locked)
+{
+ char name_buff[FN_REFLEN];
+ if (!(file=myrg_open(fn_format(name_buff,name,"","",2 | 4), mode,
+ test_if_locked)))
+ return (my_errno ? my_errno : -1);
+
+ 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);
+ 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);
+ if (table->reclength != 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;
+ }
+ return (0);
+}
+
+int ha_myisammrg::close(void)
+{
+ return myrg_close(file);
+}
+
+int ha_myisammrg::write_row(byte * buf)
+{
+ return (my_errno=HA_ERR_WRONG_COMMAND);
+}
+
+int ha_myisammrg::update_row(const byte * old_data, byte * new_data)
+{
+ statistic_increment(ha_update_count,&LOCK_status);
+ if (table->time_stamp)
+ update_timestamp(new_data+table->time_stamp-1);
+ return myrg_update(file,old_data,new_data);
+}
+
+int ha_myisammrg::delete_row(const byte * buf)
+{
+ statistic_increment(ha_delete_count,&LOCK_status);
+ return myrg_delete(file,buf);
+}
+
+int ha_myisammrg::index_read(byte * buf, const byte * key,
+ uint key_len, enum ha_rkey_function find_flag)
+{
+ return (my_errno=HA_ERR_WRONG_COMMAND);
+}
+
+int ha_myisammrg::index_read_idx(byte * buf, uint index, const byte * key,
+ uint key_len, enum ha_rkey_function find_flag)
+{
+ return (my_errno=HA_ERR_WRONG_COMMAND);
+}
+
+int ha_myisammrg::index_next(byte * buf)
+{
+ return (my_errno=HA_ERR_WRONG_COMMAND);
+}
+
+int ha_myisammrg::index_prev(byte * buf)
+{
+ return (my_errno=HA_ERR_WRONG_COMMAND);
+}
+
+int ha_myisammrg::index_first(byte * buf)
+{
+ return (my_errno=HA_ERR_WRONG_COMMAND);
+}
+
+int ha_myisammrg::index_last(byte * buf)
+{
+ return (my_errno=HA_ERR_WRONG_COMMAND);
+}
+
+int ha_myisammrg::rnd_init(bool scan)
+{
+ return myrg_extra(file,HA_EXTRA_RESET);
+}
+
+int ha_myisammrg::rnd_next(byte *buf)
+{
+ statistic_increment(ha_read_rnd_next_count,&LOCK_status);
+ int error=myrg_rrnd(file, buf, HA_OFFSET_ERROR);
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+int ha_myisammrg::rnd_pos(byte * buf, byte *pos)
+{
+ statistic_increment(ha_read_rnd_count,&LOCK_status);
+ int error=myrg_rrnd(file, buf, ha_get_ptr(pos,ref_length));
+ table->status=error ? STATUS_NOT_FOUND: 0;
+ return error;
+}
+
+void ha_myisammrg::position(const byte *record)
+{
+ ulonglong position= myrg_position(file);
+ ha_store_ptr(ref, ref_length, (my_off_t) position);
+}
+
+
+void ha_myisammrg::info(uint flag)
+{
+ MYMERGE_INFO info;
+ (void) myrg_status(file,&info,flag);
+ records = (ha_rows) info.records;
+ deleted = (ha_rows) info.deleted;
+ data_file_length=info.data_file_length;
+ errkey = info.errkey;
+ table->keys_in_use=0; // No keys yet
+ table->db_options_in_use = info.options;
+ mean_rec_length=info.reclength;
+ block_size=0;
+ update_time=0;
+#if SIZEOF_OFF_T > 4
+ ref_length=6; // Should be big enough
+#else
+ ref_length=4;
+#endif
+}
+
+
+int ha_myisammrg::extra(enum ha_extra_function operation)
+{
+ return myrg_extra(file,operation);
+}
+
+int ha_myisammrg::reset(void)
+{
+ return myrg_extra(file,HA_EXTRA_RESET);
+}
+
+int ha_myisammrg::external_lock(THD *thd, int lock_type)
+{
+ return myrg_lock_database(file,lock_type);
+}
+
+uint ha_myisammrg::lock_count(void) const
+{
+ return file->tables;
+}
+
+
+THR_LOCK_DATA **ha_myisammrg::store_lock(THD *thd,
+ THR_LOCK_DATA **to,
+ enum thr_lock_type lock_type)
+{
+ MYRG_TABLE *table;
+
+ for (table=file->open_tables ; table != file->end_table ; table++)
+ {
+ *(to++)= &table->table->lock;
+ if (lock_type != TL_IGNORE && table->table->lock.type == TL_UNLOCK)
+ table->table->lock.type=lock_type;
+ }
+ return to;
+}
+
+
+int ha_myisammrg::create(const char *name, register TABLE *form,
+ HA_CREATE_INFO *create_info)
+{
+ char buff[FN_REFLEN];
+ return myrg_create(fn_format(buff,name,"","",2+4+16),0);
+}
diff --git a/sql/ha_myisammrg.h b/sql/ha_myisammrg.h
new file mode 100644
index 00000000000..cb1feb52989
--- /dev/null
+++ b/sql/ha_myisammrg.h
@@ -0,0 +1,67 @@
+/* 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 __GNUC__
+#pragma interface /* gcc class implementation */
+#endif
+
+/* class for the the myisam merge handler */
+
+#include <myisammrg.h>
+
+class ha_myisammrg: public handler
+{
+ MYRG_INFO *file;
+
+ public:
+ ha_myisammrg(TABLE *table): handler(table), file(0) {}
+ ~ha_myisammrg() {}
+ const char *table_type() const { return "MRG_MyISAM"; }
+ const char **bas_ext() const;
+ ulong option_flag() const { return HA_READ_RND_SAME+HA_KEYPOS_TO_RNDPOS+HA_REC_NOT_IN_SEQ;}
+ uint max_record_length() const { return HA_MAX_REC_LENGTH; }
+ uint max_keys() const { return 0; }
+ uint max_key_parts() const { return 0; }
+ uint max_key_length() const { return 0; }
+
+ int open(const char *name, int mode, int test_if_locked);
+ int close(void);
+ int write_row(byte * buf);
+ int update_row(const byte * old_data, byte * new_data);
+ int delete_row(const byte * buf);
+ int index_read(byte * buf, const byte * key,
+ 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_next(byte * buf);
+ int index_prev(byte * buf);
+ int index_first(byte * buf);
+ int index_last(byte * buf);
+ int rnd_init(bool scan=1);
+ int rnd_next(byte *buf);
+ int rnd_pos(byte * buf, byte *pos);
+ void position(const byte *record);
+ my_off_t row_position() { return myrg_position(file); }
+ void info(uint);
+ int extra(enum ha_extra_function operation);
+ int reset(void);
+ int external_lock(THD *thd, int lock_type);
+ uint lock_count(void) const;
+ 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/handler.cc b/sql/handler.cc
new file mode 100644
index 00000000000..817e3fa79dc
--- /dev/null
+++ b/sql/handler.cc
@@ -0,0 +1,614 @@
+/* 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-calling-functions */
+
+#ifdef __GNUC__
+#pragma implementation // gcc: Class implementation
+#endif
+
+#include "mysql_priv.h"
+#include "ha_heap.h"
+#include "ha_myisam.h"
+#include "ha_myisammrg.h"
+#ifndef NO_ISAM
+#include "ha_isam.h"
+#include "ha_isammrg.h"
+#endif
+#ifdef HAVE_BERKELEY_DB
+#include "ha_berkeley.h"
+#endif
+#include <myisampack.h>
+#include <errno.h>
+
+ /* static functions defined in this file */
+
+static void NEAR_F set_form_timestamp(TABLE *table, byte *record);
+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_read_rnd_count, ha_read_rnd_next_count;
+
+const char *ha_table_type[] = {
+ "", "DIAB_ISAM","HASH","MISAM","PISAM","RMS_ISAM","HEAP", "ISAM",
+ "MRG_ISAM","MYISAM", "MRG_MYISAM", "BERKELEY_DB?", "?", "?",NullS
+};
+
+const char *ha_row_type[] = {
+ "", "FIXED", "DYNAMIC", "COMPRESSED","?","?","?"
+};
+
+TYPELIB ha_table_typelib= {array_elements(ha_table_type)-4,"",
+ ha_table_type+1};
+
+
+ /* Use other database handler if databasehandler is not incompiled */
+
+enum db_type ha_checktype(enum db_type database_type)
+{
+ switch (database_type) {
+#ifdef HAVE_BERKELEY_DB
+ case DB_TYPE_BERKELEY_DB:
+ return(berkeley_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
+ case DB_TYPE_ISAM:
+#endif
+ case DB_TYPE_HEAP:
+ case DB_TYPE_MYISAM:
+ case DB_TYPE_MRG_MYISAM:
+ return (database_type); /* Database exists on system */
+ default:
+ break;
+ }
+ return(DB_TYPE_MYISAM); /* Use this as default */
+} /* ha_checktype */
+
+
+handler *get_new_handler(TABLE *table, enum db_type db_type)
+{
+ switch (db_type) {
+#ifndef NO_HASH
+ return new ha_hash(table);
+#endif
+#ifndef NO_MERGE
+ case DB_TYPE_MRG_ISAM:
+ return new ha_isammrg(table);
+#endif
+#ifndef NO_ISAM
+ case DB_TYPE_ISAM:
+ return new ha_isam(table);
+#endif
+#ifdef HAVE_BERKELEY_DB
+ case DB_TYPE_BERKELEY_DB:
+ return new ha_berkeley(table);
+#endif
+ case DB_TYPE_HEAP:
+ return new ha_heap(table);
+ case DB_TYPE_MYISAM:
+ default: // should never happen
+ return new ha_myisam(table);
+ case DB_TYPE_MRG_MYISAM:
+ return new ha_myisammrg(table);
+ }
+}
+
+int ha_init()
+{
+#ifdef HAVE_BERKELEY_DB
+ if (!berkeley_skip)
+ {
+ int error;
+ if ((error=berkeley_init()))
+ return error;
+ }
+#endif
+ return 0;
+}
+
+ /* close, flush or restart databases */
+ /* Ignore this for other databases than ours */
+
+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);
+ error|=nisam_panic(flag);
+ error|=mi_panic(flag);
+ error|=myrg_panic(flag);
+#ifdef HAVE_BERKELEY_DB
+ if (!berkeley_skip)
+ error|=berkeley_end();
+#endif
+ return error;
+} /* ha_panic */
+
+
+int ha_autocommit_or_rollback(THD *thd, int error)
+{
+ DBUG_ENTER("ha_autocommit_or_rollback");
+#ifdef HAVE_BERKELEY_DB
+ if ((thd->options & OPTION_AUTO_COMMIT) && !thd->locked_tables)
+ {
+ if (!error)
+ {
+ if (ha_commit(thd))
+ error=1;
+ }
+ else
+ (void) ha_rollback(thd);
+ }
+#endif
+ DBUG_RETURN(error);
+}
+
+int ha_commit(THD *thd)
+{
+ int error=0;
+ DBUG_ENTER("commit");
+#ifdef HAVE_BERKELEY_DB
+ if (thd->transaction.bdb_tid)
+ {
+ int error=berkeley_commit(thd);
+ if (error)
+ {
+ my_error(ER_ERROR_DURING_COMMIT, MYF(0), error);
+ error=1;
+ }
+ }
+#endif
+ DBUG_RETURN(error);
+}
+
+int ha_rollback(THD *thd)
+{
+ int error=0;
+ DBUG_ENTER("commit");
+#ifdef HAVE_BERKELEY_DB
+ if (thd->transaction.bdb_tid)
+ {
+ int error=berkeley_rollback(thd);
+ if (error)
+ {
+ my_error(ER_ERROR_DURING_ROLLBACK, MYF(0), error);
+ error=1;
+ }
+ }
+#endif
+ DBUG_RETURN(error);
+}
+
+
+bool ha_flush_logs()
+{
+ bool result=0;
+#ifdef HAVE_BERKELEY_DB
+ if (!berkeley_skip && berkeley_flush_logs())
+ result=1;
+#endif
+ return result;
+}
+
+
+int ha_delete_table(enum db_type table_type, const char *path)
+{
+ handler *file=get_new_handler((TABLE*) 0, table_type);
+ if (!file)
+ return -1;
+ int error=file->delete_table(path);
+ delete file;
+ return error;
+}
+
+void ha_store_ptr(byte *buff, uint pack_length, my_off_t pos)
+{
+ switch (pack_length) {
+#if SIZEOF_OFF_T > 4
+ case 8: mi_int8store(buff,pos); break;
+ case 7: mi_int7store(buff,pos); break;
+ case 6: mi_int6store(buff,pos); break;
+ case 5: mi_int5store(buff,pos); break;
+#endif
+ case 4: mi_int4store(buff,pos); break;
+ case 3: mi_int3store(buff,pos); break;
+ case 2: mi_int2store(buff,(uint) pos); break;
+ case 1: buff[0]= (uchar) pos; break;
+ }
+ return;
+}
+
+my_off_t ha_get_ptr(byte *ptr, uint pack_length)
+{
+ my_off_t pos;
+ switch (pack_length) {
+#if SIZEOF_OFF_T > 4
+ case 8:
+ pos= (my_off_t) mi_uint8korr(ptr);
+ break;
+ case 7:
+ pos= (my_off_t) mi_uint7korr(ptr);
+ break;
+ case 6:
+ pos= (my_off_t) mi_uint6korr(ptr);
+ break;
+ case 5:
+ pos= (my_off_t) mi_uint5korr(ptr);
+ break;
+#endif
+ case 4:
+ pos= (my_off_t) mi_uint4korr(ptr);
+ break;
+ case 3:
+ pos= (my_off_t) mi_uint3korr(ptr);
+ break;
+ case 2:
+ pos= (my_off_t) mi_uint2korr(ptr);
+ break;
+ case 1:
+ pos= (my_off_t) mi_uint2korr(ptr);
+ break;
+ default:
+ pos=0; // Impossible
+ break;
+ }
+ return pos;
+}
+
+/****************************************************************************
+** General handler functions
+****************************************************************************/
+
+ /* Open database-handler. Try O_RDONLY if can't open as O_RDWR */
+ /* Don't wait for locks if not HA_OPEN_WAIT_IF_LOCKED is set */
+
+int handler::ha_open(const char *name, int mode, int test_if_locked)
+{
+ int error;
+ DBUG_ENTER("handler::open");
+ DBUG_PRINT("enter",("db_type: %d db_stat: %d mode: %d lock_test: %d",
+ table->db_type, table->db_stat, mode, test_if_locked));
+
+ if ((error=open(name,mode,test_if_locked)))
+ {
+ if ((error == EACCES || error == EROFS) && mode == O_RDWR &&
+ (table->db_stat & HA_TRY_READ_ONLY))
+ {
+ table->db_stat|=HA_READ_ONLY;
+ error=open(name,O_RDONLY,test_if_locked);
+ }
+ }
+ if (error)
+ {
+ my_errno=error; /* Safeguard */
+ DBUG_PRINT("error",("error: %d errno: %d",error,errno));
+ }
+ else
+ {
+ if (table->db_options_in_use & HA_OPTION_READ_ONLY_DATA)
+ table->db_stat|=HA_READ_ONLY;
+ }
+ if (!error)
+ {
+
+ if (!(ref=(byte*) my_malloc(ALIGN_SIZE(ref_length)*2,MYF(0))))
+ {
+ close();
+ error=HA_ERR_OUT_OF_MEM;
+ }
+ else
+ dupp_ref=ref+ALIGN_SIZE(ref_length);
+ }
+ DBUG_RETURN(error);
+}
+
+int handler::check(THD* thd, HA_CHECK_OPT* check_opt)
+{
+ return HA_CHECK_NOT_IMPLEMENTED;
+}
+
+int handler::repair(THD* thd, HA_CHECK_OPT* check_opt)
+{
+ return HA_REPAIR_NOT_IMPLEMENTED;
+}
+
+int handler::optimize(THD* thd)
+{
+ return HA_OPTIMIZE_NOT_IMPLEMENTED;
+}
+
+int handler::analyze(THD* thd)
+{
+ return HA_ANALYZE_NOT_IMPLEMENTED;
+}
+
+ /* Read first row from a table */
+
+int handler::rnd_first(byte * buf)
+{
+ register int error;
+ DBUG_ENTER("handler::rnd_first");
+
+ statistic_increment(ha_read_first_count,&LOCK_status);
+ (void) rnd_init();
+ while ((error= rnd_next(buf)) == HA_ERR_RECORD_DELETED) ;
+ (void) rnd_end();
+ DBUG_RETURN(error);
+}
+
+
+/*
+ The following function is only needed for tables that may be temporary tables
+ during joins
+*/
+
+int handler::restart_rnd_next(byte *buf, byte *pos)
+{
+ return HA_ERR_WRONG_COMMAND;
+}
+
+
+ /* Set a timestamp in record */
+
+void handler::update_timestamp(byte *record)
+{
+ long skr= (long) current_thd->query_start();
+#ifdef WORDS_BIGENDIAN
+ if (table->db_low_byte_first)
+ {
+ int4store(record,skr);
+ }
+ else
+#endif
+ longstore(record,skr);
+ return;
+}
+
+ /* 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()
+{
+ longlong nr;
+ THD *thd;
+ DBUG_ENTER("update_auto_increment");
+ if (table->next_number_field->val_int() != 0)
+ DBUG_VOID_RETURN;
+ thd=current_thd;
+ if ((nr=thd->next_insert_id))
+ thd->next_insert_id=0; // Clear after use
+ else
+ nr=get_auto_increment();
+ thd->insert_id((ulonglong) nr);
+ table->next_number_field->store(nr);
+ DBUG_VOID_RETURN;
+}
+
+
+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 (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();
+ return nr;
+}
+
+ /* Print error that we got from handler function */
+
+void handler::print_error(int error, myf errflag)
+{
+ DBUG_ENTER("print_error");
+ DBUG_PRINT("enter",("error: %d",error));
+
+ int textno=ER_GET_ERRNO;
+ switch (error) {
+ case EAGAIN:
+ textno=ER_FILE_USED;
+ break;
+ case ENOENT:
+ textno=ER_FILE_NOT_FOUND;
+ break;
+ case HA_ERR_KEY_NOT_FOUND:
+ case HA_ERR_NO_ACTIVE_RECORD:
+ case HA_ERR_END_OF_FILE:
+ textno=ER_KEY_NOT_FOUND;
+ break;
+ case HA_ERR_WRONG_TABLE_DEF:
+ textno=ER_WRONG_MRG_TABLE;
+ break;
+ case HA_ERR_FOUND_DUPP_KEY:
+ {
+ uint key_nr=get_dup_key(error);
+ if ((int) key_nr >= 0)
+ {
+ /* Write the dupplicated key in the error message */
+ char key[MAX_KEY_LENGTH];
+ String str(key,sizeof(key));
+ key_unpack(&str,table,(uint) key_nr);
+ uint max_length=MYSQL_ERRMSG_SIZE-strlen(ER(ER_DUP_ENTRY));
+ if (str.length() >= max_length)
+ {
+ str.length(max_length-4);
+ str.append("...");
+ }
+ my_error(ER_DUP_ENTRY,MYF(0),str.c_ptr(),key_nr+1);
+ DBUG_VOID_RETURN;
+ }
+ textno=ER_DUP_KEY;
+ break;
+ }
+ case HA_ERR_FOUND_DUPP_UNIQUE:
+ textno=ER_DUP_UNIQUE;
+ break;
+ case HA_ERR_RECORD_CHANGED:
+ textno=ER_CHECKREAD;
+ break;
+ case HA_ERR_CRASHED:
+ textno=ER_NOT_KEYFILE;
+ break;
+ case HA_ERR_OUT_OF_MEM:
+ my_error(ER_OUT_OF_RESOURCES,errflag);
+ DBUG_VOID_RETURN;
+ case HA_ERR_WRONG_COMMAND:
+ textno=ER_ILLEGAL_HA;
+ break;
+ case HA_ERR_OLD_FILE:
+ textno=ER_OLD_KEYFILE;
+ break;
+ case HA_ERR_UNSUPPORTED:
+ textno=ER_UNSUPPORTED_EXTENSION;
+ break;
+ case HA_ERR_RECORD_FILE_FULL:
+ textno=ER_RECORD_FILE_FULL;
+ break;
+ default:
+ {
+ my_error(ER_GET_ERRNO,errflag,error);
+ DBUG_VOID_RETURN;
+ }
+ }
+ my_error(textno,errflag,table->table_name,error);
+ DBUG_VOID_RETURN;
+}
+
+ /* Return key if error because of duplicated keys */
+
+uint handler::get_dup_key(int error)
+{
+ DBUG_ENTER("key_error");
+ table->file->errkey = (uint) -1;
+ if (error == HA_ERR_FOUND_DUPP_KEY || error == HA_ERR_FOUND_DUPP_UNIQUE)
+ info(HA_STATUS_ERRKEY | HA_STATUS_NO_LOCK);
+ DBUG_RETURN(table->file->errkey);
+}
+
+int handler::delete_table(const char *name)
+{
+ for (const char **ext=bas_ext(); *ext ; ext++)
+ {
+ if (delete_file(name,*ext,2))
+ return my_errno;
+ }
+ return 0;
+}
+
+
+int handler::rename_table(const char * from, const char * to)
+{
+ DBUG_ENTER("handler::rename_table");
+ for (const char **ext=bas_ext(); *ext ; ext++)
+ {
+ if (rename_file_ext(from,to,*ext))
+ DBUG_RETURN(my_errno);
+ }
+ DBUG_RETURN(0);
+}
+
+
+int handler::index_next_same(byte *buf, const byte *key, uint keylen)
+{
+ int error;
+ if (!(error=index_next(buf)))
+ {
+ if (key_cmp(table, key, active_index, keylen))
+ {
+ table->status=STATUS_NOT_FOUND;
+ error=HA_ERR_END_OF_FILE;
+ }
+ }
+ return error;
+}
+
+
+/*
+ The following is only needed if we would like to use the database
+ for internal temporary tables
+*/
+
+int handler::delete_all_rows()
+{
+ return (my_errno=HA_ERR_WRONG_COMMAND);
+}
+
+/****************************************************************************
+** Some general functions that isn't in the handler class
+****************************************************************************/
+
+ /* Initiates table-file and calls apropriate database-creator */
+ /* Returns 1 if something got wrong */
+
+int ha_create_table(const char *name, HA_CREATE_INFO *create_info,
+ bool update_create_info)
+
+{
+ int error;
+ TABLE table;
+ DBUG_ENTER("ha_create_table");
+
+ if (openfrm(name,"",0,(uint) READ_ALL,&table))
+ DBUG_RETURN(1);
+ if (update_create_info)
+ {
+ update_create_info_from_table(create_info, &table);
+ if (table.file->option_flag() & 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)
+ my_error(ER_CANT_CREATE_TABLE,MYF(ME_BELL+ME_WAITTANG),name,my_errno);
+ DBUG_RETURN(error != 0);
+}
+
+ /* Use key cacheing on all databases */
+
+void ha_key_cache(void)
+{
+ if (keybuff_size)
+ (void) init_key_cache(keybuff_size,0);
+} /* ha_key_cache */
+
+
+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)));
+}
diff --git a/sql/handler.h b/sql/handler.h
new file mode 100644
index 00000000000..a86b390e78b
--- /dev/null
+++ b/sql/handler.h
@@ -0,0 +1,307 @@
+/* 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 */
+
+
+/* Definitions for parameters to do with handler-routines */
+
+#ifdef __GNUC__
+#pragma interface /* gcc class implementation */
+#endif
+
+#ifndef NO_HASH
+#define NO_HASH /* Not yet implemented */
+#endif
+
+// the following is for checking tables
+
+#define HA_CHECK_OK 0
+#define HA_CHECK_NOT_IMPLEMENTED -1
+#define HA_CHECK_CORRUPT -2
+#define HA_CHECK_INTERNAL_ERROR -3
+
+#define HA_REPAIR_OK 0
+#define HA_REPAIR_NOT_IMPLEMENTED -1
+#define HA_REPAIR_FAILED -2
+#define HA_REPAIR_INTERNAL_ERROR -3
+
+#define HA_OPTIMIZE_OK 0
+#define HA_OPTIMIZE_NOT_IMPLEMENTED -1
+#define HA_OPTIMIZE_FAILED -2
+#define HA_OPTIMIZE_INTERNAL_ERROR -3
+
+#define HA_ANALYZE_OK 0
+#define HA_ANALYZE_NOT_IMPLEMENTED -1
+#define HA_ANALYZE_FAILED -2
+#define HA_ANALYZE_INTERNAL_ERROR -3
+
+/* 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
+ (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;
+ It returns a position to ha_r_rnd */
+#define HA_BINARY_KEYS 128 /* Keys must be exact */
+#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_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)
+#define HA_NOT_EXACT_COUNT (HA_REQUIRE_PRIMARY_KEY*2)
+#define HA_NO_WRITE_DELAYED (HA_NOT_EXACT_COUNT*2)
+#define HA_PRIMARY_KEY_IN_READ_INDEX (HA_NO_WRITE_DELAYED*2)
+#define HA_DROP_BEFORE_CREATE (HA_PRIMARY_KEY_IN_READ_INDEX*2)
+
+ /* Parameters for open() (in register form->filestat) */
+ /* HA_GET_INFO does a 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 */
+#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 */
+#define HA_READ_CHECK 123 /* Update with is recoverable */
+#define HA_CANT_DO_THAT 131 /* Databasehandler can't do it */
+
+ /* Some key definitions */
+#define HA_KEY_NULL_LENGTH 1
+#define HA_KEY_BLOB_LENGTH 2
+
+#define HA_LEX_CREATE_TMP_TABLE 1
+#define HA_LEX_CREATE_IF_NOT_EXISTS 2
+#define HA_OPTION_NO_CHECKSUM (1L << 17)
+#define HA_OPTION_NO_DELAY_KEY_WRITE (1L << 18)
+#define HA_MAX_REC_LENGTH 65535
+
+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_DEFAULT };
+
+enum row_type { 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
+
+typedef struct st_ha_create_information
+{
+ ulong table_options;
+ enum db_type db_type;
+ enum row_type row_type;
+ ulong avg_row_length;
+ ulonglong max_rows,min_rows;
+ ulonglong auto_increment_value;
+ char *comment,*password;
+ uint options; /* OR of HA_CREATE_ options */
+ uint raid_type,raid_chunks;
+ ulong raid_chunksize;
+ bool if_not_exists;
+ ulong used_fields;
+} HA_CREATE_INFO;
+
+
+/* The handler for a table type. Will be included in the TABLE structure */
+
+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;
+ inline void init()
+ {
+ flags= 0; quick= 0;
+ sort_buffer_size = myisam_sort_buffer_size;
+ }
+} HA_CHECK_OPT;
+
+class handler :public Sql_alloc
+{
+ protected:
+ struct st_table *table; /* The table definition */
+ uint active_index;
+
+public:
+ byte *ref; /* Pointer to current row */
+ byte *dupp_ref; /* Pointer to dupp row */
+ uint ref_length; /* Length of ref (1-8) */
+ uint block_size; /* index block size */
+ ha_rows records; /* Records i datafilen */
+ ha_rows deleted; /* Deleted records */
+ ulonglong data_file_length; /* Length off data file */
+ ulonglong max_data_file_length; /* Length off data file */
+ ulonglong index_file_length;
+ ulonglong max_index_file_length;
+ ulonglong delete_length; /* Free bytes */
+ ulonglong auto_increment_value;
+ uint raid_type,raid_chunks;
+ ulong raid_chunksize;
+ uint errkey; /* Last dup key */
+ uint sortkey, key_used_on_scan;
+ time_t create_time; /* When table was created */
+ time_t check_time;
+ time_t update_time;
+ ulong mean_rec_length; /* physical reclength */
+ double ft_relevance;
+ void *ft_handler;
+
+ handler(TABLE *table_arg) : table(table_arg),active_index(MAX_REF_PARTS),
+ ref(0),ref_length(sizeof(my_off_t)), block_size(0),records(0),deleted(0),
+ data_file_length(0), max_data_file_length(0), index_file_length(0),
+ delete_length(0), auto_increment_value(0), raid_type(0),
+ key_used_on_scan(MAX_KEY),
+ create_time(0), check_time(0), update_time(0), mean_rec_length(0),
+ ft_relevance(0.0), ft_handler(0)
+ {}
+ virtual ~handler(void) { my_free((char*) ref,MYF(MY_ALLOW_ZERO_PTR)); }
+ int ha_open(const char *name, int mode, int test_if_locked);
+ void update_timestamp(byte *record);
+ void update_auto_increment();
+ void print_error(int error, myf errflag);
+ uint get_dup_key(int error);
+ 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 bool fast_key_read() { return 0;}
+ virtual bool has_transactions(){ return 0;}
+
+ virtual int index_init(uint idx) { active_index=idx; return 0;}
+ virtual int index_end() {return 0; }
+ uint get_index(void) const { return active_index; }
+ virtual int open(const char *name, int mode, int test_if_locked)=0;
+ virtual void initialize(void) {}
+ virtual int close(void)=0;
+ virtual int write_row(byte * buf)=0;
+ 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;
+ 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;
+ virtual int index_prev(byte * buf)=0;
+ 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 ft_init(uint inx,const byte *key, uint keylen, bool presort)
+ { return -1; }
+ virtual int ft_read(byte *buf) { return -1; }
+ virtual int ft_close() { 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 restart_rnd_next(byte *buf, byte *pos);
+ virtual 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)
+ { return (ha_rows) 10; }
+ virtual void position(const byte *record)=0;
+ 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 reset()=0;
+ virtual int external_lock(THD *thd, int lock_type)=0;
+ virtual int delete_all_rows();
+ virtual longlong get_auto_increment();
+ virtual void update_create_info(HA_CREATE_INFO *create_info) {}
+ virtual int check(THD* thd, HA_CHECK_OPT* check_opt );
+ virtual int repair(THD* thd, HA_CHECK_OPT* check_opt);
+ virtual int optimize(THD* thd);
+ virtual int analyze(THD* thd);
+ virtual int dump(THD* thd, int fd = -1) { return ER_DUMP_NOT_IMPLEMENTED; }
+ // not implemented by default
+ virtual int net_read_dump(NET* net)
+ { return ER_DUMP_NOT_IMPLEMENTED; }
+
+ /* 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 uint max_record_length() const =0;
+ virtual uint max_keys() const =0;
+ virtual uint max_key_parts() const =0;
+ virtual uint max_key_length()const =0;
+ virtual uint min_record_length(uint options) const { return 1; }
+ virtual bool low_byte_first() const { return 1; }
+
+ virtual int rename_table(const char *from, const char *to);
+ virtual int delete_table(const char *name);
+ virtual int create(const char *name, TABLE *form, HA_CREATE_INFO *info)=0;
+ virtual uint lock_count(void) const { return 1; }
+ virtual THR_LOCK_DATA **store_lock(THD *thd,
+ THR_LOCK_DATA **to,
+ enum thr_lock_type lock_type)=0;
+};
+
+ /* Some extern variables used with handlers */
+
+extern const char *ha_row_type[];
+extern TYPELIB ha_table_typelib;
+
+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);
+int ha_init(void);
+int ha_panic(enum ha_panic_function flag);
+enum db_type ha_checktype(enum db_type database_type);
+int ha_create_table(const char *name, HA_CREATE_INFO *create_info,
+ bool update_create_info);
+int ha_delete_table(enum db_type db_type, const char *path);
+void ha_key_cache(void);
+int ha_commit(THD *thd);
+int ha_rollback(THD *thd);
+int ha_autocommit_or_rollback(THD *thd, int error);
+bool ha_flush_logs(void);
+
diff --git a/sql/hash_filo.cc b/sql/hash_filo.cc
new file mode 100644
index 00000000000..990d2d662d6
--- /dev/null
+++ b/sql/hash_filo.cc
@@ -0,0 +1,28 @@
+/* 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 */
+
+
+/*
+** A class for static sized hash tables where old entries are deleted according
+** to usage.
+*/
+
+#ifdef __GNUC__
+#pragma implementation // gcc: Class implementation
+#endif
+
+#include "mysql_priv.h"
+#include "hash_filo.h"
diff --git a/sql/hash_filo.h b/sql/hash_filo.h
new file mode 100644
index 00000000000..7d659f706a3
--- /dev/null
+++ b/sql/hash_filo.h
@@ -0,0 +1,131 @@
+/* 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 */
+
+
+/*
+** A class for static sized hash tables where old entries are deleted in
+** first-in-last-out to usage.
+*/
+
+#ifndef HASH_FILO_H
+#define HASH_FILO_H
+
+#ifdef __GNUC__
+#pragma interface /* gcc class implementation */
+#endif
+
+class hash_filo_element
+{
+ hash_filo_element *next_used,*prev_used;
+ public:
+ hash_filo_element() {}
+ friend class hash_filo;
+};
+
+
+class hash_filo
+{
+ const uint size, key_offset, key_length;
+ const hash_get_key get_key;
+ void (*free_element)(void*);
+ bool init;
+
+ hash_filo_element *first_link,*last_link;
+public:
+ pthread_mutex_t lock;
+ 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*))
+ :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)
+ {
+ bzero((char*) &cache,sizeof(cache));
+ }
+
+ ~hash_filo()
+ {
+ if (init)
+ {
+ if (cache.array.buffer) /* Avoid problems with thread library */
+ (void) hash_free(&cache);
+ pthread_mutex_destroy(&lock);
+ }
+ }
+ void clear(bool locked=0)
+ {
+ if (!init)
+ {
+ init=1;
+ (void) pthread_mutex_init(&lock,NULL);
+ }
+ if (!locked)
+ (void) pthread_mutex_lock(&lock);
+ (void) hash_free(&cache);
+ (void) hash_init(&cache,size,key_offset, key_length, get_key, free_element,
+ 0);
+ if (!locked)
+ (void) pthread_mutex_unlock(&lock);
+ first_link=last_link=0;
+ }
+
+ hash_filo_element *search(gptr key,uint length)
+ {
+ hash_filo_element *entry=(hash_filo_element*)
+ hash_search(&cache,(byte*) key,length);
+ if (entry)
+ { // Found; link it first
+ if (entry != first_link)
+ { // Relink used-chain
+ if (entry == last_link)
+ last_link=entry->prev_used;
+ else
+ {
+ entry->next_used->prev_used = entry->prev_used;
+ entry->prev_used->next_used = entry->next_used;
+ }
+ if ((entry->next_used= first_link))
+ first_link->prev_used=entry;
+ first_link=entry;
+ }
+ }
+ return entry;
+ }
+
+ my_bool add(hash_filo_element *entry)
+ {
+ if (cache.records == size)
+ {
+ hash_filo_element *tmp=last_link;
+ last_link=last_link->prev_used;
+ hash_delete(&cache,(byte*) tmp);
+ }
+ if (hash_insert(&cache,(byte*) entry))
+ {
+ if (free_element)
+ (*free_element)(entry); // This should never happen
+ return 1;
+ }
+ if ((entry->next_used=first_link))
+ first_link->prev_used=entry;
+ else
+ last_link=entry;
+ first_link=entry;
+ return 0;
+ }
+};
+
+#endif
diff --git a/sql/hostname.cc b/sql/hostname.cc
new file mode 100644
index 00000000000..0e8d6e36f0f
--- /dev/null
+++ b/sql/hostname.cc
@@ -0,0 +1,235 @@
+/* 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 */
+
+
+/*
+ Get hostname for an IP. Hostnames are checked with reverse name lookup and
+ checked that they doesn't resemble an ip.
+*/
+
+#include "mysql_priv.h"
+#include "hash_filo.h"
+#include <m_ctype.h>
+#ifdef __cplusplus
+extern "C" { // Because of SCO 3.2V4.2
+#endif
+#ifndef __WIN__
+#include <sys/resource.h>
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+#include <netdb.h>
+#include <sys/utsname.h>
+#endif // __WIN__
+#ifdef __cplusplus
+}
+#endif
+
+
+class host_entry :public hash_filo_element
+{
+public:
+ char ip[sizeof(((struct in_addr *) 0)->s_addr)];
+ uint errors;
+ char *hostname;
+};
+
+static hash_filo *hostname_cache;
+
+void hostname_cache_refresh()
+{
+ hostname_cache->clear();
+}
+
+bool hostname_cache_init()
+{
+ if (!(hostname_cache=new hash_filo(HOST_CACHE_SIZE,offsetof(host_entry,ip),
+ sizeof(struct in_addr),NULL,
+ (void (*)(void*)) free)))
+ return 1;
+ hostname_cache->clear();
+ return 0;
+}
+
+void hostname_cache_free()
+{
+ delete hostname_cache;
+}
+
+static void add_hostname(struct in_addr *in,const char *name)
+{
+ if (!(specialflag & SPECIAL_NO_HOST_CACHE))
+ {
+ VOID(pthread_mutex_lock(&hostname_cache->lock));
+ host_entry *entry;
+ if (!(entry=(host_entry*) hostname_cache->search((gptr) &in->s_addr,0)))
+ {
+ uint length=name ? strlen(name) : 0;
+
+ if ((entry=(host_entry*) malloc(sizeof(host_entry)+length+1)))
+ {
+ char *new_name= (char *) (entry+1);
+ memcpy_fixed(&entry->ip, &in->s_addr, sizeof(in->s_addr));
+ memcpy(new_name, name, length); // Should work even if name == NULL
+ new_name[length]=0; // End of string
+ entry->hostname=new_name;
+ entry->errors=0;
+ (void) hostname_cache->add(entry);
+ }
+ }
+ VOID(pthread_mutex_unlock(&hostname_cache->lock));
+ }
+}
+
+
+static inline void add_wrong_ip(struct in_addr *in)
+{
+ add_hostname(in,NullS);
+}
+
+void inc_host_errors(struct in_addr *in)
+{
+ VOID(pthread_mutex_lock(&hostname_cache->lock));
+ host_entry *entry;
+ if ((entry=(host_entry*) hostname_cache->search((gptr) &in->s_addr,0)))
+ entry->errors++;
+ VOID(pthread_mutex_unlock(&hostname_cache->lock));
+}
+
+void reset_host_errors(struct in_addr *in)
+{
+ VOID(pthread_mutex_lock(&hostname_cache->lock));
+ host_entry *entry;
+ if ((entry=(host_entry*) hostname_cache->search((gptr) &in->s_addr,0)))
+ entry->errors=0;
+ VOID(pthread_mutex_unlock(&hostname_cache->lock));
+}
+
+
+my_string ip_to_hostname(struct in_addr *in, uint *errors)
+{
+ host_entry *entry;
+ DBUG_ENTER("ip_to_hostname");
+
+ /* Check first if we have name in cache */
+ *errors=0;
+ if (!(specialflag & SPECIAL_NO_HOST_CACHE))
+ {
+ VOID(pthread_mutex_lock(&hostname_cache->lock));
+ if ((entry=(host_entry*) hostname_cache->search((gptr) &in->s_addr,0)))
+ {
+ char *name;
+ if (!entry->hostname)
+ name=0; // Don't allow connection
+ else
+ name=my_strdup(entry->hostname,MYF(0));
+ *errors= entry->errors;
+ VOID(pthread_mutex_unlock(&hostname_cache->lock));
+ DBUG_RETURN(name);
+ }
+ VOID(pthread_mutex_unlock(&hostname_cache->lock));
+ }
+
+ struct hostent *hp, *check;
+ char *name;
+ LINT_INIT(check);
+#if defined(HAVE_GETHOSTBYADDR_R) && defined(HAVE_SOLARIS_STYLE_GETHOST)
+ char buff[GETHOSTBYADDR_BUFF_SIZE],buff2[GETHOSTBYNAME_BUFF_SIZE];
+ int tmp_errno;
+ struct hostent tmp_hostent, tmp_hostent2;
+#ifdef HAVE_purify
+ bzero(buff,sizeof(buff)); // Bug in purify
+#endif
+ if (!(hp=gethostbyaddr_r((char*) in,sizeof(*in),
+ AF_INET,
+ &tmp_hostent,buff,sizeof(buff),&tmp_errno)))
+ {
+ DBUG_PRINT("error",("gethostbyaddr_r returned %d",tmp_errno));
+ return 0;
+ }
+ if (!(check=my_gethostbyname_r(hp->h_name,&tmp_hostent2,buff2,sizeof(buff2),
+ &tmp_errno)))
+ {
+ DBUG_PRINT("error",("gethostbyname_r returned %d",tmp_errno));
+ add_wrong_ip(in);
+ DBUG_RETURN(0);
+ }
+ if (!hp->h_name[0])
+ {
+ DBUG_PRINT("error",("Got an empty hostname"));
+ add_wrong_ip(in);
+ DBUG_RETURN(0); // Don't allow empty hostnames
+ }
+ if (!(name=my_strdup(hp->h_name,MYF(0))))
+ DBUG_RETURN(0); // out of memory
+
+#else
+ VOID(pthread_mutex_lock(&hostname_cache->lock));
+ if (!(hp=gethostbyaddr((char*) in,sizeof(*in), AF_INET)))
+ {
+ DBUG_PRINT("error",("gethostbyaddr returned %d",errno));
+ VOID(pthread_mutex_unlock(&hostname_cache->lock));
+ add_wrong_ip(in);
+ DBUG_RETURN(0);
+ }
+ if (!hp->h_name[0])
+ {
+ VOID(pthread_mutex_unlock(&hostname_cache->lock));
+ DBUG_PRINT("error",("Got an empty hostname"));
+ add_wrong_ip(in);
+ DBUG_RETURN(0); // Don't allow empty hostnames
+ }
+ if (!(name=my_strdup(hp->h_name,MYF(0))))
+ DBUG_RETURN(0); // out of memory
+ check=gethostbyname(name);
+ VOID(pthread_mutex_unlock(&hostname_cache->lock));
+ if (!check)
+ {
+ DBUG_PRINT("error",("gethostbyname returned %d",errno));
+ my_free(name,MYF(0));
+ DBUG_RETURN(0);
+ }
+#endif
+
+ /* Don't accept hostnames that starts with digits because they may be
+ false ip:s */
+ if (isdigit(name[0]))
+ {
+ char *pos;
+ for (pos= name+1 ; isdigit(*pos); pos++) ;
+ if (*pos == '.')
+ {
+ DBUG_PRINT("error",("mysqld doesn't accept hostnames that starts with a number followed by a '.'"));
+ my_free(name,MYF(0));
+ add_wrong_ip(in);
+ DBUG_RETURN(0);
+ }
+ }
+
+ /* Check that 'gethostbyname' returned the used ip */
+ for (uint i=0; check->h_addr_list[i]; i++)
+ {
+ if (*(uint32*)(check->h_addr_list)[i] == in->s_addr)
+ {
+ add_hostname(in,name);
+ DBUG_RETURN(name);
+ }
+ }
+ DBUG_PRINT("error",("Couldn't verify hostname with gethostbyname"));
+ my_free(name,MYF(0));
+ add_wrong_ip(in);
+ DBUG_RETURN(0);
+}
diff --git a/sql/init.cc b/sql/init.cc
new file mode 100644
index 00000000000..0103cd5038a
--- /dev/null
+++ b/sql/init.cc
@@ -0,0 +1,69 @@
+/* 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 */
+
+
+/* Init and dummy functions for interface with unireg */
+
+#include "mysql_priv.h"
+#include <m_ctype.h>
+
+void unireg_init(ulong options)
+{
+ uint i;
+ double nr;
+ DBUG_ENTER("unireg_init");
+
+ MYSYS_PROGRAM_DONT_USE_CURSES();
+ abort_loop=0;
+
+ my_disable_async_io=1; /* aioread is only in shared library */
+ wild_many='%'; wild_one='_'; wild_prefix='\\'; /* Change to sql syntax */
+
+ current_pid=(ulong) getpid(); /* Save for later ref */
+ init_time(); /* Init time-functions (read zone) */
+#ifdef USE_MY_ATOF
+ init_my_atof(); /* use our atof */
+#endif
+ my_abort_hook=unireg_abort; /* Abort with close of databases */
+ f_fyllchar=' '; /* Input fill char */
+ bfill(last_ref,MAX_REFLENGTH,(uchar) 255); /* This is indexfile-last-ref */
+
+ VOID(strmov(reg_ext,".frm"));
+ for (i=0 ; i < 6 ; i++) // YYMMDDHHMMSS
+ dayord.pos[i]=i;
+ specialflag=SPECIAL_SAME_DB_NAME;
+ blob_newline='^'; /* Convert newline in blobs to this */
+ /* Make a tab of powers of 10 */
+ for (i=0,nr=1.0; i < array_elements(log_10) ; i++)
+ { /* It's used by filesort... */
+ log_10[i]= nr ; nr*= 10.0;
+ }
+ specialflag|=options; /* Set options from argv */
+
+ // The following is needed because of like optimization in select.cc
+
+ uchar max_char=my_sort_order[(uchar) max_sort_char];
+ for (i = 0; i < 256; i++)
+ {
+ if ((uchar) my_sort_order[i] > max_char)
+ {
+ max_char=(uchar) my_sort_order[i];
+ max_sort_char= (char) i;
+ }
+ }
+ thread_stack_min=thread_stack - STACK_MIN_SIZE;
+ DBUG_VOID_RETURN;
+}
diff --git a/sql/item.cc b/sql/item.cc
new file mode 100644
index 00000000000..cc3f8101601
--- /dev/null
+++ b/sql/item.cc
@@ -0,0 +1,680 @@
+/* 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 __GNUC__
+#pragma implementation // gcc: Class implementation
+#endif
+
+#include "mysql_priv.h"
+#include <m_ctype.h>
+#include "my_dir.h"
+
+/*****************************************************************************
+** Item functions
+*****************************************************************************/
+
+/* Init all special items */
+
+void item_init(void)
+{
+ item_user_lock_init();
+}
+
+Item::Item()
+{
+ marker=0;
+ binary=maybe_null=null_value=with_sum_func=0;
+ name=0;
+ decimals=0; max_length=0;
+ next=current_thd->free_list; // Put in free list
+ current_thd->free_list=this;
+}
+
+void Item::set_name(char *str,uint length)
+{
+ if (!length)
+ name=str; // Used by AS
+ else
+ {
+ while (length && !isgraph(*str))
+ { // Fix problem with yacc
+ length--;
+ str++;
+ }
+ name=sql_strmake(str,min(length,MAX_FIELD_WIDTH));
+ }
+}
+
+bool Item::eq(const Item *item) const // Only doing this on conds
+{
+ return type() == item->type() && name && item->name &&
+ !my_strcasecmp(name,item->name);
+}
+
+/*
+ Get the value of the function as a TIME structure.
+ As a extra convenience the time structure is reset on error!
+ */
+
+bool Item::get_date(TIME *ltime,bool fuzzydate)
+{
+ char buff[40];
+ String tmp(buff,sizeof(buff)),*res;
+ if (!(res=val_str(&tmp)) ||
+ str_to_TIME(res->ptr(),res->length(),ltime,0) == TIMESTAMP_NONE)
+ {
+ bzero((char*) ltime,sizeof(*ltime));
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ Get time of first argument.
+ As a extra convenience the time structure is reset on error!
+ */
+
+bool Item::get_time(TIME *ltime)
+{
+ char buff[40];
+ String tmp(buff,sizeof(buff)),*res;
+ if (!(res=val_str(&tmp)) ||
+ str_to_time(res->ptr(),res->length(),ltime))
+ {
+ bzero((char*) ltime,sizeof(*ltime));
+ return 1;
+ }
+ return 0;
+}
+
+Item_field::Item_field(Field *f) :Item_ident(NullS,f->table_name,f->field_name)
+{
+ set_field(f);
+}
+
+
+void Item_field::set_field(Field *field_par)
+{
+ field=result_field=field_par; // for easy coding with fields
+ maybe_null=field->maybe_null();
+ max_length=field_par->field_length;
+ decimals= field->decimals();
+ table_name=field_par->table_name;
+ field_name=field_par->field_name;
+ binary=field_par->binary();
+}
+
+const char *Item_ident::full_name() const
+{
+ char *tmp;
+ if (!table_name)
+ return field_name ? field_name : name ? name : "tmp_field";
+ if (db_name)
+ {
+ tmp=(char*) sql_alloc(strlen(db_name)+strlen(table_name)+
+ strlen(field_name)+3);
+ strxmov(tmp,db_name,".",table_name,".",field_name,NullS);
+ }
+ else
+ {
+ tmp=(char*) sql_alloc(strlen(table_name)+strlen(field_name)+2);
+ strxmov(tmp,table_name,".",field_name,NullS);
+ }
+ return tmp;
+}
+
+/* ARGSUSED */
+String *Item_field::val_str(String *str)
+{
+ if ((null_value=field->is_null()))
+ return 0;
+ return field->val_str(str,&str_value);
+}
+
+double Item_field::val()
+{
+ if ((null_value=field->is_null()))
+ return 0.0;
+ return field->val_real();
+}
+
+longlong Item_field::val_int()
+{
+ if ((null_value=field->is_null()))
+ return 0;
+ return field->val_int();
+}
+
+
+String *Item_field::str_result(String *str)
+{
+ if ((null_value=result_field->is_null()))
+ return 0;
+ return result_field->val_str(str,&str_value);
+}
+
+bool Item_field::get_date(TIME *ltime,bool fuzzydate)
+{
+ if ((null_value=field->is_null()) || field->get_date(ltime,fuzzydate))
+ {
+ bzero((char*) ltime,sizeof(*ltime));
+ return 1;
+ }
+ return 0;
+}
+
+bool Item_field::get_time(TIME *ltime)
+{
+ if ((null_value=field->is_null()) || field->get_time(ltime))
+ {
+ bzero((char*) ltime,sizeof(*ltime));
+ return 1;
+ }
+ return 0;
+}
+
+double Item_field::val_result()
+{
+ if ((null_value=result_field->is_null()))
+ return 0.0;
+ return result_field->val_real();
+}
+
+longlong Item_field::val_int_result()
+{
+ if ((null_value=result_field->is_null()))
+ return 0;
+ return result_field->val_int();
+}
+
+bool Item_field::eq(const Item *item) const
+{
+ return item->type() == FIELD_ITEM && ((Item_field*) item)->field == field;
+}
+
+table_map Item_field::used_tables() const
+{
+ if (field->table->const_table)
+ return 0; // const item
+ return field->table->map;
+}
+
+
+String *Item_int::val_str(String *str)
+{
+ str->set(value);
+ return str;
+}
+
+void Item_int::print(String *str)
+{
+ if (!name)
+ {
+ str_value.set(value);
+ name=str_value.c_ptr();
+ }
+ str->append(name);
+}
+
+
+String *Item_real::val_str(String *str)
+{
+ str->set(value,decimals);
+ return str;
+}
+
+void Item_string::print(String *str)
+{
+ str->append('\'');
+ str->append(full_name());
+ str->append('\'');
+}
+
+bool Item_null::eq(const Item *item) const { return item->type() == type(); }
+double Item_null::val() { null_value=1; return 0.0; }
+longlong Item_null::val_int() { null_value=1; return 0; }
+/* ARGSUSED */
+String *Item_null::val_str(String *str)
+{ null_value=1; return 0;}
+
+
+void Item_copy_string::copy()
+{
+ String *res=item->val_str(&str_value);
+ if (res && res != &str_value)
+ str_value.copy(*res);
+ null_value=item->null_value;
+}
+
+/* ARGSUSED */
+String *Item_copy_string::val_str(String *str)
+{
+ if (null_value)
+ return (String*) 0;
+ return &str_value;
+}
+
+/*
+** Functions to convert item to field (for send_fields)
+*/
+
+/* ARGSUSED */
+bool Item::fix_fields(THD *thd,
+ struct st_table_list *list)
+{
+ return 0;
+}
+
+bool Item_field::fix_fields(THD *thd,TABLE_LIST *tables)
+{
+ if (!field)
+ {
+ Field *tmp;
+ if (!(tmp=find_field_in_tables(thd,this,tables)))
+ return 1;
+ set_field(tmp);
+ }
+ return 0;
+}
+
+
+void Item::init_make_field(Send_field *tmp_field,
+ enum enum_field_types field_type)
+{
+ tmp_field->table_name=(char*) "";
+ tmp_field->col_name=name;
+ tmp_field->flags=maybe_null ? 0 : NOT_NULL_FLAG;
+ tmp_field->type=field_type;
+ tmp_field->length=max_length;
+ tmp_field->decimals=decimals;
+}
+
+/* ARGSUSED */
+void Item_field::make_field(Send_field *tmp_field)
+{
+ field->make_field(tmp_field);
+ if (name)
+ tmp_field->col_name=name; // Use user supplied name
+}
+
+void Item_int::make_field(Send_field *tmp_field)
+{
+ init_make_field(tmp_field,FIELD_TYPE_LONGLONG);
+}
+
+void Item_real::make_field(Send_field *tmp_field)
+{
+ init_make_field(tmp_field,FIELD_TYPE_DOUBLE);
+}
+
+void Item_string::make_field(Send_field *tmp_field)
+{
+ init_make_field(tmp_field,FIELD_TYPE_STRING);
+}
+
+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) ?
+ FIELD_TYPE_VAR_STRING :
+ (result_type() == INT_RESULT) ?
+ FIELD_TYPE_LONGLONG : FIELD_TYPE_DOUBLE));
+}
+
+void Item_avg_field::make_field(Send_field *tmp_field)
+{
+ init_make_field(tmp_field,FIELD_TYPE_DOUBLE);
+}
+
+void Item_std_field::make_field(Send_field *tmp_field)
+{
+ init_make_field(tmp_field,FIELD_TYPE_DOUBLE);
+}
+
+/*
+** Set a field:s value from a item
+*/
+
+
+void Item_field::save_org_in_field(Field *to)
+{
+ if (field->is_null())
+ {
+ null_value=1;
+ set_field_to_null(to);
+ }
+ else
+ {
+ to->set_notnull();
+ field_conv(to,field);
+ null_value=0;
+ }
+}
+
+bool Item_field::save_in_field(Field *to)
+{
+ if (result_field->is_null())
+ {
+ null_value=1;
+ return set_field_to_null(to);
+ }
+ else
+ {
+ to->set_notnull();
+ field_conv(to,result_field);
+ null_value=0;
+ }
+ return 0;
+}
+
+
+bool Item_null::save_in_field(Field *field)
+{
+ return set_field_to_null(field);
+}
+
+
+bool Item::save_in_field(Field *field)
+{
+ if (result_type() == STRING_RESULT ||
+ result_type() == REAL_RESULT &&
+ field->result_type() == STRING_RESULT)
+ {
+ String *result;
+ char buff[MAX_FIELD_WIDTH]; // Alloc buffer for small columns
+ str_value.set_quick(buff,sizeof(buff));
+ result=val_str(&str_value);
+ if (null_value)
+ return set_field_to_null(field);
+ field->set_notnull();
+ field->store(result->ptr(),result->length());
+ str_value.set_quick(0, 0);
+ }
+ else if (result_type() == REAL_RESULT)
+ {
+ double nr=val();
+ if (null_value)
+ return set_field_to_null(field);
+ field->set_notnull();
+ field->store(nr);
+ }
+ else
+ {
+ longlong nr=val_int();
+ if (null_value)
+ return set_field_to_null(field);
+ field->set_notnull();
+ field->store(nr);
+ }
+ return 0;
+}
+
+bool Item_string::save_in_field(Field *field)
+{
+ String *result;
+ result=val_str(&str_value);
+ if (null_value)
+ return set_field_to_null(field);
+ field->set_notnull();
+ field->store(result->ptr(),result->length());
+ return 0;
+}
+
+bool Item_int::save_in_field(Field *field)
+{
+ longlong nr=val_int();
+ if (null_value)
+ return set_field_to_null(field);
+ field->set_notnull();
+ field->store(nr);
+ return 0;
+}
+
+bool Item_real::save_in_field(Field *field)
+{
+ double nr=val();
+ if (null_value)
+ return set_field_to_null(field);
+ field->set_notnull();
+ field->store(nr);
+ return 0;
+}
+
+/****************************************************************************
+** varbinary item
+** In string context this is a binary string
+** In number context this is a longlong value.
+****************************************************************************/
+
+static inline uint char_val(char X)
+{
+ return (uint) (X >= '0' && X <= '9' ? X-'0' :
+ X >= 'A' && X <= 'Z' ? X-'A'+10 :
+ X-'a'+10);
+}
+
+Item_varbinary::Item_varbinary(const char *str, uint str_length)
+{
+ name=(char*) str-2; // Lex makes this start with 0x
+ max_length=(str_length+1)/2;
+ char *ptr=(char*) sql_alloc(max_length+1);
+ if (!ptr)
+ return;
+ str_value.set(ptr,max_length);
+ char *end=ptr+max_length;
+ if (max_length*2 != str_length)
+ *ptr++=char_val(*str++); // Not even, assume 0 prefix
+ while (ptr != end)
+ {
+ *ptr++= (char) (char_val(str[0])*16+char_val(str[1]));
+ str+=2;
+ }
+ *ptr=0; // Keep purify happy
+ binary=1; // Binary is default
+}
+
+longlong Item_varbinary::val_int()
+{
+ char *end=(char*) str_value.ptr()+str_value.length(),
+ *ptr=end-min(str_value.length(),sizeof(longlong));
+
+ ulonglong value=0;
+ for (; ptr != end ; ptr++)
+ value=(value << 8)+ (ulonglong) (uchar) *ptr;
+ return (longlong) value;
+}
+
+
+bool Item_varbinary::save_in_field(Field *field)
+{
+ field->set_notnull();
+ if (field->result_type() == STRING_RESULT)
+ {
+ field->store(str_value.ptr(),str_value.length());
+ }
+ else
+ {
+ longlong nr=val_int();
+ field->store(nr);
+ }
+ return 0;
+}
+
+
+void Item_varbinary::make_field(Send_field *tmp_field)
+{
+ init_make_field(tmp_field,FIELD_TYPE_STRING);
+}
+
+/*
+** pack data in buffer for sending
+*/
+
+bool Item::send(String *packet)
+{
+ char buff[MAX_FIELD_WIDTH];
+ String s(buff,sizeof(buff)),*res;
+ if (!(res=val_str(&s)))
+ return net_store_null(packet);
+ CONVERT *convert;
+ if ((convert=current_thd->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)
+{
+ return net_store_null(packet);
+}
+
+/*
+ This is used for HAVING clause
+ Find field in select list having the same name
+ */
+
+bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables)
+{
+ if (!ref)
+ {
+ if (!(ref=find_item_in_list(this,thd->lex.item_list)))
+ return 1;
+ max_length= (*ref)->max_length;
+ maybe_null= (*ref)->maybe_null;
+ decimals= (*ref)->decimals;
+ binary= (*ref)->binary;
+ }
+ return 0;
+}
+
+/*
+** 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)
+{
+ if (a == STRING_RESULT && b == STRING_RESULT)
+ return STRING_RESULT;
+ else if (a == INT_RESULT && b == INT_RESULT)
+ return INT_RESULT;
+ else
+ return REAL_RESULT;
+}
+
+
+Item *resolve_const_item(Item *item,Item *comp_item)
+{
+ if (item->basic_const_item())
+ return item; // Can't be better
+ Item_result res_type=item_cmp_type(comp_item->result_type(),
+ item->result_type());
+ char *name=item->name; // Alloced by sql_alloc
+
+ if (res_type == STRING_RESULT)
+ {
+ char buff[MAX_FIELD_WIDTH];
+ String tmp(buff,sizeof(buff)),*result;
+ result=item->val_str(&tmp);
+ if (item->null_value)
+ {
+#ifdef DELETE_ITEMS
+ delete item;
+#endif
+ return new Item_null(name);
+ }
+ uint length=result->length();
+ char *tmp_str=sql_strmake(result->ptr(),length);
+#ifdef DELETE_ITEMS
+ delete item;
+#endif
+ return new Item_string(name,tmp_str,length);
+ }
+ if (res_type == INT_RESULT)
+ {
+ longlong result=item->val_int();
+ uint length=item->max_length;
+ bool null_value=item->null_value;
+#ifdef DELETE_ITEMS
+ delete item;
+#endif
+ return (null_value ? (Item*) new Item_null(name) :
+ (Item*) new Item_int(name,result,length));
+ }
+ else
+ { // It must REAL_RESULT
+ double result=item->val();
+ uint length=item->max_length,decimals=item->decimals;
+ bool null_value=item->null_value;
+#ifdef DELETE_ITEMS
+ delete item;
+#endif
+ return (null_value ? (Item*) new Item_null(name) :
+ (Item*) new Item_real(name,result,decimals,length));
+ }
+}
+
+/*
+ Return true if the value stored in the field is equal to the const item
+ We need to use this on the range optimizer because in some cases
+ we can't store the value in the field without some precision/character loss.
+*/
+
+bool field_is_equal_to_item(Field *field,Item *item)
+{
+
+ Item_result res_type=item_cmp_type(field->result_type(),
+ item->result_type());
+ if (res_type == STRING_RESULT)
+ {
+ char item_buff[MAX_FIELD_WIDTH];
+ char field_buff[MAX_FIELD_WIDTH];
+ String item_tmp(item_buff,sizeof(item_buff)),*item_result;
+ String field_tmp(field_buff,sizeof(field_buff));
+ item_result=item->val_str(&item_tmp);
+ if (item->null_value)
+ return 1; // This must be true
+ field->val_str(&field_tmp,&field_tmp);
+ return !stringcmp(&field_tmp,item_result);
+ }
+ if (res_type == INT_RESULT)
+ return 1; // Both where of type int
+ double result=item->val();
+ if (item->null_value)
+ return 1;
+ return result == field->val_real();
+}
+
+
+/*****************************************************************************
+** Instantiate templates
+*****************************************************************************/
+
+#ifdef __GNUC__
+template class List<Item>;
+template class List_iterator<Item>;
+template class List<List_item>;
+#endif
diff --git a/sql/item.h b/sql/item.h
new file mode 100644
index 00000000000..bce9c6600ef
--- /dev/null
+++ b/sql/item.h
@@ -0,0 +1,432 @@
+/* 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 __GNUC__
+#pragma interface /* gcc class implementation */
+#endif
+
+struct st_table_list;
+void item_init(void); /* Init item functions */
+
+class Item {
+ Item(const Item &); /* Prevent use of theese */
+ void operator=(Item &);
+public:
+ static void *operator new(size_t size) {return (void*) sql_alloc(size); }
+ static void operator delete(void *ptr,size_t size) {} /*lint -e715 */
+
+ 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,
+ PROC_ITEM,COND_ITEM,REF_ITEM,FIELD_STD_ITEM, CONST_ITEM};
+ enum cond_result { COND_UNDEF,COND_OK,COND_TRUE,COND_FALSE };
+
+ String str_value; /* used to store value */
+ my_string name; /* Name from select */
+ Item *next;
+ uint32 max_length;
+ uint8 marker,decimals;
+ my_bool maybe_null; /* If item may be null */
+ my_bool null_value; /* if item is null */
+ my_bool binary;
+ my_bool with_sum_func;
+
+
+ // alloc & destruct is done as start of select using sql_alloc
+ Item();
+ virtual ~Item() { name=0; } /*lint -e1509 */
+ 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 void save_org_in_field(Field *field)
+ { (void) save_in_field(field); }
+ virtual bool send(String *str);
+ virtual bool eq(const Item *) const;
+ virtual Item_result result_type () const { return REAL_RESULT; }
+ virtual enum Type type() const =0;
+ virtual double val()=0;
+ 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 const char *full_name() const { return name ? name : "???"; }
+ virtual double val_result() { return val(); }
+ virtual longlong val_int_result() { return val_int(); }
+ virtual String *str_result(String* tmp) { return val_str(tmp); }
+ virtual table_map used_tables() const { return (table_map) 0L; }
+ virtual bool basic_const_item() const { return 0; }
+ virtual Item *new_item() { return 0; } /* Only for const items */
+ virtual cond_result eq_cmp_result() const { return COND_OK; }
+ inline uint float_length(uint decimals_par) const
+ { return decimals != NOT_FIXED_DEC ? (DBL_DIG+2+decimals_par) : DBL_DIG+8;}
+ virtual bool const_item() const { return used_tables() == 0; }
+ virtual void print(String *str_arg) { str_arg->append(full_name()); }
+ virtual void update_used_tables() {}
+ virtual void split_sum_func(List<Item> &fields) {}
+ virtual bool get_date(TIME *ltime,bool fuzzydate);
+ virtual bool get_time(TIME *ltime);
+};
+
+
+class Item_ident :public Item
+{
+public:
+ const char *db_name;
+ const char *table_name;
+ const char *field_name;
+ Item_ident(const char *db_name_par,const char *table_name_par,
+ const char *field_name_par)
+ :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;
+};
+
+class Item_field :public Item_ident
+{
+ void set_field(Field *field);
+public:
+ Field *field,*result_field;
+ // Item_field() {}
+
+ Item_field(const char *db_par,const char *table_name_par,
+ const char *field_name_par)
+ :Item_ident(db_par,table_name_par,field_name_par),field(0),result_field(0)
+ {}
+ Item_field(Field *field);
+ enum Type type() const { return FIELD_ITEM; }
+ bool eq(const Item *item) const;
+ double val();
+ longlong val_int();
+ String *val_str(String*);
+ double val_result();
+ longlong val_int_result();
+ String *str_result(String* tmp);
+ bool send(String *str_arg) { return result_field->send(str_arg); }
+ void make_field(Send_field *field);
+ bool fix_fields(THD *,struct st_table_list *);
+ bool save_in_field(Field *field);
+ 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; }
+ bool get_date(TIME *ltime,bool fuzzydate);
+ bool get_time(TIME *ltime);
+};
+
+
+class Item_null :public Item
+{
+public:
+ Item_null(char *name_par=0)
+ { maybe_null=null_value=TRUE; name= name_par ? name_par : (char*) "NULL";}
+ enum Type type() const { return NULL_ITEM; }
+ bool eq(const Item *item) const;
+ double val();
+ longlong val_int();
+ String *val_str(String *str);
+ void make_field(Send_field *field);
+ bool save_in_field(Field *field);
+ enum Item_result result_type () const
+ { return STRING_RESULT; }
+ bool send(String *str);
+ bool basic_const_item() const { return 1; }
+ Item *new_item() { return new Item_null(name); }
+};
+
+
+class Item_int :public Item
+{
+public:
+ const longlong value;
+ Item_int(int32 i,uint length=11) :value((longlong) i)
+ { max_length=length;}
+#ifdef HAVE_LONG_LONG
+ Item_int(longlong i,uint length=21) :value(i)
+ { max_length=length;}
+#endif
+ Item_int(const char *str_arg,longlong i,uint length) :value(i)
+ { max_length=length; name=(char*) str_arg;}
+ Item_int(const char *str_arg) :
+ value(str_arg[0] == '-' ? strtoll(str_arg,(char**) 0,10) :
+ (longlong) strtoull(str_arg,(char**) 0,10))
+ { max_length=strlen(str_arg); name=(char*) str_arg;}
+ enum Type type() const { return INT_ITEM; }
+ virtual enum Item_result result_type () const { return INT_RESULT; }
+ longlong val_int() { return value; }
+ double val() { return (double) value; }
+ String *val_str(String*);
+ void make_field(Send_field *field);
+ bool save_in_field(Field *field);
+ bool basic_const_item() const { return 1; }
+ Item *new_item() { return new Item_int(name,value,max_length); }
+ void print(String *str);
+};
+
+
+class Item_real :public Item
+{
+public:
+ const double value;
+ // Item_real() :value(0) {}
+ Item_real(const char *str_arg,uint length) :value(atof(str_arg))
+ {
+ name=(char*) str_arg;
+ decimals=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;
+ max_length=length;
+ }
+ Item_real(double value_par) :value(value_par) {}
+ bool save_in_field(Field *field);
+ enum Type type() const { return REAL_ITEM; }
+ double val() { return value; }
+ longlong val_int() { return (longlong) (value+(value > 0 ? 0.5 : -0.5));}
+ String *val_str(String*);
+ 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); }
+};
+
+
+class Item_float :public Item_real
+{
+public:
+ Item_float(const char *str,uint length) :Item_real(str,length)
+ {
+ decimals=NOT_FIXED_DEC;
+ max_length=DBL_DIG+8;
+ }
+};
+
+class Item_string :public Item
+{
+public:
+ Item_string(const char *str,uint length)
+ {
+ str_value.set(str,length);
+ max_length=length;
+ name=(char*) str_value.ptr();
+ decimals=NOT_FIXED_DEC;
+ }
+ Item_string(const char *name_par,const char *str,uint length)
+ {
+ str_value.set(str,length);
+ max_length=length;
+ name=(char*) name_par;
+ decimals=NOT_FIXED_DEC;
+ }
+ ~Item_string() {}
+ enum Type type() const { return STRING_ITEM; }
+ 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);
+ void make_field(Send_field *field);
+ enum Item_result result_type () const { return STRING_RESULT; }
+ bool basic_const_item() const { return 1; }
+ Item *new_item() { return new Item_string(name,str_value.ptr(),max_length); }
+ String *const_string() { return &str_value; }
+ inline void append(char *str,uint length) { str_value.append(str,length); }
+ void print(String *str);
+};
+
+/* for show tables */
+
+class Item_datetime :public Item_string
+{
+public:
+ Item_datetime(const char *item_name): Item_string(item_name,"",0)
+ { max_length=19;}
+ void make_field(Send_field *field);
+};
+
+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;}
+};
+
+class Item_varbinary :public Item
+{
+public:
+ Item_varbinary(const char *str,uint str_length);
+ ~Item_varbinary() {}
+ enum Type type() const { return VARBIN_ITEM; }
+ double val() { return (double) Item_varbinary::val_int(); }
+ longlong val_int();
+ String *val_str(String*) { return &str_value; }
+ bool save_in_field(Field *field);
+ void make_field(Send_field *field);
+ enum Item_result result_type () const { return INT_RESULT; }
+};
+
+
+class Item_result_field :public Item /* Item with result field */
+{
+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; }
+ virtual void fix_length_and_dec()=0;
+};
+
+
+class Item_ref :public Item_ident
+{
+ Item **ref;
+public:
+ 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) {}
+ Item_ref(Item **item, char *table_name_par,char *field_name_par)
+ :Item_ident(NullS,table_name_par,field_name_par),ref(item) {}
+ enum Type type() const { return REF_ITEM; }
+ bool eq(const Item *item) const { return (*ref)->eq(item); }
+ ~Item_ref() { if (ref) delete *ref; }
+ double val()
+ {
+ double tmp=(*ref)->val_result();
+ null_value=(*ref)->null_value;
+ return tmp;
+ }
+ longlong val_int()
+ {
+ longlong tmp=(*ref)->val_int_result();
+ null_value=(*ref)->null_value;
+ return tmp;
+ }
+ String *val_str(String* tmp)
+ {
+ tmp=(*ref)->str_result(tmp);
+ null_value=(*ref)->null_value;
+ return tmp;
+ }
+ bool get_date(TIME *ltime,bool fuzzydate)
+ {
+ return (null_value=(*ref)->get_date(ltime,fuzzydate));
+ }
+ bool send(String *tmp) { return (*ref)->send(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); }
+ 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(); }
+};
+
+
+#include "item_sum.h"
+#include "item_func.h"
+#include "item_cmpfunc.h"
+#include "item_strfunc.h"
+#include "item_timefunc.h"
+#include "item_uniq.h"
+
+class Item_copy_string :public Item
+{
+public:
+ Item *item;
+ Item_copy_string(Item *i) :item(i)
+ {
+ null_value=maybe_null=item->maybe_null;
+ decimals=item->decimals;
+ max_length=item->max_length;
+ name=item->name;
+ }
+ ~Item_copy_string() { delete item; }
+ enum Type type() const { return COPY_STR_ITEM; }
+ enum Item_result result_type () const { return STRING_RESULT; }
+ double val()
+ { return null_value ? 0.0 : atof(str_value.c_ptr()); }
+ longlong val_int()
+ { return null_value ? LL(0) : strtoll(str_value.c_ptr(),(char**) 0,10); }
+ String *val_str(String*);
+ void make_field(Send_field *field) { item->make_field(field); }
+ void copy();
+ table_map used_tables() const { return (table_map) 1L; }
+ bool const_item() const { return 0; }
+};
+
+
+class Item_buff :public Sql_alloc
+{
+public:
+ my_bool null_value;
+ Item_buff() :null_value(0) {}
+ virtual bool cmp(void)=0;
+ virtual ~Item_buff(); /*line -e1509 */
+};
+
+class Item_str_buff :public Item_buff
+{
+ Item *item;
+ String value,tmp_value;
+public:
+ Item_str_buff(Item *arg) :item(arg),value(arg->max_length) {}
+ bool cmp(void);
+ ~Item_str_buff(); // Deallocate String:s
+};
+
+
+class Item_real_buff :public Item_buff
+{
+ Item *item;
+ double value;
+public:
+ Item_real_buff(Item *item_par) :item(item_par),value(0.0) {}
+ bool cmp(void);
+};
+
+class Item_int_buff :public Item_buff
+{
+ Item *item;
+ longlong value;
+public:
+ Item_int_buff(Item *item_par) :item(item_par),value(0) {}
+ bool cmp(void);
+};
+
+
+class Item_field_buff :public Item_buff
+{
+ char *buff;
+ Field *field;
+ uint length;
+
+public:
+ Item_field_buff(Item_field *item)
+ {
+ field=item->field;
+ buff= (char*) sql_calloc(length=field->pack_length());
+ }
+ bool cmp(void);
+};
+
+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);
diff --git a/sql/item_buff.cc b/sql/item_buff.cc
new file mode 100644
index 00000000000..61e1f5498a9
--- /dev/null
+++ b/sql/item_buff.cc
@@ -0,0 +1,119 @@
+/* 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 */
+
+
+/* Buffers to save and compare item values */
+
+#include "mysql_priv.h"
+
+/*
+** Create right type of item_buffer for an item
+*/
+
+Item_buff *new_Item_buff(Item *item)
+{
+ if (item->type() == Item::FIELD_ITEM &&
+ !(((Item_field *) item)->field->flags & BLOB_FLAG))
+ return new Item_field_buff((Item_field *) item);
+ if (item->result_type() == STRING_RESULT)
+ return new Item_str_buff((Item_field *) item);
+ if (item->result_type() == INT_RESULT)
+ return new Item_int_buff((Item_field *) item);
+ return new Item_real_buff(item);
+}
+
+Item_buff::~Item_buff() {}
+
+/*
+** Compare with old value and replace value with new value
+** Return true if values have changed
+*/
+
+bool Item_str_buff::cmp(void)
+{
+ String *res;
+ bool tmp;
+
+ res=item->val_str(&tmp_value);
+ if (null_value != item->null_value)
+ {
+ if ((null_value= item->null_value))
+ return TRUE; // New value was null
+ tmp=TRUE;
+ }
+ else if (null_value)
+ return 0; // new and old value was null
+ else if (!item->binary)
+ tmp= sortcmp(&value,res) != 0;
+ else
+ tmp= stringcmp(&value,res) != 0;
+ if (tmp)
+ value.copy(*res); // Remember for next cmp
+ return tmp;
+}
+
+Item_str_buff::~Item_str_buff()
+{
+ item=0; // Safety
+}
+
+bool Item_real_buff::cmp(void)
+{
+ double nr=item->val();
+ if (null_value != item->null_value || nr != value)
+ {
+ null_value= item->null_value;
+ value=nr;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+bool Item_int_buff::cmp(void)
+{
+ longlong nr=item->val_int();
+ if (null_value != item->null_value || nr != value)
+ {
+ null_value= item->null_value;
+ value=nr;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+bool Item_field_buff::cmp(void)
+{
+ bool tmp= field->cmp(buff) != 0; // This is not a blob!
+ if (tmp)
+ field->get_image(buff,length);
+ if (null_value != field->is_null())
+ {
+ null_value= !null_value;
+ tmp=TRUE;
+ }
+ return tmp;
+}
+
+
+/*****************************************************************************
+** Instansiate templates
+*****************************************************************************/
+
+#ifdef __GNUC__
+template class List<Item_buff>;
+template class List_iterator<Item_buff>;
+#endif
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
new file mode 100644
index 00000000000..0a75998ffe1
--- /dev/null
+++ b/sql/item_cmpfunc.cc
@@ -0,0 +1,1298 @@
+/* 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 */
+
+
+/* This file defines all compare functions */
+
+#ifdef __GNUC__
+#pragma implementation // gcc: Class implementation
+#endif
+
+#include "mysql_priv.h"
+#include <m_ctype.h>
+
+/*
+** Test functions
+** These returns 0LL if false and 1LL if true and null if some arg is null
+** 'AND' and 'OR' never return null
+*/
+
+longlong Item_func_not::val_int()
+{
+ double value=args[0]->val();
+ null_value=args[0]->null_value;
+ return !null_value && value == 0 ? 1 : 0;
+}
+
+
+static bool convert_constant_item(Field *field, Item **item)
+{
+ if ((*item)->const_item())
+ {
+ (*item)->save_in_field(field);
+ if (!((*item)->null_value))
+ {
+ Item *tmp=new Item_int(field->val_int());
+ if ((tmp))
+ *item=tmp;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+void Item_bool_func2::fix_length_and_dec()
+{
+ max_length=1;
+
+ /* As some compare functions are generated after sql_yacc,
+ we have to check for out of memory conditons here */
+ if (!args[0] || !args[1])
+ return;
+ // Make a special case of compare with fields to get nicer DATE comparisons
+ if (args[0]->type() == FIELD_ITEM)
+ {
+ Field *field=((Item_field*) args[0])->field;
+ if (field->store_for_compare())
+ {
+ if (convert_constant_item(field,&args[1]))
+ {
+ cmp_func= &Item_bool_func2::compare_int; // Works for all types.
+ return;
+ }
+ }
+ }
+ if (args[1]->type() == FIELD_ITEM)
+ {
+ Field *field=((Item_field*) args[1])->field;
+ if (field->store_for_compare())
+ {
+ if (convert_constant_item(field,&args[0]))
+ {
+ cmp_func= &Item_bool_func2::compare_int; // Works for all types.
+ return;
+ }
+ }
+ }
+ set_cmp_func(item_cmp_type(args[0]->result_type(),args[1]->result_type()));
+}
+
+
+void Item_bool_func2::set_cmp_func(Item_result type)
+{
+ switch (type) {
+ case STRING_RESULT:
+ cmp_func=&Item_bool_func2::compare_string;
+ break;
+ case REAL_RESULT:
+ cmp_func=&Item_bool_func2::compare_real;
+ break;
+ case INT_RESULT:
+ cmp_func=&Item_bool_func2::compare_int;
+ break;
+ }
+}
+
+
+int Item_bool_func2::compare_string()
+{
+ String *res1,*res2;
+ if ((res1=args[0]->val_str(&tmp_value1)))
+ {
+ if ((res2=args[1]->val_str(&tmp_value2)))
+ {
+ null_value=0;
+ return binary ? stringcmp(res1,res2) : sortcmp(res1,res2);
+ }
+ }
+ null_value=1;
+ return -1;
+}
+
+int Item_bool_func2::compare_real()
+{
+ double val1=args[0]->val();
+ if (!args[0]->null_value)
+ {
+ double val2=args[1]->val();
+ if (!args[1]->null_value)
+ {
+ null_value=0;
+ if (val1 < val2) return -1;
+ if (val1 == val2) return 0;
+ return 1;
+ }
+ }
+ null_value=1;
+ return -1;
+}
+
+
+int Item_bool_func2::compare_int()
+{
+ longlong val1=args[0]->val_int();
+ if (!args[0]->null_value)
+ {
+ longlong val2=args[1]->val_int();
+ if (!args[1]->null_value)
+ {
+ null_value=0;
+ if (val1 < val2) return -1;
+ if (val1 == val2) return 0;
+ return 1;
+ }
+ }
+ null_value=1;
+ return -1;
+}
+
+
+
+longlong Item_func_eq::val_int()
+{
+ int value=(this->*cmp_func)();
+ return value == 0 ? 1 : 0;
+}
+
+/* Same as Item_func_eq, but NULL = NULL */
+
+longlong Item_func_equal::val_int()
+{
+ int value=(this->*cmp_func)();
+ if (null_value)
+ {
+ null_value=0;
+ return (args[0]->null_value && args[1]->null_value) ? 1 : 0;
+ }
+ return value == 0;
+}
+
+
+longlong Item_func_ne::val_int()
+{
+ int value=(this->*cmp_func)();
+ return value != 0 ? 1 : 0;
+}
+
+
+longlong Item_func_ge::val_int()
+{
+ int value=(this->*cmp_func)();
+ return value >= 0 ? 1 : 0;
+}
+
+
+longlong Item_func_gt::val_int()
+{
+ int value=(this->*cmp_func)();
+ return value > 0 ? 1 : 0;
+}
+
+longlong Item_func_le::val_int()
+{
+ int value=(this->*cmp_func)();
+ return value <= 0 && !null_value ? 1 : 0;
+}
+
+
+longlong Item_func_lt::val_int()
+{
+ int value=(this->*cmp_func)();
+ return value < 0 && !null_value ? 1 : 0;
+}
+
+
+longlong Item_func_strcmp::val_int()
+{
+ String *a=args[0]->val_str(&tmp_value1);
+ String *b=args[1]->val_str(&tmp_value2);
+ if (!a || !b)
+ {
+ null_value=1;
+ return 0;
+ }
+ int value=stringcmp(a,b);
+ null_value=0;
+ return !value ? 0 : (value < 0 ? (longlong) -1 : (longlong) 1);
+}
+
+
+void Item_func_interval::fix_length_and_dec()
+{
+ bool nums=1;
+ uint i;
+ for (i=0 ; i < arg_count ; i++)
+ {
+ if (!args[i])
+ return; // End of memory
+ if (args[i]->type() != Item::INT_ITEM &&
+ args[i]->type() != Item::REAL_ITEM)
+ {
+ nums=0;
+ break;
+ }
+ }
+ if (nums && arg_count >= 8)
+ {
+ if ((intervals=(double*) sql_alloc(sizeof(double)*arg_count)))
+ {
+ for (i=0 ; i < arg_count ; i++)
+ intervals[i]=args[i]->val();
+ }
+ }
+ maybe_null=0; max_length=2;
+ used_tables_cache|=item->used_tables();
+}
+
+/*
+ return -1 if null value,
+ 0 if lower than lowest
+ 1 - arg_count if between args[n] and args[n+1]
+ arg_count+1 if higher than biggest argument
+*/
+
+longlong Item_func_interval::val_int()
+{
+ double value=item->val();
+ if (item->null_value)
+ return -1; // -1 if null /* purecov: inspected */
+ if (intervals)
+ { // Use binary search to find interval
+ uint start,end;
+ start=0; end=arg_count-1;
+ while (start != end)
+ {
+ uint mid=(start+end+1)/2;
+ if (intervals[mid] <= value)
+ start=mid;
+ else
+ end=mid-1;
+ }
+ return (value < intervals[start]) ? 0 : start+1;
+ }
+ if (args[0]->val() > value)
+ return 0;
+ for (uint i=1 ; i < arg_count ; i++)
+ {
+ if (args[i]->val() > value)
+ return i;
+ }
+ return (longlong) arg_count;
+}
+
+
+void Item_func_interval::update_used_tables()
+{
+ Item_func::update_used_tables();
+ item->update_used_tables();
+ used_tables_cache|=item->used_tables();
+ const_item_cache&=item->const_item();
+}
+
+void Item_func_between::fix_length_and_dec()
+{
+ max_length=1;
+
+ /* As some compare functions are generated after sql_yacc,
+ we have to check for out of memory conditons here */
+ if (!args[0] || !args[1] || !args[2])
+ return;
+ cmp_type=args[0]->result_type();
+ if (args[0]->binary)
+ string_compare=stringcmp;
+ else
+ string_compare=sortcmp;
+
+ // Make a special case of compare with fields to get nicer DATE comparisons
+ if (args[0]->type() == FIELD_ITEM)
+ {
+ Field *field=((Item_field*) args[0])->field;
+ if (field->store_for_compare())
+ {
+ if (convert_constant_item(field,&args[1]))
+ cmp_type=INT_RESULT; // Works for all types.
+ if (convert_constant_item(field,&args[2]))
+ cmp_type=INT_RESULT; // Works for all types.
+ }
+ }
+}
+
+
+longlong Item_func_between::val_int()
+{ // ANSI BETWEEN
+ if (cmp_type == STRING_RESULT)
+ {
+ String *value,*a,*b;
+ value=args[0]->val_str(&value0);
+ if ((null_value=args[0]->null_value))
+ return 0;
+ a=args[1]->val_str(&value1);
+ b=args[2]->val_str(&value2);
+ if (!args[1]->null_value && !args[2]->null_value)
+ return (string_compare(value,a) >= 0 && string_compare(value,b) <= 0) ?
+ 1 : 0;
+ if (args[1]->null_value && args[2]->null_value)
+ null_value=1;
+ else if (args[1]->null_value)
+ {
+ null_value= string_compare(value,b) <= 0; // not null if false range.
+ }
+ else
+ {
+ null_value= string_compare(value,a) >= 0; // not null if false range.
+ }
+ }
+ else if (cmp_type == INT_RESULT)
+ {
+ longlong value=args[0]->val_int(),a,b;
+ if ((null_value=args[0]->null_value))
+ return 0; /* purecov: inspected */
+ a=args[1]->val_int();
+ b=args[2]->val_int();
+ if (!args[1]->null_value && !args[2]->null_value)
+ return (value >= a && value <= b) ? 1 : 0;
+ if (args[1]->null_value && args[2]->null_value)
+ null_value=1;
+ else if (args[1]->null_value)
+ {
+ null_value= value <= b; // not null if false range.
+ }
+ else
+ {
+ null_value= value >= a;
+ }
+ }
+ else
+ {
+ double value=args[0]->val(),a,b;
+ if ((null_value=args[0]->null_value))
+ return 0; /* purecov: inspected */
+ a=args[1]->val();
+ b=args[2]->val();
+ if (!args[1]->null_value && !args[2]->null_value)
+ return (value >= a && value <= b) ? 1 : 0;
+ if (args[1]->null_value && args[2]->null_value)
+ null_value=1;
+ else if (args[1]->null_value)
+ {
+ null_value= value <= b; // not null if false range.
+ }
+ else
+ {
+ null_value= value >= a;
+ }
+ }
+ return 0;
+}
+
+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();
+}
+
+double
+Item_func_ifnull::val()
+{
+ double value=args[0]->val();
+ if (!args[0]->null_value)
+ {
+ null_value=0;
+ return value;
+ }
+ value=args[1]->val();
+ if ((null_value=args[1]->null_value))
+ return 0.0;
+ return value;
+}
+
+longlong
+Item_func_ifnull::val_int()
+{
+ longlong value=args[0]->val_int();
+ if (!args[0]->null_value)
+ {
+ null_value=0;
+ return value;
+ }
+ value=args[1]->val_int();
+ if ((null_value=args[1]->null_value))
+ return 0;
+ return value;
+}
+
+String *
+Item_func_ifnull::val_str(String *str)
+{
+ String *res =args[0]->val_str(str);
+ if (!args[0]->null_value)
+ {
+ null_value=0;
+ return res;
+ }
+ res=args[1]->val_str(str);
+ if ((null_value=args[1]->null_value))
+ return 0;
+ return res;
+}
+
+void
+Item_func_if::fix_length_and_dec()
+{
+ maybe_null=args[1]->maybe_null || args[2]->maybe_null;
+ max_length=max(args[1]->max_length,args[2]->max_length);
+ decimals=max(args[0]->decimals,args[1]->decimals);
+ enum Item_result arg1_type=args[1]->result_type();
+ enum Item_result arg2_type=args[2]->result_type();
+ if (arg1_type == STRING_RESULT || arg2_type == STRING_RESULT)
+ cached_result_type = STRING_RESULT;
+ 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
+}
+
+
+double
+Item_func_if::val()
+{
+ Item *arg= args[0]->val_int() ? args[1] : args[2];
+ double value=arg->val();
+ null_value=arg->null_value;
+ return value;
+}
+
+longlong
+Item_func_if::val_int()
+{
+ Item *arg= args[0]->val_int() ? args[1] : args[2];
+ longlong value=arg->val_int();
+ null_value=arg->null_value;
+ return value;
+}
+
+String *
+Item_func_if::val_str(String *str)
+{
+ Item *arg= args[0]->val_int() ? args[1] : args[2];
+ String *res=arg->val_str(str);
+ null_value=arg->null_value;
+ return res;
+}
+
+
+void
+Item_func_nullif::fix_length_and_dec()
+{
+ Item_bool_func2::fix_length_and_dec();
+ maybe_null=1;
+ if (args[0]) // Only false if EOM
+ {
+ max_length=args[0]->max_length;
+ decimals=args[0]->decimals;
+ cached_result_type=args[0]->result_type();
+ }
+}
+
+/*
+ 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
+*/
+
+double
+Item_func_nullif::val()
+{
+ double value;
+ if (!(this->*cmp_func)() || null_value)
+ {
+ null_value=1;
+ return 0.0;
+ }
+ value=args[0]->val();
+ null_value=args[0]->null_value;
+ return value;
+}
+
+longlong
+Item_func_nullif::val_int()
+{
+ longlong value;
+ if (!(this->*cmp_func)() || null_value)
+ {
+ null_value=1;
+ return 0;
+ }
+ value=args[0]->val_int();
+ null_value=args[0]->null_value;
+ return value;
+}
+
+String *
+Item_func_nullif::val_str(String *str)
+{
+ String *res;
+ if (!(this->*cmp_func)() || null_value)
+ {
+ null_value=1;
+ return 0;
+ }
+ res=args[0]->val_str(str);
+ null_value=args[0]->null_value;
+ return res;
+}
+
+/*
+** CASE expression
+*/
+
+/* Return the matching ITEM or NULL if all compares (including else) failed */
+
+Item *Item_func_case::find_item(String *str)
+{
+ String *first_expr_str,*tmp;
+ longlong first_expr_int;
+ double first_expr_real;
+ bool int_used, real_used,str_used;
+ int_used=real_used=str_used=0;
+
+ /* These will be initialized later */
+ LINT_INIT(first_expr_str);
+ LINT_INIT(first_expr_int);
+ LINT_INIT(first_expr_real);
+
+ // Compare every WHEN argument with it and return the first match
+ for (uint i=0 ; i < arg_count ; i+=2)
+ {
+ if (!first_expr)
+ {
+ // No expression between CASE and first WHEN
+ if (args[i]->val_int())
+ return args[i+1];
+ continue;
+ }
+ switch (args[i]->result_type()) {
+ case STRING_RESULT:
+ if (!str_used)
+ {
+ str_used=1;
+ // We can't use 'str' here as this may be overwritten
+ if (!(first_expr_str= first_expr->val_str(&str_value)))
+ return else_expr; // Impossible
+ }
+ if ((tmp=args[i]->val_str(str))) // If not null
+ {
+ if (first_expr->binary || args[i]->binary)
+ {
+ if (stringcmp(tmp,first_expr_str)==0)
+ return args[i+1];
+ }
+ else if (sortcmp(tmp,first_expr_str)==0)
+ return args[i+1];
+ }
+ break;
+ case INT_RESULT:
+ if (!int_used)
+ {
+ int_used=1;
+ first_expr_int= first_expr->val_int();
+ if (first_expr->null_value)
+ return else_expr;
+ }
+ if (args[i]->val_int()==first_expr_int && !args[i]->null_value)
+ return args[i+1];
+ break;
+ case REAL_RESULT:
+ if (!real_used)
+ {
+ real_used=1;
+ first_expr_real= first_expr->val();
+ if (first_expr->null_value)
+ return else_expr;
+ }
+ if (args[i]->val()==first_expr_real && !args[i]->null_value)
+ return args[i+1];
+ }
+ }
+ // No, WHEN clauses all missed, return ELSE expression
+ return else_expr;
+}
+
+
+
+String *Item_func_case::val_str(String *str)
+{
+ String *res;
+ Item *item=find_item(str);
+
+ if (!item)
+ {
+ null_value=1;
+ return 0;
+ }
+ if (!(res=item->val_str(str)))
+ null_value=1;
+ return res;
+}
+
+
+longlong Item_func_case::val_int()
+{
+ char buff[MAX_FIELD_WIDTH];
+ String dummy_str(buff,sizeof(buff));
+ Item *item=find_item(&dummy_str);
+ longlong res;
+
+ if (!item)
+ {
+ null_value=1;
+ return 0;
+ }
+ res=item->val_int();
+ null_value=item->null_value;
+ return res;
+}
+
+double Item_func_case::val()
+{
+ char buff[MAX_FIELD_WIDTH];
+ String dummy_str(buff,sizeof(buff));
+ Item *item=find_item(&dummy_str);
+ double res;
+
+ if (!item)
+ {
+ null_value=1;
+ return 0;
+ }
+ res=item->val();
+ null_value=item->null_value;
+ return res;
+}
+
+
+bool
+Item_func_case::fix_fields(THD *thd,TABLE_LIST *tables)
+{
+
+ if (first_expr && first_expr->fix_fields(thd,tables) ||
+ else_expr && else_expr->fix_fields(thd,tables))
+ return 1;
+ if (Item_func::fix_fields(thd,tables))
+ return 1;
+ if (!else_expr || else_expr->maybe_null)
+ maybe_null=1; // The result may be NULL
+ return 0;
+}
+
+
+void Item_func_case::fix_length_and_dec()
+{
+ max_length=0;
+ decimals=0;
+ cached_result_type = args[1]->result_type();
+ for (uint i=0 ; i < arg_count ; i+=2)
+ {
+ set_if_bigger(max_length,args[i+1]->max_length);
+ set_if_bigger(decimals,args[i+1]->decimals);
+ }
+ if (else_expr != NULL)
+ {
+ set_if_bigger(max_length,else_expr->max_length);
+ set_if_bigger(decimals,else_expr->decimals);
+ }
+}
+
+
+void Item_func_case::print(String *str)
+{
+ str->append("case "); // Not yet complete
+}
+
+/*
+** Coalesce - return first not NULL argument.
+*/
+
+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);
+ }
+ null_value=1;
+ return 0;
+}
+
+longlong Item_func_coalesce::val_int()
+{
+ null_value=0;
+ for (uint i=0 ; i < arg_count ; i++)
+ {
+ longlong res=args[i]->val_int();
+ if (!args[i]->null_value)
+ return res;
+ }
+ null_value=1;
+ return 0;
+}
+
+double Item_func_coalesce::val()
+{
+ null_value=0;
+ for (uint i=0 ; i < arg_count ; i++)
+ {
+ double res=args[i]->val();
+ if (!args[i]->null_value)
+ return res;
+ }
+ null_value=1;
+ return 0;
+}
+
+
+void Item_func_coalesce::fix_length_and_dec()
+{
+ max_length=0;
+ decimals=0;
+ cached_result_type = args[0]->result_type();
+ for (uint i=0 ; i < arg_count ; i++)
+ {
+ set_if_bigger(max_length,args[i]->max_length);
+ set_if_bigger(decimals,args[i]->decimals);
+ }
+}
+
+/****************************************************************************
+** classes and function for the IN operator
+****************************************************************************/
+
+static int cmp_longlong(longlong *a,longlong *b)
+{
+ return *a < *b ? -1 : *a == *b ? 0 : 1;
+}
+
+static int cmp_double(double *a,double *b)
+{
+ return *a < *b ? -1 : *a == *b ? 0 : 1;
+}
+
+int in_vector::find(Item *item)
+{
+ byte *result=get_value(item);
+ if (!result || !used_count)
+ return 0; // Null value
+
+ uint start,end;
+ start=0; end=used_count-1;
+ while (start != end)
+ {
+ uint mid=(start+end+1)/2;
+ int res;
+ if ((res=(*compare)(base+mid*size,result)) == 0)
+ return 1;
+ if (res < 0)
+ start=mid;
+ else
+ end=mid-1;
+ }
+ return (int) ((*compare)(base+start*size,result) == 0);
+}
+
+
+in_string::in_string(uint elements,qsort_cmp cmp_func)
+ :in_vector(elements,sizeof(String),cmp_func),tmp(buff,sizeof(buff))
+{}
+
+in_string::~in_string()
+{
+ for (uint i=0 ; i < count ; i++)
+ ((String*) base)[i].free();
+}
+
+void in_string::set(uint pos,Item *item)
+{
+ String *str=((String*) base)+pos;
+ String *res=item->val_str(str);
+ if (res && res != str)
+ *str= *res;
+}
+
+byte *in_string::get_value(Item *item)
+{
+ return (byte*) item->val_str(&tmp);
+}
+
+
+in_longlong::in_longlong(uint elements)
+ :in_vector(elements,sizeof(longlong),(qsort_cmp) cmp_longlong)
+{}
+
+void in_longlong::set(uint pos,Item *item)
+{
+ ((longlong*) base)[pos]=item->val_int();
+}
+
+byte *in_longlong::get_value(Item *item)
+{
+ tmp=item->val_int();
+ if (item->null_value)
+ return 0; /* purecov: inspected */
+ return (byte*) &tmp;
+}
+
+
+in_double::in_double(uint elements)
+ :in_vector(elements,sizeof(double),(qsort_cmp) cmp_double)
+{}
+
+void in_double::set(uint pos,Item *item)
+{
+ ((double*) base)[pos]=item->val();
+}
+
+byte *in_double::get_value(Item *item)
+{
+ tmp=item->val();
+ if (item->null_value)
+ return 0; /* purecov: inspected */
+ return (byte*) &tmp;
+}
+
+
+void Item_func_in::fix_length_and_dec()
+{
+ if (const_item())
+ {
+ switch (item->result_type()) {
+ case STRING_RESULT:
+ if (item->binary)
+ array=new in_string(arg_count,(qsort_cmp) stringcmp); /* purecov: inspected */
+ else
+ array=new in_string(arg_count,(qsort_cmp) sortcmp);
+ break;
+ case INT_RESULT:
+ array= new in_longlong(arg_count);
+ break;
+ case REAL_RESULT:
+ array= new in_double(arg_count);
+ break;
+ }
+ uint j=0;
+ for (uint i=0 ; i < arg_count ; i++)
+ {
+ array->set(j,args[i]);
+ if (!args[i]->null_value) // Skipp NULL values
+ j++;
+ }
+ if ((array->used_count=j))
+ array->sort();
+ }
+ else
+ {
+ switch (item->result_type()) {
+ case STRING_RESULT:
+ if (item->binary)
+ in_item= new cmp_item_binary_string;
+ else
+ in_item= new cmp_item_sort_string;
+ break;
+ case INT_RESULT:
+ in_item= new cmp_item_int;
+ break;
+ case REAL_RESULT:
+ in_item= new cmp_item_real;
+ break;
+ }
+ }
+ maybe_null= item->maybe_null;
+ max_length=2;
+ used_tables_cache|=item->used_tables();
+ const_item_cache&=item->const_item();
+}
+
+
+void Item_func_in::print(String *str)
+{
+ str->append('(');
+ item->print(str);
+ Item_func::print(str);
+ str->append(')');
+}
+
+
+longlong Item_func_in::val_int()
+{
+ if (array)
+ {
+ int tmp=array->find(item);
+ null_value=item->null_value;
+ return tmp;
+ }
+ in_item->store_value(item);
+ if ((null_value=item->null_value))
+ return 0;
+ for (uint i=0 ; i < arg_count ; i++)
+ {
+ if (!in_item->cmp(args[i]) && !args[i]->null_value)
+ return 1; // Would maybe be nice with i ?
+ }
+ return 0;
+}
+
+
+void Item_func_in::update_used_tables()
+{
+ Item_func::update_used_tables();
+ item->update_used_tables();
+ used_tables_cache|=item->used_tables();
+ const_item_cache&=item->const_item();
+}
+
+
+longlong Item_func_bit_or::val_int()
+{
+ ulonglong arg1= (ulonglong) args[0]->val_int();
+ if (args[0]->null_value)
+ {
+ null_value=1; /* purecov: inspected */
+ return 0; /* purecov: inspected */
+ }
+ ulonglong arg2= (ulonglong) args[1]->val_int();
+ if (args[1]->null_value)
+ {
+ null_value=1;
+ return 0;
+ }
+ null_value=0;
+ return (longlong) (arg1 | arg2);
+}
+
+
+longlong Item_func_bit_and::val_int()
+{
+ ulonglong arg1= (ulonglong) args[0]->val_int();
+ if (args[0]->null_value)
+ {
+ null_value=1; /* purecov: inspected */
+ return 0; /* purecov: inspected */
+ }
+ ulonglong arg2= (ulonglong) args[1]->val_int();
+ if (args[1]->null_value)
+ {
+ null_value=1; /* purecov: inspected */
+ return 0; /* purecov: inspected */
+ }
+ null_value=0;
+ return (longlong) (arg1 & arg2);
+}
+
+
+bool
+Item_cond::fix_fields(THD *thd,TABLE_LIST *tables)
+{
+ List_iterator<Item> li(list);
+ Item *item;
+ char buff[sizeof(char*)]; // Max local vars in function
+ used_tables_cache=0;
+ const_item_cache=0;
+
+ if (thd && check_stack_overrun(thd,buff))
+ return 0; // Fatal error flag is set!
+ while ((item=li++))
+ {
+ while (item->type() == Item::COND_ITEM &&
+ ((Item_cond*) item)->functype() == functype())
+ { // Identical function
+ li.replace(((Item_cond*) item)->list);
+ ((Item_cond*) item)->list.empty();
+#ifdef DELETE_ITEMS
+ delete (Item_cond*) item;
+#endif
+ item= *li.ref(); // new current item
+ }
+ if (item->fix_fields(thd,tables))
+ return 1; /* purecov: inspected */
+ used_tables_cache|=item->used_tables();
+ with_sum_func= with_sum_func || item->with_sum_func;
+ const_item_cache&=item->const_item();
+ }
+ if (thd)
+ thd->cond_count+=list.elements;
+ fix_length_and_dec();
+ return 0;
+}
+
+
+void Item_cond::split_sum_func(List<Item> &fields)
+{
+ List_iterator<Item> li(list);
+ Item *item;
+ used_tables_cache=0;
+ const_item_cache=0;
+ while ((item=li++))
+ {
+ if (item->with_sum_func && item->type() != SUM_FUNC_ITEM)
+ item->split_sum_func(fields);
+ else if (item->used_tables() || item->type() == SUM_FUNC_ITEM)
+ {
+ fields.push_front(item);
+ li.replace(new Item_ref((Item**) fields.head_ref(),0,item->name));
+ }
+ item->update_used_tables();
+ used_tables_cache|=item->used_tables();
+ const_item_cache&=item->const_item();
+ }
+}
+
+
+table_map
+Item_cond::used_tables() const
+{ // This caches used_tables
+ return used_tables_cache;
+}
+
+void Item_cond::update_used_tables()
+{
+ used_tables_cache=0;
+ const_item_cache=1;
+ List_iterator<Item> li(list);
+ Item *item;
+ while ((item=li++))
+ {
+ item->update_used_tables();
+ used_tables_cache|=item->used_tables();
+ const_item_cache&= item->const_item();
+ }
+}
+
+
+void Item_cond::print(String *str)
+{
+ str->append('(');
+ List_iterator<Item> li(list);
+ Item *item;
+ if ((item=li++))
+ item->print(str);
+ while ((item=li++))
+ {
+ str->append(' ');
+ str->append(func_name());
+ str->append(' ');
+ item->print(str);
+ }
+ str->append(')');
+}
+
+
+longlong Item_cond_and::val_int()
+{
+ List_iterator<Item> li(list);
+ Item *item;
+ 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;
+ }
+ }
+ null_value=0;
+ return 1;
+}
+
+longlong Item_cond_or::val_int()
+{
+ List_iterator<Item> li(list);
+ Item *item;
+ null_value=0;
+ while ((item=li++))
+ {
+ if (item->val_int() != 0)
+ {
+ null_value=0;
+ return 1;
+ }
+ if (item->null_value)
+ null_value=1;
+ }
+ return 0;
+}
+
+longlong Item_func_isnull::val_int()
+{
+ (void) args[0]->val();
+ return (args[0]->null_value) ? 1 : 0;
+}
+
+longlong Item_func_isnotnull::val_int()
+{
+ (void) args[0]->val();
+ return !(args[0]->null_value) ? 1 : 0;
+}
+
+
+void Item_func_like::fix_length_and_dec()
+{
+ decimals=0; max_length=1;
+ // cmp_type=STRING_RESULT; // For quick select
+}
+
+
+longlong Item_func_like::val_int()
+{
+ String *res,*res2;
+ res=args[0]->val_str(&tmp_value1);
+ if (args[0]->null_value)
+ {
+ null_value=1;
+ return 0;
+ }
+ res2=args[1]->val_str(&tmp_value2);
+ if (args[1]->null_value)
+ {
+ null_value=1;
+ return 0;
+ }
+ null_value=0;
+ if (binary)
+ return wild_compare(*res,*res2,escape) ? 0 : 1;
+ else
+ return wild_case_compare(*res,*res2,escape) ? 0 : 1;
+}
+
+
+/* We can optimize a where if first character isn't a wildcard */
+
+Item_func::optimize_type Item_func_like::select_optimize() const
+{
+ if (args[1]->type() == STRING_ITEM)
+ {
+ if (((Item_string *) args[1])->str_value[0] != wild_many)
+ {
+ if ((args[0]->result_type() != STRING_RESULT) ||
+ ((Item_string *) args[1])->str_value[0] != wild_one)
+ return OPTIMIZE_OP;
+ }
+ }
+ return OPTIMIZE_NONE;
+}
+
+#ifdef USE_REGEX
+
+bool
+Item_func_regex::fix_fields(THD *thd,TABLE_LIST *tables)
+{
+ if (args[0]->fix_fields(thd,tables) || args[1]->fix_fields(thd,tables))
+ return 1; /* purecov: inspected */
+ with_sum_func=args[0]->with_sum_func || args[1]->with_sum_func;
+ max_length=1; decimals=0;
+ binary=args[0]->binary || args[1]->binary;
+ used_tables_cache=args[0]->used_tables() | args[1]->used_tables();
+ const_item_cache=args[0]->const_item() && args[1]->const_item();
+ if (!regex_compiled && args[1]->const_item())
+ {
+ char buff[MAX_FIELD_WIDTH];
+ String tmp(buff,sizeof(buff));
+ String *res=args[1]->val_str(&tmp);
+ if (args[1]->null_value)
+ { // Will always return NULL
+ maybe_null=1;
+ return 0;
+ }
+ int error;
+ if ((error=regcomp(&preg,res->c_ptr(),
+ binary ? REG_EXTENDED | REG_NOSUB :
+ REG_EXTENDED | REG_NOSUB | REG_ICASE)))
+ {
+ (void) regerror(error,&preg,buff,sizeof(buff));
+ my_printf_error(ER_REGEXP_ERROR,ER(ER_REGEXP_ERROR),MYF(0),buff);
+ return 1;
+ }
+ regex_compiled=regex_is_const=1;
+ maybe_null=args[0]->maybe_null;
+ }
+ else
+ maybe_null=1;
+ return 0;
+}
+
+
+longlong Item_func_regex::val_int()
+{
+ char buff[MAX_FIELD_WIDTH];
+ String *res, tmp(buff,sizeof(buff));
+
+ res=args[0]->val_str(&tmp);
+ if (args[0]->null_value)
+ {
+ null_value=1;
+ return 0;
+ }
+ if (!regex_is_const)
+ {
+ char buff2[MAX_FIELD_WIDTH];
+ String *res2, tmp2(buff2,sizeof(buff2));
+
+ res2= args[1]->val_str(&tmp2);
+ if (args[1]->null_value)
+ {
+ null_value=1;
+ return 0;
+ }
+ if (!regex_compiled || stringcmp(res2,&prev_regexp))
+ {
+ prev_regexp.copy(*res2);
+ if (regex_compiled)
+ {
+ regfree(&preg);
+ regex_compiled=0;
+ }
+ if (regcomp(&preg,res2->c_ptr(),
+ binary ? REG_EXTENDED | REG_NOSUB :
+ REG_EXTENDED | REG_NOSUB | REG_ICASE))
+
+ {
+ null_value=1;
+ return 0;
+ }
+ regex_compiled=1;
+ }
+ }
+ null_value=0;
+ return regexec(&preg,res->c_ptr(),0,(regmatch_t*) 0,0) ? 0 : 1;
+}
+
+
+Item_func_regex::~Item_func_regex()
+{
+ if (regex_compiled)
+ {
+ regfree(&preg);
+ regex_compiled=0;
+ }
+}
+
+#endif /* USE_REGEX */
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
new file mode 100644
index 00000000000..591d0f6a2f7
--- /dev/null
+++ b/sql/item_cmpfunc.h
@@ -0,0 +1,567 @@
+/* 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 */
+
+
+/* compare and test functions */
+
+#ifdef __GNUC__
+#pragma interface /* gcc class implementation */
+#endif
+
+class Item_bool_func :public Item_int_func
+{
+public:
+ Item_bool_func() :Item_int_func() {}
+ 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; }
+};
+
+class Item_bool_func2 :public Item_int_func
+{ /* Bool with 2 string args */
+protected:
+ String tmp_value1,tmp_value2;
+public:
+ Item_bool_func2(Item *a,Item *b) :Item_int_func(a,b) {}
+ void fix_length_and_dec();
+ void set_cmp_func(Item_result type);
+ int (Item_bool_func2::*cmp_func)();
+ int compare_string(); /* compare arg[0] & arg[1] */
+ int compare_real(); /* compare arg[0] & arg[1] */
+ int compare_int(); /* compare arg[0] & arg[1] */
+ optimize_type select_optimize() const { return OPTIMIZE_OP; }
+ 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); }
+};
+
+
+class Item_func_not :public Item_bool_func
+{
+public:
+ Item_func_not(Item *a) :Item_bool_func(a) {}
+ longlong val_int();
+ const char *func_name() const { return "not"; }
+};
+
+class Item_func_eq :public Item_bool_func2
+{
+public:
+ Item_func_eq(Item *a,Item *b) :Item_bool_func2(a,b) { };
+ longlong val_int();
+ enum Functype functype() const { return EQ_FUNC; }
+ enum Functype rev_functype() const { return EQ_FUNC; }
+ cond_result eq_cmp_result() const { return COND_TRUE; }
+ const char *func_name() const { return "="; }
+};
+
+class Item_func_equal :public Item_bool_func2
+{
+public:
+ Item_func_equal(Item *a,Item *b) :Item_bool_func2(a,b) { };
+ longlong val_int();
+ void fix_length_and_dec()
+ { Item_bool_func2::fix_length_and_dec() ; maybe_null=0; }
+ enum Functype functype() const { return EQUAL_FUNC; }
+ enum Functype rev_functype() const { return EQUAL_FUNC; }
+ cond_result eq_cmp_result() const { return COND_TRUE; }
+ const char *func_name() const { return "<=>"; }
+};
+
+
+class Item_func_ge :public Item_bool_func2
+{
+public:
+ Item_func_ge(Item *a,Item *b) :Item_bool_func2(a,b) { };
+ longlong val_int();
+ enum Functype functype() const { return GE_FUNC; }
+ enum Functype rev_functype() const { return LE_FUNC; }
+ cond_result eq_cmp_result() const { return COND_TRUE; }
+ const char *func_name() const { return ">="; }
+};
+
+
+class Item_func_gt :public Item_bool_func2
+{
+public:
+ Item_func_gt(Item *a,Item *b) :Item_bool_func2(a,b) { };
+ longlong val_int();
+ enum Functype functype() const { return GT_FUNC; }
+ enum Functype rev_functype() const { return LT_FUNC; }
+ cond_result eq_cmp_result() const { return COND_FALSE; }
+ const char *func_name() const { return ">"; }
+};
+
+
+class Item_func_le :public Item_bool_func2
+{
+public:
+ Item_func_le(Item *a,Item *b) :Item_bool_func2(a,b) { };
+ longlong val_int();
+ enum Functype functype() const { return LE_FUNC; }
+ enum Functype rev_functype() const { return GE_FUNC; }
+ cond_result eq_cmp_result() const { return COND_TRUE; }
+ const char *func_name() const { return "<="; }
+};
+
+
+class Item_func_lt :public Item_bool_func2
+{
+public:
+ Item_func_lt(Item *a,Item *b) :Item_bool_func2(a,b) { }
+ longlong val_int();
+ enum Functype functype() const { return LT_FUNC; }
+ enum Functype rev_functype() const { return GT_FUNC; }
+ cond_result eq_cmp_result() const { return COND_FALSE; }
+ const char *func_name() const { return "<"; }
+};
+
+
+class Item_func_ne :public Item_bool_func2
+{
+public:
+ Item_func_ne(Item *a,Item *b) :Item_bool_func2(a,b) { }
+ longlong val_int();
+ enum Functype functype() const { return NE_FUNC; }
+ cond_result eq_cmp_result() const { return COND_FALSE; }
+ optimize_type select_optimize() const { return OPTIMIZE_NONE; }
+ const char *func_name() const { return "<>"; }
+};
+
+
+class Item_func_between :public Item_int_func
+{
+ int (*string_compare)(const String *x,const String *y);
+public:
+ Item_result cmp_type;
+ String value0,value1,value2;
+ Item_func_between(Item *a,Item *b,Item *c) :Item_int_func(a,b,c) {}
+ longlong val_int();
+ optimize_type select_optimize() const { return OPTIMIZE_KEY; }
+ enum Functype functype() const { return BETWEEN; }
+ const char *func_name() const { return "between"; }
+ void fix_length_and_dec();
+};
+
+
+class Item_func_strcmp :public Item_bool_func2
+{
+public:
+ Item_func_strcmp(Item *a,Item *b) :Item_bool_func2(a,b) {}
+ longlong val_int();
+ void fix_length_and_dec() { max_length=2; }
+ optimize_type select_optimize() const { return OPTIMIZE_NONE; }
+ const char *func_name() const { return "strcmp"; }
+};
+
+
+class Item_func_interval :public Item_int_func
+{
+ Item *item;
+ double *intervals;
+public:
+ Item_func_interval(Item *a,List<Item> &list)
+ :Item_int_func(list),item(a),intervals(0) {}
+ longlong val_int();
+ bool fix_fields(THD *thd,struct st_table_list *tlist)
+ {
+ return (item->fix_fields(thd,tlist) || Item_func::fix_fields(thd,tlist));
+ }
+ void fix_length_and_dec();
+ ~Item_func_interval() { delete item; }
+ const char *func_name() const { return "interval"; }
+ void update_used_tables();
+};
+
+
+class Item_func_ifnull :public Item_func
+{
+ enum Item_result cached_result_type;
+public:
+ Item_func_ifnull(Item *a,Item *b) :Item_func(a,b) { }
+ double val();
+ longlong val_int();
+ String *val_str(String *str);
+ enum Item_result result_type () const { return cached_result_type; }
+ void fix_length_and_dec();
+ const char *func_name() const { return "ifnull"; }
+};
+
+
+class Item_func_if :public Item_func
+{
+ enum Item_result cached_result_type;
+public:
+ Item_func_if(Item *a,Item *b,Item *c) :Item_func(a,b,c) { }
+ double val();
+ longlong val_int();
+ String *val_str(String *str);
+ enum Item_result result_type () const { return cached_result_type; }
+ void fix_length_and_dec();
+ const char *func_name() const { return "if"; }
+};
+
+
+class Item_func_nullif :public Item_bool_func2
+{
+ enum Item_result cached_result_type;
+public:
+ Item_func_nullif(Item *a,Item *b) :Item_bool_func2(a,b) { }
+ double val();
+ longlong val_int();
+ String *val_str(String *str);
+ enum Item_result result_type () const { return cached_result_type; }
+ void fix_length_and_dec();
+ const char *func_name() const { return "nullif"; }
+};
+
+
+class Item_func_coalesce :public Item_func
+{
+ enum Item_result cached_result_type;
+public:
+ Item_func_coalesce(List<Item> &list) :Item_func(list) {}
+ double val();
+ longlong val_int();
+ String *val_str(String *);
+ void fix_length_and_dec();
+ enum Item_result result_type () const { return cached_result_type; }
+ const char *func_name() const { return "coalesce"; }
+};
+
+class Item_func_case :public Item_func
+{
+ Item * first_expr, *else_expr;
+ enum Item_result cached_result_type;
+ String tmp_value;
+public:
+ Item_func_case(List<Item> &list, Item *first_expr_, Item *else_expr_)
+ :Item_func(list), first_expr(first_expr_), else_expr(else_expr_) {}
+ double val();
+ longlong val_int();
+ String *val_str(String *);
+ void fix_length_and_dec();
+ enum Item_result result_type () const { return cached_result_type; }
+ const char *func_name() const { return "case"; }
+ void print(String *str);
+ bool fix_fields(THD *thd,struct st_table_list *tlist);
+ Item *find_item(String *str);
+};
+
+
+/* Functions to handle the optimized IN */
+
+class in_vector :public Sql_alloc
+{
+ protected:
+ char *base;
+ uint size;
+ qsort_cmp compare;
+ uint count;
+public:
+ uint used_count;
+ in_vector(uint elements,uint element_length,qsort_cmp cmp_func)
+ :base((char*) sql_calloc(elements*element_length)),
+ size(element_length), compare(cmp_func), count(elements),
+ used_count(elements) {}
+ virtual ~in_vector() {}
+ virtual void set(uint pos,Item *item)=0;
+ virtual byte *get_value(Item *item)=0;
+ void sort()
+ {
+ qsort(base,used_count,size,compare);
+ }
+ int find(Item *item);
+};
+
+
+class in_string :public in_vector
+{
+ char buff[80];
+ String tmp;
+public:
+ in_string(uint elements,qsort_cmp cmp_func);
+ ~in_string();
+ void set(uint pos,Item *item);
+ byte *get_value(Item *item);
+};
+
+
+class in_longlong :public in_vector
+{
+ longlong tmp;
+public:
+ in_longlong(uint elements);
+ void set(uint pos,Item *item);
+ byte *get_value(Item *item);
+};
+
+
+class in_double :public in_vector
+{
+ double tmp;
+public:
+ in_double(uint elements);
+ void set(uint pos,Item *item);
+ byte *get_value(Item *item);
+};
+
+
+/*
+** Classes for easy comparing of non const items
+*/
+
+class cmp_item :public Sql_alloc
+{
+public:
+ cmp_item() {}
+ virtual ~cmp_item() {}
+ virtual void store_value(Item *item)=0;
+ virtual int cmp(Item *item)=0;
+};
+
+
+class cmp_item_sort_string :public cmp_item {
+ protected:
+ char value_buff[80];
+ String value,*value_res;
+public:
+ cmp_item_sort_string() :value(value_buff,sizeof(value_buff)) {}
+ void store_value(Item *item)
+ {
+ value_res=item->val_str(&value);
+ }
+ int cmp(Item *arg)
+ {
+ char buff[80];
+ String tmp(buff,sizeof(buff)),*res;
+ if (!(res=arg->val_str(&tmp)))
+ return 1; /* Can't be right */
+ return sortcmp(value_res,res);
+ }
+};
+
+class cmp_item_binary_string :public cmp_item_sort_string {
+public:
+ cmp_item_binary_string() {}
+ int cmp(Item *arg)
+ {
+ char buff[80];
+ String tmp(buff,sizeof(buff)),*res;
+ if (!(res=arg->val_str(&tmp)))
+ return 1; /* Can't be right */
+ return stringcmp(value_res,res);
+ }
+};
+
+
+class cmp_item_int :public cmp_item
+{
+ longlong value;
+public:
+ void store_value(Item *item)
+ {
+ value=item->val_int();
+ }
+ int cmp(Item *arg)
+ {
+ return value != arg->val_int();
+ }
+};
+
+
+class cmp_item_real :public cmp_item
+{
+ double value;
+public:
+ void store_value(Item *item)
+ {
+ value= item->val();
+ }
+ int cmp(Item *arg)
+ {
+ return value != arg->val();
+ }
+};
+
+
+class Item_func_in :public Item_int_func
+{
+ Item *item;
+ in_vector *array;
+ cmp_item *in_item;
+ public:
+ Item_func_in(Item *a,List<Item> &list)
+ :Item_int_func(list),item(a),array(0),in_item(0) {}
+ longlong val_int();
+ bool fix_fields(THD *thd,struct st_table_list *tlist)
+ {
+ return (item->fix_fields(thd,tlist) || Item_func::fix_fields(thd,tlist));
+ }
+ void fix_length_and_dec();
+ ~Item_func_in() { delete item; delete array; delete in_item; }
+ optimize_type select_optimize() const
+ { return array ? OPTIMIZE_KEY : OPTIMIZE_NONE; }
+ Item *key_item() const { return item; }
+ void print(String *str);
+ enum Functype functype() const { return IN_FUNC; }
+ const char *func_name() const { return " IN "; }
+ void update_used_tables();
+};
+
+
+
+/* Functions used by where clause */
+
+class Item_func_isnull :public Item_bool_func
+{
+public:
+ Item_func_isnull(Item *a) :Item_bool_func(a) {}
+ longlong val_int();
+ enum Functype functype() const { return ISNULL_FUNC; }
+ void fix_length_and_dec()
+ {
+ decimals=0; max_length=1; maybe_null=0;
+ Item_func_isnull::update_used_tables();
+ }
+ const char *func_name() const { return "isnull"; }
+ /* Optimize case of not_null_column IS NULL */
+ void update_used_tables()
+ {
+ if (!args[0]->maybe_null)
+ used_tables_cache=0; /* is always false */
+ else
+ {
+ args[0]->update_used_tables();
+ used_tables_cache=args[0]->used_tables();
+ }
+ }
+ optimize_type select_optimize() const { return OPTIMIZE_NULL; }
+};
+
+class Item_func_isnotnull :public Item_bool_func
+{
+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; }
+ const char *func_name() const { return "isnotnull"; }
+ optimize_type select_optimize() const { return OPTIMIZE_NULL; }
+};
+
+class Item_func_like :public Item_bool_func2
+{
+ char escape;
+public:
+ Item_func_like(Item *a,Item *b, char* escape_arg) :Item_bool_func2(a,b),escape(*escape_arg)
+ {}
+ longlong val_int();
+ enum Functype functype() const { return LIKE_FUNC; }
+ optimize_type select_optimize() const;
+ cond_result eq_cmp_result() const { return COND_TRUE; }
+ const char *func_name() const { return "like"; }
+ void fix_length_and_dec();
+};
+
+#ifdef USE_REGEX
+
+#include <regex.h>
+
+class Item_func_regex :public Item_bool_func
+{
+ regex_t preg;
+ bool regex_compiled;
+ bool regex_is_const;
+ String prev_regexp;
+public:
+ Item_func_regex(Item *a,Item *b) :Item_bool_func(a,b),
+ regex_compiled(0),regex_is_const(0) {}
+ ~Item_func_regex();
+ longlong val_int();
+ bool fix_fields(THD *thd,struct st_table_list *tlist);
+ const char *func_name() const { return "regex"; }
+};
+
+#else
+
+class Item_func_regex :public Item_bool_func
+{
+public:
+ Item_func_regex(Item *a,Item *b) :Item_bool_func(a,b) {}
+ longlong val_int() { return 0;}
+ const char *func_name() const { return "regex"; }
+};
+
+#endif /* USE_REGEX */
+
+
+typedef class Item COND;
+
+class Item_cond :public Item_bool_func
+{
+protected:
+ List<Item> list;
+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() { list.delete_elements(); }
+ bool add(Item *item) { return list.push_back(item); }
+ bool fix_fields(THD *,struct st_table_list *);
+
+ enum Type type() const { return COND_ITEM; }
+ List<Item>* argument_list() { return &list; }
+ table_map used_tables() const;
+ void update_used_tables();
+ void print(String *str);
+ void split_sum_func(List<Item> &fields);
+ friend int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds);
+};
+
+
+class Item_cond_and :public Item_cond
+{
+public:
+ Item_cond_and() :Item_cond() {}
+ Item_cond_and(Item *i1,Item *i2) :Item_cond(i1,i2) {}
+ enum Functype functype() const { return COND_AND_FUNC; }
+ longlong val_int();
+ const char *func_name() const { return "and"; }
+};
+
+class Item_cond_or :public Item_cond
+{
+public:
+ Item_cond_or() :Item_cond() {}
+ Item_cond_or(Item *i1,Item *i2) :Item_cond(i1,i2) {}
+ enum Functype functype() const { return COND_OR_FUNC; }
+ longlong val_int();
+ const char *func_name() const { return "or"; }
+};
+
+
+/* Some usefull inline functions */
+
+inline Item *and_conds(Item *a,Item *b)
+{
+ if (!b) return a;
+ if (!a) return b;
+ Item *cond=new Item_cond_and(a,b);
+ if (cond)
+ cond->update_used_tables();
+ return cond;
+}
diff --git a/sql/item_create.cc b/sql/item_create.cc
new file mode 100644
index 00000000000..a8f41c14aad
--- /dev/null
+++ b/sql/item_create.cc
@@ -0,0 +1,383 @@
+/* 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 */
+
+/* Functions to create an item. Used by lex.h */
+
+#include "mysql_priv.h"
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+Item *create_func_abs(Item* a)
+{
+ return new Item_func_abs(a);
+}
+
+Item *create_func_acos(Item* a)
+{
+ return new Item_func_acos(a);
+}
+
+Item *create_func_ascii(Item* a)
+{
+ return new Item_func_ascii(a);
+}
+
+Item *create_func_ord(Item* a)
+{
+ return new Item_func_ord(a);
+}
+
+Item *create_func_asin(Item* a)
+{
+ return new Item_func_asin(a);
+}
+
+Item *create_func_bin(Item* a)
+{
+ return new Item_func_conv(a,new Item_int((int32) 10,2),
+ new Item_int((int32) 2,1));
+}
+
+Item *create_func_bit_count(Item* a)
+{
+ return new Item_func_bit_count(a);
+}
+
+Item *create_func_ceiling(Item* a)
+{
+ return new Item_func_ceiling(a);
+}
+
+Item *create_func_connection_id(void)
+{
+ return new Item_int("CONNECTION_ID()",(longlong) current_thd->thread_id,10);
+}
+
+Item *create_func_conv(Item* a, Item *b, Item *c)
+{
+ return new Item_func_conv(a,b,c);
+}
+
+Item *create_func_cos(Item* a)
+{
+ return new Item_func_cos(a);
+}
+
+Item *create_func_cot(Item* a)
+{
+ return new Item_func_div(new Item_int((char*) "1",1,1),
+ new Item_func_tan(a));
+}
+
+Item *create_func_date_format(Item* a,Item *b)
+{
+ return new Item_func_date_format(a,b,0);
+}
+
+Item *create_func_dayofmonth(Item* a)
+{
+ return new Item_func_dayofmonth(a);
+}
+
+Item *create_func_dayofweek(Item* a)
+{
+ return new Item_func_weekday(new Item_func_to_days(a),1);
+}
+
+Item *create_func_dayofyear(Item* a)
+{
+ return new Item_func_dayofyear(a);
+}
+
+Item *create_func_dayname(Item* a)
+{
+ return new Item_func_dayname(new Item_func_to_days(a));
+}
+
+Item *create_func_degrees(Item *a)
+{
+ return new Item_func_units((char*) "degrees",a,180/M_PI,0.0);
+}
+
+Item *create_func_exp(Item* a)
+{
+ return new Item_func_exp(a);
+}
+
+Item *create_func_find_in_set(Item* a, Item *b)
+{
+ return new Item_func_find_in_set(a, b);
+}
+
+Item *create_func_floor(Item* a)
+{
+ return new Item_func_floor(a);
+}
+
+Item *create_func_from_days(Item* a)
+{
+ return new Item_func_from_days(a);
+}
+
+Item *create_func_get_lock(Item* a, Item *b)
+{
+ 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));
+}
+
+Item *create_func_inet_ntoa(Item* a)
+{
+ return new Item_func_inet_ntoa(a);
+}
+
+Item *create_func_inet_aton(Item* a)
+{
+ return new Item_func_inet_aton(a);
+}
+
+
+Item *create_func_ifnull(Item* a, Item *b)
+{
+ return new Item_func_ifnull(a,b);
+}
+
+Item *create_func_nullif(Item* a, Item *b)
+{
+ return new Item_func_nullif(a,b);
+}
+
+Item *create_func_locate(Item* a, Item *b)
+{
+ return new Item_func_locate(b,a);
+}
+
+Item *create_func_instr(Item* a, Item *b)
+{
+ return new Item_func_locate(a,b);
+}
+
+Item *create_func_isnull(Item* a)
+{
+ return new Item_func_isnull(a);
+}
+
+Item *create_func_lcase(Item* a)
+{
+ return new Item_func_lcase(a);
+}
+
+Item *create_func_length(Item* a)
+{
+ return new Item_func_length(a);
+}
+
+Item *create_func_char_length(Item* a)
+{
+ return new Item_func_char_length(a);
+}
+
+Item *create_func_log(Item* a)
+{
+ return new Item_func_log(a);
+}
+
+Item *create_func_log10(Item* a)
+{
+ return new Item_func_log10(a);
+}
+
+Item *create_func_lpad(Item* a, Item *b, Item *c)
+{
+ return new Item_func_lpad(a,b,c);
+}
+
+Item *create_func_ltrim(Item* a)
+{
+ return new Item_func_ltrim(a,new Item_string(" ",1));
+}
+
+Item *create_func_md5(Item* a)
+{
+ return new Item_func_md5(a);
+}
+
+Item *create_func_mod(Item* a, Item *b)
+{
+ return new Item_func_mod(a,b);
+}
+
+Item *create_func_monthname(Item* a)
+{
+ return new Item_func_monthname(a);
+}
+
+Item *create_func_month(Item* a)
+{
+ return new Item_func_month(a);
+}
+
+Item *create_func_oct(Item *a)
+{
+ return new Item_func_conv(a,new Item_int((int32) 10,2),
+ new Item_int((int32) 8,1));
+}
+
+Item *create_func_period_add(Item* a, Item *b)
+{
+ return new Item_func_period_add(a,b);
+}
+
+Item *create_func_period_diff(Item* a, Item *b)
+{
+ return new Item_func_period_diff(a,b);
+}
+
+Item *create_func_pi(void)
+{
+ return new Item_real("PI()",M_PI,6,8);
+}
+
+Item *create_func_pow(Item* a, Item *b)
+{
+ return new Item_func_pow(a,b);
+}
+
+Item *create_func_quarter(Item* a)
+{
+ return new Item_func_quarter(a);
+}
+
+Item *create_func_radians(Item *a)
+{
+ return new Item_func_units((char*) "radians",a,M_PI/180,0.0);
+}
+
+Item *create_func_release_lock(Item* a)
+{
+ return new Item_func_release_lock(a);
+}
+
+Item *create_func_repeat(Item* a, Item *b)
+{
+ return new Item_func_repeat(a,b);
+}
+
+Item *create_func_reverse(Item* a)
+{
+ return new Item_func_reverse(a);
+}
+
+Item *create_func_rpad(Item* a, Item *b, Item *c)
+{
+ return new Item_func_rpad(a,b,c);
+}
+
+Item *create_func_rtrim(Item* a)
+{
+ return new Item_func_rtrim(a,new Item_string(" ",1));
+}
+
+Item *create_func_sec_to_time(Item* a)
+{
+ return new Item_func_sec_to_time(a);
+}
+
+Item *create_func_sign(Item* a)
+{
+ return new Item_func_sign(a);
+}
+
+Item *create_func_sin(Item* a)
+{
+ return new Item_func_sin(a);
+}
+
+Item *create_func_space(Item *a)
+{
+ return new Item_func_repeat(new Item_string(" ",1),a);
+}
+
+Item *create_func_soundex(Item* a)
+{
+ return new Item_func_soundex(a);
+}
+
+Item *create_func_sqrt(Item* a)
+{
+ return new Item_func_sqrt(a);
+}
+
+Item *create_func_strcmp(Item* a, Item *b)
+{
+ return new Item_func_strcmp(a,b);
+}
+
+Item *create_func_tan(Item* a)
+{
+ return new Item_func_tan(a);
+}
+
+Item *create_func_time_format(Item *a, Item *b)
+{
+ return new Item_func_date_format(a,b,1);
+}
+
+Item *create_func_time_to_sec(Item* a)
+{
+ return new Item_func_time_to_sec(a);
+}
+
+Item *create_func_to_days(Item* a)
+{
+ return new Item_func_to_days(a);
+}
+
+Item *create_func_truncate (Item *a, Item *b)
+{
+ return new Item_func_round(a,b,1);
+}
+
+Item *create_func_ucase(Item* a)
+{
+ return new Item_func_ucase(a);
+}
+
+Item *create_func_version(void)
+{
+ return new Item_string(NullS,server_version, strlen(server_version));
+}
+
+Item *create_func_weekday(Item* a)
+{
+ return new Item_func_weekday(new Item_func_to_days(a),0);
+}
+
+Item *create_func_year(Item* a)
+{
+ return new Item_func_year(a);
+}
+
+Item *create_load_file(Item* a)
+{
+ return new Item_load_file(a);
+}
diff --git a/sql/item_create.h b/sql/item_create.h
new file mode 100644
index 00000000000..aa617946d98
--- /dev/null
+++ b/sql/item_create.h
@@ -0,0 +1,88 @@
+/* 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 */
+
+/* Functions to create an item. Used by lex.h */
+
+Item *create_func_abs(Item* a);
+Item *create_func_acos(Item* a);
+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_ceiling(Item* a);
+Item *create_func_char_length(Item* a);
+Item *create_func_connection_id(void);
+Item *create_func_conv(Item* a, Item *b, Item *c);
+Item *create_func_cos(Item* a);
+Item *create_func_cot(Item* a);
+Item *create_func_date_format(Item* a,Item *b);
+Item *create_func_dayname(Item* a);
+Item *create_func_dayofmonth(Item* a);
+Item *create_func_dayofweek(Item* a);
+Item *create_func_dayofyear(Item* a);
+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_from_days(Item* a);
+Item *create_func_get_lock(Item* a, Item *b);
+Item *create_func_hex(Item *a);
+Item *create_func_inet_aton(Item* a);
+Item *create_func_inet_ntoa(Item* a);
+
+Item *create_func_ifnull(Item* a, Item *b);
+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_locate(Item* a, Item *b);
+Item *create_func_log(Item* a);
+Item *create_func_log10(Item* a);
+Item *create_func_lpad(Item* a, Item *b, Item *c);
+Item *create_func_ltrim(Item* a);
+Item *create_func_md5(Item* a);
+Item *create_func_mod(Item* a, Item *b);
+Item *create_func_monthname(Item* a);
+Item *create_func_nullif(Item* a, Item *b);
+Item *create_func_oct(Item *);
+Item *create_func_ord(Item* a);
+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_quarter(Item* a);
+Item *create_func_radians(Item *a);
+Item *create_func_release_lock(Item* a);
+Item *create_func_repeat(Item* a, Item *b);
+Item *create_func_reverse(Item* a);
+Item *create_func_rpad(Item* a, Item *b, Item *c);
+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_soundex(Item* a);
+Item *create_func_space(Item *);
+Item *create_func_sqrt(Item* a);
+Item *create_func_strcmp(Item* a, Item *b);
+Item *create_func_tan(Item* a);;
+Item *create_func_time_format(Item *a, Item *b);
+Item *create_func_time_to_sec(Item* a);
+Item *create_func_to_days(Item* a);
+Item *create_func_truncate (Item *a, Item *b);
+Item *create_func_ucase(Item* a);
+Item *create_func_version(void);
+Item *create_func_weekday(Item* a);
+Item *create_load_file(Item* a);
diff --git a/sql/item_func.cc b/sql/item_func.cc
new file mode 100644
index 00000000000..b5e9a0c4035
--- /dev/null
+++ b/sql/item_func.cc
@@ -0,0 +1,1981 @@
+/* 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 */
+
+
+/* This file defines all numerical functions */
+
+#ifdef __GNUC__
+#pragma implementation // gcc: Class implementation
+#endif
+
+#include "mysql_priv.h"
+#include <m_ctype.h>
+#include <hash.h>
+#include <time.h>
+#include <ft_global.h>
+
+/* return TRUE if item is a constant */
+
+bool
+eval_const_cond(COND *cond)
+{
+ return ((Item_func*) cond)->val_int() ? TRUE : FALSE;
+}
+
+
+Item_func::Item_func(List<Item> &list)
+{
+ arg_count=list.elements;
+ if ((args=(Item**) sql_alloc(sizeof(Item*)*arg_count)))
+ {
+ uint i=0;
+ List_iterator<Item> li(list);
+ Item *item;
+
+ while ((item=li++))
+ {
+ args[i++]= item;
+ with_sum_func|=item->with_sum_func;
+ }
+ }
+ list.empty(); // Fields are used
+}
+
+bool
+Item_func::fix_fields(THD *thd,TABLE_LIST *tables)
+{
+ Item **arg,**arg_end;
+ char buff[sizeof(double)]; // Max argument in function
+ binary=0;
+ used_tables_cache=0;
+ const_item_cache=1;
+
+ if (thd && check_stack_overrun(thd,buff))
+ return 0; // Fatal error if flag is set!
+ if (arg_count)
+ { // Print purify happy
+ for (arg=args, arg_end=args+arg_count; arg != arg_end ; arg++)
+ {
+ if ((*arg)->fix_fields(thd,tables))
+ return 1; /* purecov: inspected */
+ if ((*arg)->maybe_null)
+ maybe_null=1;
+ if ((*arg)->binary)
+ binary=1;
+ with_sum_func= with_sum_func || (*arg)->with_sum_func;
+ used_tables_cache|=(*arg)->used_tables();
+ const_item_cache&= (*arg)->const_item();
+ }
+ }
+ fix_length_and_dec();
+ return 0;
+}
+
+
+void Item_func::split_sum_func(List<Item> &fields)
+{
+ Item **arg,**arg_end;
+ for (arg=args, arg_end=args+arg_count; arg != arg_end ; arg++)
+ {
+ if ((*arg)->with_sum_func && (*arg)->type() != SUM_FUNC_ITEM)
+ (*arg)->split_sum_func(fields);
+ else if ((*arg)->used_tables() || (*arg)->type() == SUM_FUNC_ITEM)
+ {
+ fields.push_front(*arg);
+ *arg=new Item_ref((Item**) fields.head_ref(),0,(*arg)->name);
+ }
+ }
+}
+
+
+void Item_func::update_used_tables()
+{
+ used_tables_cache=0;
+ const_item_cache=1;
+ for (uint i=0 ; i < arg_count ; i++)
+ {
+ args[i]->update_used_tables();
+ used_tables_cache|=args[i]->used_tables();
+ const_item_cache&=args[i]->const_item();
+ }
+}
+
+
+table_map Item_func::used_tables() const
+{
+ return used_tables_cache;
+}
+
+void Item_func::print(String *str)
+{
+ str->append(func_name());
+ str->append('(');
+ for (uint i=0 ; i < arg_count ; i++)
+ {
+ if (i)
+ str->append(',');
+ args[i]->print(str);
+ }
+ str->append(')');
+}
+
+
+void Item_func::print_op(String *str)
+{
+ str->append('(');
+ for (uint i=0 ; i < arg_count-1 ; i++)
+ {
+ args[i]->print(str);
+ str->append(' ');
+ str->append(func_name());
+ str->append(' ');
+ }
+ args[arg_count-1]->print(str);
+ str->append(')');
+}
+
+bool Item_func::eq(const Item *item) const
+{
+ /* Assume we don't have rtti */
+ if (this == item)
+ return 1;
+ if (item->type() != FUNC_ITEM)
+ return 0;
+ Item_func *item_func=(Item_func*) item;
+ if (arg_count != item_func->arg_count ||
+ func_name() != item_func->func_name())
+ return 0;
+ for (uint i=0; i < arg_count ; i++)
+ if (!args[i]->eq(item_func->args[i]))
+ return 0;
+ return 1;
+}
+
+
+String *Item_real_func::val_str(String *str)
+{
+ double nr=val();
+ if (null_value)
+ return 0; /* purecov: inspected */
+ else
+ str->set(nr,decimals);
+ return str;
+}
+
+
+String *Item_num_func::val_str(String *str)
+{
+ if (hybrid_type == INT_RESULT)
+ {
+ longlong nr=val_int();
+ if (null_value)
+ return 0; /* purecov: inspected */
+ else
+ str->set(nr);
+ }
+ else
+ {
+ double nr=val();
+ if (null_value)
+ return 0; /* purecov: inspected */
+ else
+ str->set(nr,decimals);
+ }
+ return str;
+}
+
+
+void Item_func::fix_num_length_and_dec()
+{
+ decimals=0;
+ for (uint i=0 ; i < arg_count ; i++)
+ set_if_bigger(decimals,args[i]->decimals);
+ max_length=float_length(decimals);
+}
+
+
+String *Item_int_func::val_str(String *str)
+{
+ longlong nr=val_int();
+ if (null_value)
+ return 0;
+ else
+ str->set(nr);
+ return str;
+}
+
+/* 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;
+}
+
+String *Item_num_op::val_str(String *str)
+{
+ if (hybrid_type == INT_RESULT)
+ {
+ longlong nr=val_int();
+ if (null_value)
+ return 0; /* purecov: inspected */
+ else
+ str->set(nr);
+ }
+ else
+ {
+ double nr=val();
+ if (null_value)
+ return 0; /* purecov: inspected */
+ else
+ str->set(nr,decimals);
+ }
+ return str;
+}
+
+
+double Item_func_plus::val()
+{
+ double value=args[0]->val()+args[1]->val();
+ if ((null_value=args[0]->null_value || args[1]->null_value))
+ return 0.0;
+ return value;
+}
+
+longlong Item_func_plus::val_int()
+{
+ longlong value=args[0]->val_int()+args[1]->val_int();
+ if ((null_value=args[0]->null_value || args[1]->null_value))
+ return 0;
+ return value;
+}
+
+double Item_func_minus::val()
+{
+ double value=args[0]->val() - args[1]->val();
+ if ((null_value=args[0]->null_value || args[1]->null_value))
+ return 0.0;
+ return value;
+}
+
+longlong Item_func_minus::val_int()
+{
+ longlong value=args[0]->val_int() - args[1]->val_int();
+ if ((null_value=args[0]->null_value || args[1]->null_value))
+ return 0;
+ return value;
+}
+
+double Item_func_mul::val()
+{
+ double value=args[0]->val()*args[1]->val();
+ if ((null_value=args[0]->null_value || args[1]->null_value))
+ return 0.0; /* purecov: inspected */
+ return value;
+}
+
+longlong Item_func_mul::val_int()
+{
+ longlong value=args[0]->val_int()*args[1]->val_int();
+ if ((null_value=args[0]->null_value || args[1]->null_value))
+ return 0; /* purecov: inspected */
+ return value;
+}
+
+
+double Item_func_div::val()
+{
+ double value=args[0]->val();
+ double val2=args[1]->val();
+ if ((null_value= val2 == 0.0 || args[0]->null_value || args[1]->null_value))
+ return 0.0;
+ return value/val2;
+}
+
+longlong Item_func_div::val_int()
+{
+ longlong value=args[0]->val_int();
+ longlong val2=args[1]->val_int();
+ if ((null_value= val2 == 0 || args[0]->null_value || args[1]->null_value))
+ return 0;
+ return value/val2;
+}
+
+void Item_func_div::fix_length_and_dec()
+{
+ decimals=max(args[0]->decimals,args[1]->decimals)+2;
+ max_length=args[0]->max_length - args[0]->decimals + decimals;
+ uint tmp=float_length(decimals);
+ set_if_smaller(max_length,tmp);
+ maybe_null=1;
+}
+
+double Item_func_mod::val()
+{
+ double value= floor(args[0]->val()+0.5);
+ double val2=floor(args[1]->val()+0.5);
+ if ((null_value=val2 == 0.0 || args[0]->null_value || args[1]->null_value))
+ return 0.0; /* purecov: inspected */
+ return fmod(value,val2);
+}
+
+longlong Item_func_mod::val_int()
+{
+ longlong value= args[0]->val_int();
+ longlong val2= args[1]->val_int();
+ if ((null_value=val2 == 0 || args[0]->null_value || args[1]->null_value))
+ return 0; /* purecov: inspected */
+ return value % val2;
+}
+
+void Item_func_mod::fix_length_and_dec()
+{
+ max_length=args[1]->max_length;
+ decimals=0;
+ maybe_null=1;
+ find_num_type();
+}
+
+
+double Item_func_neg::val()
+{
+ double value=args[0]->val();
+ null_value=args[0]->null_value;
+ return -value;
+}
+
+longlong Item_func_neg::val_int()
+{
+ longlong value=args[0]->val_int();
+ null_value=args[0]->null_value;
+ return -value;
+}
+
+void Item_func_neg::fix_length_and_dec()
+{
+ decimals=args[0]->decimals;
+ max_length=args[0]->max_length;
+ hybrid_type= args[0]->result_type() == INT_RESULT ? INT_RESULT : REAL_RESULT;
+}
+
+double Item_func_abs::val()
+{
+ double value=args[0]->val();
+ null_value=args[0]->null_value;
+ return fabs(value);
+}
+
+longlong Item_func_abs::val_int()
+{
+ longlong value=args[0]->val_int();
+ null_value=args[0]->null_value;
+ return value >= 0 ? value : -value;
+}
+
+void Item_func_abs::fix_length_and_dec()
+{
+ decimals=args[0]->decimals;
+ max_length=args[0]->max_length;
+ hybrid_type= args[0]->result_type() == INT_RESULT ? INT_RESULT : REAL_RESULT;
+}
+
+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 log(value);
+}
+
+double Item_func_log10::val()
+{
+ double value=args[0]->val();
+ if ((null_value=(args[0]->null_value || value <= 0.0)))
+ return 0.0; /* purecov: inspected */
+ return log10(value);
+}
+
+double Item_func_exp::val()
+{
+ double value=args[0]->val();
+ if ((null_value=args[0]->null_value))
+ return 0.0; /* purecov: inspected */
+ return exp(value);
+}
+
+double Item_func_sqrt::val()
+{
+ double value=args[0]->val();
+ if ((null_value=(args[0]->null_value || value < 0)))
+ return 0.0; /* purecov: inspected */
+ return sqrt(value);
+}
+
+double Item_func_pow::val()
+{
+ double value=args[0]->val();
+ double val2=args[1]->val();
+ if ((null_value=(args[0]->null_value || args[1]->null_value)))
+ return 0.0; /* purecov: inspected */
+ return pow(value,val2);
+}
+
+// Trigonometric functions
+
+double Item_func_acos::val()
+{
+ double value=args[0]->val();
+ if ((null_value=(args[0]->null_value || (value < -1.0 || value > 1.0))))
+ return 0.0;
+ return fix_result(acos(value));
+}
+
+double Item_func_asin::val()
+{
+ double value=args[0]->val();
+ if ((null_value=(args[0]->null_value || (value < -1.0 || value > 1.0))))
+ return 0.0;
+ return fix_result(asin(value));
+}
+
+double Item_func_atan::val()
+{
+ double value=args[0]->val();
+ if ((null_value=args[0]->null_value))
+ return 0.0;
+ if (arg_count == 2)
+ {
+ double val2= args[1]->val();
+ if ((null_value=args[1]->null_value))
+ return 0.0;
+ return fix_result(atan2(value,val2));
+ }
+ return fix_result(atan(value));
+}
+
+double Item_func_cos::val()
+{
+ double value=args[0]->val();
+ if ((null_value=args[0]->null_value))
+ return 0.0;
+ return fix_result(cos(value));
+}
+
+double Item_func_sin::val()
+{
+ double value=args[0]->val();
+ if ((null_value=args[0]->null_value))
+ return 0.0;
+ return fix_result(sin(value));
+}
+
+double Item_func_tan::val()
+{
+ double value=args[0]->val();
+ if ((null_value=args[0]->null_value))
+ return 0.0;
+ return fix_result(tan(value));
+}
+
+
+// Shift-functions, same as << and >> in C/C++
+
+
+longlong Item_func_shift_left::val_int()
+{
+ uint shift;
+ ulonglong res= ((ulonglong) args[0]->val_int() <<
+ (shift=(uint) args[1]->val_int()));
+ if (args[0]->null_value || args[1]->null_value)
+ {
+ null_value=1;
+ return 0;
+ }
+ null_value=0;
+ return (shift < sizeof(longlong)*8 ? (longlong) res : LL(0));
+}
+
+longlong Item_func_shift_right::val_int()
+{
+ uint shift;
+ ulonglong res= (ulonglong) args[0]->val_int() >>
+ (shift=(uint) args[1]->val_int());
+ if (args[0]->null_value || args[1]->null_value)
+ {
+ null_value=1;
+ return 0;
+ }
+ null_value=0;
+ return (shift < sizeof(longlong)*8 ? (longlong) res : LL(0));
+}
+
+
+longlong Item_func_bit_neg::val_int()
+{
+ ulonglong res= (ulonglong) args[0]->val_int();
+ if ((null_value=args[0]->null_value))
+ return 0;
+ return ~res;
+}
+
+
+// Conversion functions
+
+void Item_func_integer::fix_length_and_dec()
+{
+ max_length=args[0]->max_length - args[0]->decimals+1;
+ uint tmp=float_length(decimals);
+ set_if_smaller(max_length,tmp);
+ decimals=0;
+}
+
+longlong Item_func_ceiling::val_int()
+{
+ double value=args[0]->val();
+ null_value=args[0]->null_value;
+ return (longlong) ceil(value);
+}
+
+longlong Item_func_floor::val_int()
+{
+ double value=args[0]->val();
+ null_value=args[0]->null_value;
+ return (longlong) floor(value);
+}
+
+void Item_func_round::fix_length_and_dec()
+{
+ max_length=args[0]->max_length;
+ decimals=args[0]->decimals;
+ if (args[1]->const_item())
+ {
+ int tmp=(int) args[1]->val_int();
+ if (tmp < 0)
+ decimals=0;
+ else
+ decimals=tmp;
+ }
+}
+
+double Item_func_round::val()
+{
+ double value=args[0]->val();
+ int dec=(int) args[1]->val_int();
+ uint abs_dec=abs(dec);
+
+ if ((null_value=args[0]->null_value || args[1]->null_value))
+ return 0.0;
+ double tmp=(abs_dec < array_elements(log_10) ?
+ log_10[abs_dec] : pow(10.0,(double) abs_dec));
+
+ if (truncate)
+ return dec < 0 ? floor(value/tmp)*tmp : floor(value*tmp)/tmp;
+ return dec < 0 ? rint(value/tmp)*tmp : rint(value*tmp)/tmp;
+}
+
+
+double Item_func_rand::val()
+{
+ if (arg_count)
+ { // Only use argument once in query
+ ulong tmp=((ulong) args[0]->val_int())+55555555L;
+ randominit(&current_thd->rand,tmp,tmp/2);
+#ifdef DELETE_ITEMS
+ delete args[0];
+#endif
+ arg_count=0;
+ }
+ return rnd(&current_thd->rand);
+}
+
+longlong Item_func_sign::val_int()
+{
+ double value=args[0]->val();
+ null_value=args[0]->null_value;
+ return value < 0.0 ? -1 : (value > 0 ? 1 : 0);
+}
+
+
+double Item_func_units::val()
+{
+ double value=args[0]->val();
+ if ((null_value=args[0]->null_value))
+ return 0;
+ return value*mul+add;
+}
+
+
+void Item_func_min_max::fix_length_and_dec()
+{
+ decimals=0;
+ max_length=0;
+ maybe_null=1;
+ binary=0;
+ cmp_type=args[0]->result_type();
+ for (uint i=0 ; i < arg_count ; i++)
+ {
+ if (max_length < args[i]->max_length)
+ max_length=args[i]->max_length;
+ if (decimals < args[i]->decimals)
+ decimals=args[i]->decimals;
+ if (!args[i]->maybe_null)
+ maybe_null=0;
+ cmp_type=item_cmp_type(cmp_type,args[i]->result_type());
+ if (args[i]->binary)
+ binary=1;
+ }
+}
+
+
+String *Item_func_min_max::val_str(String *str)
+{
+ switch (cmp_type) {
+ case INT_RESULT:
+ {
+ longlong nr=val_int();
+ if (null_value)
+ return 0;
+ else
+ str->set(nr);
+ return str;
+ }
+ case REAL_RESULT:
+ {
+ double nr=val();
+ if (null_value)
+ return 0; /* purecov: inspected */
+ else
+ str->set(nr,decimals);
+ return str;
+ }
+ case STRING_RESULT:
+ {
+ String *res;
+ LINT_INIT(res);
+ null_value=1;
+ for (uint i=0; i < arg_count ; i++)
+ {
+ if (null_value)
+ {
+ res=args[i]->val_str(str);
+ null_value=args[i]->null_value;
+ }
+ else
+ {
+ String *res2;
+ res2= args[i]->val_str(res == str ? &tmp_value : str);
+ if (res2)
+ {
+ int cmp=binary ? stringcmp(res,res2) : sortcmp(res,res2);
+ if ((cmp_sign < 0 ? cmp : -cmp) < 0)
+ res=res2;
+ }
+ }
+ }
+ return res;
+ }
+ }
+ return 0; // Keep compiler happy
+}
+
+
+double Item_func_min_max::val()
+{
+ double value=0.0;
+ null_value=1;
+ for (uint i=0; i < arg_count ; i++)
+ {
+ if (null_value)
+ {
+ value=args[i]->val();
+ null_value=args[i]->null_value;
+ }
+ else
+ {
+ double tmp=args[i]->val();
+ if (!args[i]->null_value && (tmp < value ? cmp_sign : -cmp_sign) > 0)
+ value=tmp;
+ }
+ }
+ return value;
+}
+
+
+longlong Item_func_min_max::val_int()
+{
+ longlong value=0;
+ null_value=1;
+ for (uint i=0; i < arg_count ; i++)
+ {
+ if (null_value)
+ {
+ value=args[i]->val_int();
+ null_value=args[i]->null_value;
+ }
+ else
+ {
+ longlong tmp=args[i]->val_int();
+ if (!args[i]->null_value && (tmp < value ? cmp_sign : -cmp_sign) > 0)
+ value=tmp;
+ }
+ }
+ return value;
+}
+
+
+longlong Item_func_length::val_int()
+{
+ String *res=args[0]->val_str(&value);
+ if (!res)
+ {
+ null_value=1;
+ return 0; /* purecov: inspected */
+ }
+ null_value=0;
+ return (longlong) res->length();
+}
+
+longlong Item_func_char_length::val_int()
+{
+ String *res=args[0]->val_str(&value);
+ if (!res)
+ {
+ null_value=1;
+ return 0; /* purecov: inspected */
+ }
+ null_value=0;
+ return (longlong) (!args[0]->binary) ? res->numchars() : res->length();
+}
+
+
+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;
+ return 0; /* purecov: inspected */
+ }
+ null_value=0;
+ uint start=0,start0=0;
+ if (arg_count == 3)
+ {
+ start=(uint) args[2]->val_int()-1;
+#ifdef USE_MB
+ if (use_mb(default_charset_info))
+ {
+ start0=start;
+ if (!binary_str)
+ start=a->charpos(start);
+ }
+#endif
+ if (start > a->length() || start+b->length() > a->length())
+ return 0;
+ }
+ if (!b->length()) // Found empty string at start
+ return (longlong) (start+1);
+#ifdef USE_MB
+ if (use_mb(default_charset_info) && !binary_str)
+ {
+ const char *ptr=a->ptr()+start;
+ const char *search=b->ptr();
+ const char *strend = ptr+a->length();
+ const char *end=strend-b->length()+1;
+ const char *search_end=search+b->length();
+ register uint32 l;
+ while (ptr < end)
+ {
+ if (*ptr == *search)
+ {
+ register char *i,*j;
+ i=(char*) ptr+1; j=(char*) search+1;
+ while (j != search_end)
+ if (*i++ != *j++) goto skipp;
+ return (longlong) start0+1;
+ }
+ skipp:
+ if ((l=my_ismbchar(default_charset_info,ptr,strend))) ptr+=l;
+ else ++ptr;
+ ++start0;
+ }
+ return 0;
+ }
+#endif /* USE_MB */
+ return (longlong) (a->strstr(*b,start)+1) ;
+}
+
+
+longlong Item_func_field::val_int()
+{
+ String *field;
+ if (!(field=item->val_str(&value)))
+ return 0; // -1 if null ?
+ for (uint i=0 ; i < arg_count ; i++)
+ {
+ String *tmp_value=args[i]->val_str(&tmp);
+ if (tmp_value && field->length() == tmp_value->length() &&
+ !memcmp(field->ptr(),tmp_value->ptr(),tmp_value->length()))
+ return (longlong) (i+1);
+ }
+ return 0;
+}
+
+
+longlong Item_func_ascii::val_int()
+{
+ String *res=args[0]->val_str(&value);
+ if (!res)
+ {
+ null_value=1;
+ return 0;
+ }
+ null_value=0;
+ return (longlong) (res->length() ? (uchar) (*res)[0] : (uchar) 0);
+}
+
+longlong Item_func_ord::val_int()
+{
+ String *res=args[0]->val_str(&value);
+ if (!res)
+ {
+ null_value=1;
+ return 0;
+ }
+ null_value=0;
+ if (!res->length()) return 0;
+#ifdef USE_MB
+ if (use_mb(default_charset_info) && !args[0]->binary)
+ {
+ register const char *str=res->ptr();
+ register uint32 n=0, l=my_ismbchar(default_charset_info,
+ str,str+res->length());
+ if (!l) return (longlong)((uchar) *str);
+ while (l--)
+ n=(n<<8)|(uint32)((uchar) *str++);
+ return (longlong) n;
+ }
+#endif
+ return (longlong) ((uchar) (*res)[0]);
+}
+
+ /* Search after a string in a string of strings separated by ',' */
+ /* Returns number of found type >= 1 or 0 if not found */
+ /* This optimizes searching in enums to bit testing! */
+
+void Item_func_find_in_set::fix_length_and_dec()
+{
+ decimals=0;
+ max_length=3; // 1-999
+ if (args[0]->const_item() && args[1]->type() == FIELD_ITEM)
+ {
+ Field *field= ((Item_field*) args[1])->field;
+ if (field->real_type() == FIELD_TYPE_SET)
+ {
+ String *find=args[0]->val_str(&value);
+ if (find)
+ {
+ enum_value=find_enum(((Field_enum*) field)->typelib,find->ptr(),
+ find->length());
+ enum_bit=0;
+ if (enum_value)
+ enum_bit=LL(1) << (enum_value-1);
+ }
+ }
+ }
+}
+
+static const char separator=',';
+
+longlong Item_func_find_in_set::val_int()
+{
+ if (enum_value)
+ {
+ ulonglong tmp=(ulonglong) args[1]->val_int();
+ if (!(null_value=args[1]->null_value || args[0]->null_value))
+ {
+ if (tmp & enum_bit)
+ return enum_value;
+ }
+ return 0L;
+ }
+
+ String *find=args[0]->val_str(&value);
+ String *buffer=args[1]->val_str(&value2);
+ if (!find || !buffer)
+ {
+ null_value=1;
+ return 0; /* purecov: inspected */
+ }
+ null_value=0;
+
+ int diff;
+ if ((diff=buffer->length() - find->length()) >= 0)
+ {
+ const char *f_pos=find->ptr();
+ const char *f_end=f_pos+find->length();
+ const char *str=buffer->ptr();
+ const char *end=str+diff+1;
+ const char *real_end=str+buffer->length();
+ uint position=1;
+ do
+ {
+ const char *pos= f_pos;
+ while (pos != f_end)
+ {
+ if (toupper(*str) != toupper(*pos))
+ goto not_found;
+ str++;
+ pos++;
+ }
+ if (str == real_end || str[0] == separator)
+ return (longlong) position;
+ not_found:
+ while (str < end && str[0] != separator)
+ str++;
+ position++;
+ } while (++str <= end);
+ }
+ return 0;
+}
+
+static char nbits[256] = {
+ 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8,
+};
+
+uint count_bits(ulonglong v)
+{
+#if SIZEOF_LONG_LONG > 4
+ /* The following code is a bit faster on 16 bit machines than if we would
+ only shift v */
+ ulong v2=(ulong) (v >> 32);
+ return (uint) (uchar) (nbits[(uchar) v] +
+ nbits[(uchar) (v >> 8)] +
+ nbits[(uchar) (v >> 16)] +
+ nbits[(uchar) (v >> 24)] +
+ nbits[(uchar) (v2)] +
+ nbits[(uchar) (v2 >> 8)] +
+ nbits[(uchar) (v2 >> 16)] +
+ nbits[(uchar) (v2 >> 24)]);
+#else
+ return (uint) (uchar) (nbits[(uchar) v] +
+ nbits[(uchar) (v >> 8)] +
+ nbits[(uchar) (v >> 16)] +
+ nbits[(uchar) (v >> 24)]);
+#endif
+}
+
+longlong Item_func_bit_count::val_int()
+{
+ ulonglong value= (ulonglong) args[0]->val_int();
+ if (args[0]->null_value)
+ {
+ null_value=1; /* purecov: inspected */
+ return 0; /* purecov: inspected */
+ }
+ return (longlong) count_bits(value);
+}
+
+
+/****************************************************************************
+** Functions to handle dynamic loadable functions
+** Original source by: Alexis Mikhailov <root@medinf.chuvashia.su>
+** Rewritten by: Monty.
+****************************************************************************/
+
+#ifdef HAVE_DLOPEN
+
+udf_handler::~udf_handler()
+{
+ if (initialized)
+ {
+ if (u_d->func_deinit != NULL)
+ {
+ void (*deinit)(UDF_INIT *) = (void (*)(UDF_INIT*))
+ u_d->func_deinit;
+ (*deinit)(&initid);
+ }
+ free_udf(u_d);
+ }
+ delete [] buffers;
+}
+
+
+bool
+udf_handler::fix_fields(THD *thd,TABLE_LIST *tables,Item_result_field *func,
+ uint arg_count, Item **arguments)
+{
+ char buff[sizeof(double)]; // Max argument in function
+ DBUG_ENTER("Item_udf_func::fix_fields");
+
+ if (thd && check_stack_overrun(thd,buff))
+ return 0; // Fatal error flag is set!
+
+ udf_func *tmp_udf=find_udf(u_d->name,strlen(u_d->name),1);
+
+ if (!tmp_udf)
+ {
+ my_printf_error(ER_CANT_FIND_UDF,ER(ER_CANT_FIND_UDF),MYF(0),u_d->name,
+ errno);
+ DBUG_RETURN(1);
+ }
+ u_d=tmp_udf;
+ args=arguments;
+
+ /* Fix all arguments */
+ func->binary=func->maybe_null=0;
+ used_tables_cache=0;
+ const_item_cache=1;
+
+ if ((f_args.arg_count=arg_count))
+ {
+ if (!(f_args.arg_type= (Item_result*)
+ sql_alloc(f_args.arg_count*sizeof(Item_result))))
+
+ {
+ free_udf(u_d);
+ DBUG_RETURN(1);
+ }
+ uint i;
+ Item **arg,**arg_end;
+ for (i=0, arg=arguments, arg_end=arguments+arg_count;
+ arg != arg_end ;
+ arg++,i++)
+ {
+ if ((*arg)->fix_fields(thd,tables))
+ return 1;
+ if ((*arg)->binary)
+ func->binary=1;
+ if ((*arg)->maybe_null)
+ func->maybe_null=1;
+ func->with_sum_func= func->with_sum_func || (*arg)->with_sum_func;
+ used_tables_cache|=(*arg)->used_tables();
+ const_item_cache&=(*arg)->const_item();
+ f_args.arg_type[i]=(*arg)->result_type();
+ }
+ if (!(buffers=new String[arg_count]) ||
+ !(f_args.args= (char**) sql_alloc(arg_count * sizeof(char *))) ||
+ !(f_args.lengths=(ulong*) sql_alloc(arg_count * sizeof(long))) ||
+ !(f_args.maybe_null=(char*) sql_alloc(arg_count * sizeof(char))) ||
+ !(num_buffer= (char*) sql_alloc(ALIGN_SIZE(sizeof(double))*arg_count)))
+ {
+ free_udf(u_d);
+ DBUG_RETURN(1);
+ }
+ }
+ func->fix_length_and_dec();
+ initid.max_length=func->max_length;
+ initid.maybe_null=func->maybe_null;
+ initid.const_item=const_item_cache;
+ initid.decimals=func->decimals;
+ initid.ptr=0;
+
+ if (u_d->func_init)
+ {
+ char *to=num_buffer;
+ for (uint i=0; i < arg_count; i++)
+ {
+ f_args.args[i]=0;
+ f_args.lengths[i]=arguments[i]->max_length;
+ f_args.maybe_null[i]=(char) arguments[i]->maybe_null;
+
+ switch(arguments[i]->type()) {
+ case Item::STRING_ITEM: // Constant string !
+ {
+ String *res=arguments[i]->val_str((String *) 0);
+ if (arguments[i]->null_value)
+ continue;
+ f_args.args[i]= (char*) res->ptr();
+ break;
+ }
+ case Item::INT_ITEM:
+ *((longlong*) to) = arguments[i]->val_int();
+ if (!arguments[i]->null_value)
+ {
+ f_args.args[i]=to;
+ to+= ALIGN_SIZE(sizeof(longlong));
+ }
+ break;
+ case Item::REAL_ITEM:
+ *((double*) to) = arguments[i]->val();
+ if (!arguments[i]->null_value)
+ {
+ f_args.args[i]=to;
+ to+= ALIGN_SIZE(sizeof(double));
+ }
+ break;
+ default: // Skip these
+ break;
+ }
+ }
+ thd->net.last_error[0]=0;
+ my_bool (*init)(UDF_INIT *, UDF_ARGS *, char *)=
+ (my_bool (*)(UDF_INIT *, UDF_ARGS *, char *))
+ u_d->func_init;
+ if ((error=(uchar) init(&initid, &f_args, thd->net.last_error)))
+ {
+ my_printf_error(ER_CANT_INITIALIZE_UDF,ER(ER_CANT_INITIALIZE_UDF),MYF(0),
+ u_d->name,thd->net.last_error);
+ free_udf(u_d);
+ DBUG_RETURN(1);
+ }
+ func->max_length=min(initid.max_length,MAX_BLOB_WIDTH);
+ func->maybe_null=initid.maybe_null;
+ const_item_cache=initid.const_item;
+ func->decimals=min(initid.decimals,31);
+ }
+ initialized=1;
+ if (error)
+ {
+ my_printf_error(ER_CANT_INITIALIZE_UDF,ER(ER_CANT_INITIALIZE_UDF),MYF(0),
+ u_d->name, ER(ER_UNKNOWN_ERROR));
+ DBUG_RETURN(1);
+ }
+ DBUG_RETURN(0);
+}
+
+
+bool udf_handler::get_arguments()
+{
+ if (error)
+ return 1; // Got an error earlier
+ char *to= num_buffer;
+ uint str_count=0;
+ for (uint i=0; i < f_args.arg_count; i++)
+ {
+ f_args.args[i]=0;
+ switch (f_args.arg_type[i]) {
+ case STRING_RESULT:
+ {
+ String *res=args[i]->val_str(&buffers[str_count++]);
+ if (!(args[i]->null_value))
+ {
+ f_args.args[i]= (char*) res->ptr();
+ f_args.lengths[i]= res->length();
+ break;
+ }
+ }
+ case INT_RESULT:
+ *((longlong*) to) = args[i]->val_int();
+ if (!args[i]->null_value)
+ {
+ f_args.args[i]=to;
+ to+= ALIGN_SIZE(sizeof(longlong));
+ }
+ break;
+ case REAL_RESULT:
+ *((double*) to) = args[i]->val();
+ if (!args[i]->null_value)
+ {
+ f_args.args[i]=to;
+ to+= ALIGN_SIZE(sizeof(double));
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+/* This returns (String*) 0 in case of NULL values */
+
+String *udf_handler::val_str(String *str,String *save_str)
+{
+ uchar is_null=0;
+ ulong res_length;
+
+ if (get_arguments())
+ return 0;
+ char * (*func)(UDF_INIT *, UDF_ARGS *, char *, ulong *, uchar *, uchar *)=
+ (char* (*)(UDF_INIT *, UDF_ARGS *, char *, ulong *, uchar *, uchar *))
+ u_d->func;
+
+ if ((res_length=str->alloced_length()) < MAX_FIELD_WIDTH)
+ { // This happens VERY seldom
+ if (str->alloc(MAX_FIELD_WIDTH))
+ {
+ error=1;
+ return 0;
+ }
+ }
+ char *res=func(&initid, &f_args, (char*) str->ptr(), &res_length, &is_null,
+ &error);
+ if (is_null || !res || error) // The !res is for safety
+ {
+ return 0;
+ }
+ if (res == str->ptr())
+ {
+ str->length(res_length);
+ return str;
+ }
+ save_str->set(res, res_length);
+ return save_str;
+}
+
+
+
+double Item_func_udf_float::val()
+{
+ DBUG_ENTER("Item_func_udf_float::val");
+ DBUG_PRINT("info",("result_type: %d arg_count: %d",
+ args[0]->result_type(), arg_count));
+ DBUG_RETURN(udf.val(&null_value));
+}
+
+
+String *Item_func_udf_float::val_str(String *str)
+{
+ double nr=val();
+ if (null_value)
+ return 0; /* purecov: inspected */
+ else
+ str->set(nr,decimals);
+ return str;
+}
+
+
+longlong Item_func_udf_int::val_int()
+{
+ DBUG_ENTER("Item_func_udf_int::val_int");
+ DBUG_PRINT("info",("result_type: %d arg_count: %d",
+ args[0]->result_type(), arg_count));
+
+ DBUG_RETURN(udf.val_int(&null_value));
+}
+
+
+String *Item_func_udf_int::val_str(String *str)
+{
+ longlong nr=val_int();
+ if (null_value)
+ return 0;
+ else
+ str->set(nr);
+ return str;
+}
+
+/* Default max_length is max argument length */
+
+void Item_func_udf_str::fix_length_and_dec()
+{
+ DBUG_ENTER("Item_func_udf_str::fix_length_and_dec");
+ max_length=0;
+ for (uint i = 0; i < arg_count; i++)
+ set_if_bigger(max_length,args[i]->max_length);
+ DBUG_VOID_RETURN;
+}
+
+String *Item_func_udf_str::val_str(String *str)
+{
+ String *res=udf.val_str(str,&str_value);
+ null_value = !res;
+ return res;
+}
+
+#else
+bool udf_handler::get_arguments() { return 0; }
+#endif /* HAVE_DLOPEN */
+
+/*
+** User level locks
+*/
+
+pthread_mutex_t LOCK_user_locks;
+static HASH hash_user_locks;
+
+class ULL
+{
+ char *key;
+ uint key_length;
+
+public:
+ int count;
+ bool locked;
+ pthread_cond_t cond;
+ pthread_t thread;
+
+ ULL(const char *key_arg,uint length) :key_length(length),count(1),locked(1)
+ {
+ key=(char*) my_memdup((byte*) key_arg,length,MYF(0));
+ pthread_cond_init(&cond,NULL);
+ if (key)
+ {
+ if (hash_insert(&hash_user_locks,(byte*) this))
+ {
+ my_free((gptr) key,MYF(0));
+ key=0;
+ }
+ }
+ }
+ ~ULL()
+ {
+ if (key)
+ {
+ hash_delete(&hash_user_locks,(byte*) this);
+ my_free((gptr) key,MYF(0));
+ }
+ pthread_cond_destroy(&cond);
+ }
+ inline bool initialized() { return key != 0; }
+ friend void item_user_lock_release(ULL *ull);
+ friend char *ull_get_key(const ULL *ull,uint *length,my_bool not_used);
+};
+
+char *ull_get_key(const ULL *ull,uint *length,
+ my_bool not_used __attribute__((unused)))
+{
+ *length=(uint) ull->key_length;
+ return (char*) ull->key;
+}
+
+void item_user_lock_init(void)
+{
+ pthread_mutex_init(&LOCK_user_locks,NULL);
+ hash_init(&hash_user_locks,16,0,0,(hash_get_key) ull_get_key,NULL,0);
+}
+
+void item_user_lock_free(void)
+{
+ hash_free(&hash_user_locks);
+}
+
+void item_user_lock_release(ULL *ull)
+{
+ ull->locked=0;
+ if (--ull->count)
+ pthread_cond_signal(&ull->cond);
+ else
+ delete ull;
+}
+
+/*
+ Get a user level lock. If the thread has an old lock this is first released.
+ Returns 1: Got lock
+ Returns 0: Timeout
+ Returns NULL: Error
+*/
+
+longlong Item_func_get_lock::val_int()
+{
+ String *res=args[0]->val_str(&value);
+ longlong timeout=args[1]->val_int();
+ struct timespec abstime;
+ THD *thd=current_thd;
+ ULL *ull;
+ int error;
+
+ pthread_mutex_lock(&LOCK_user_locks);
+
+ if (!res || !res->length())
+ {
+ pthread_mutex_unlock(&LOCK_user_locks);
+ null_value=1;
+ return 0;
+ }
+ null_value=0;
+
+ if (thd->ull)
+ {
+ item_user_lock_release(thd->ull);
+ thd->ull=0;
+ }
+
+ if (!(ull= ((ULL*) hash_search(&hash_user_locks,(byte*) res->ptr(),
+ res->length()))))
+ {
+ ull=new ULL(res->ptr(),res->length());
+ if (!ull || !ull->initialized())
+ {
+ delete ull;
+ pthread_mutex_unlock(&LOCK_user_locks);
+ null_value=1; // Probably out of memory
+ return 0;
+ }
+ ull->thread=thd->real_id;
+ thd->ull=ull;
+ pthread_mutex_unlock(&LOCK_user_locks);
+ return 1; // Got new lock
+ }
+ ull->count++;
+
+ /* structure is now initialized. Try to get the lock */
+ /* Set up control struct to allow others to abort locks */
+ pthread_mutex_lock(&thd->mysys_var->mutex);
+ thd->proc_info="User lock";
+ thd->mysys_var->current_mutex= &LOCK_user_locks;
+ thd->mysys_var->current_cond= &ull->cond;
+ pthread_mutex_unlock(&thd->mysys_var->mutex);
+
+ abstime.tv_sec=time((time_t*) 0)+(time_t) timeout;
+ abstime.tv_nsec=0;
+ while ((error=pthread_cond_timedwait(&ull->cond,&LOCK_user_locks,&abstime))
+ != ETIME && error != ETIMEDOUT && ull->locked)
+ {
+ if (thd->killed || abort_loop)
+ {
+ error=EINTR; // Return NULL
+ break;
+ }
+ }
+ if (ull->locked)
+ {
+ if (!--ull->count)
+ delete ull; // Should never happen
+ if (error != ETIME && error != ETIMEDOUT)
+ {
+ error=1;
+ null_value=1; // Return NULL
+ }
+ }
+ else
+ {
+ ull->locked=1;
+ ull->thread=thd->real_id;
+ thd->ull=ull;
+ error=0;
+ }
+ 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);
+
+ return !error ? 1 : 0;
+}
+
+
+/*
+** Release a user level lock.
+** Returns 1 if lock released
+** 0 if lock wasn't held
+** NULL if no such lock
+*/
+
+longlong Item_func_release_lock::val_int()
+{
+ String *res=args[0]->val_str(&value);
+ ULL *ull;
+ longlong result;
+ if (!res || !res->length())
+ {
+ null_value=1;
+ return 0;
+ }
+ null_value=0;
+
+ result=0;
+ pthread_mutex_lock(&LOCK_user_locks);
+ if (!(ull= ((ULL*) hash_search(&hash_user_locks,(const byte*) res->ptr(),
+ res->length()))))
+ {
+ null_value=1;
+ }
+ else
+ {
+ if (ull->locked && pthread_equal(pthread_self(),ull->thread))
+ {
+ result=1; // Release is ok
+ item_user_lock_release(ull);
+ current_thd->ull=0;
+ }
+ }
+ pthread_mutex_unlock(&LOCK_user_locks);
+ return result;
+}
+
+
+longlong Item_func_set_last_insert_id::val_int()
+{
+ longlong value=args[0]->val_int();
+ current_thd->insert_id(value);
+ null_value=args[0]->null_value;
+ return value;
+}
+
+/* This function is just used to test speed of different functions */
+
+longlong Item_func_benchmark::val_int()
+{
+ char buff[MAX_FIELD_WIDTH];
+ String tmp(buff,sizeof(buff));
+ THD *thd=current_thd;
+
+ for (ulong loop=0 ; loop < loop_count && !thd->killed; loop++)
+ {
+ switch (args[0]->result_type()) {
+ case REAL_RESULT:
+ (void) args[0]->val();
+ break;
+ case INT_RESULT:
+ (void) args[0]->val_int();
+ break;
+ case STRING_RESULT:
+ (void) args[0]->val_str(&tmp);
+ break;
+ }
+ }
+ return 0;
+}
+
+#define extra_size sizeof(double)
+
+static user_var_entry *get_variable(HASH *hash, LEX_STRING &name,
+ bool create_if_not_exists)
+{
+ user_var_entry *entry;
+
+ if (!(entry = (user_var_entry*) hash_search(hash, (byte*) name.str,
+ name.length)) &&
+ create_if_not_exists)
+ {
+ uint size=ALIGN_SIZE(sizeof(user_var_entry))+name.length+1+extra_size;
+ if (!hash_inited(hash))
+ return 0;
+ if (!(entry = (user_var_entry*) my_malloc(size,MYF(MY_WME))))
+ return 0;
+ entry->name.str=(char*) entry+ ALIGN_SIZE(sizeof(user_var_entry))+
+ extra_size;
+ entry->name.length=name.length;
+ entry->value=0;
+ entry->length=0;
+ entry->type=STRING_RESULT;
+ memcpy(entry->name.str, name.str, name.length+1);
+ if (hash_insert(hash,(byte*) entry))
+ {
+ my_free((char*) entry,MYF(0));
+ return 0;
+ }
+ }
+ return entry;
+}
+
+
+bool Item_func_set_user_var::fix_fields(THD *thd,TABLE_LIST *tables)
+{
+ if (Item_func::fix_fields(thd,tables) ||
+ !(entry= get_variable(&thd->user_vars, name, 1)))
+ return 1;
+ return 0;
+}
+
+
+void
+Item_func_set_user_var::fix_length_and_dec()
+{
+ maybe_null=args[0]->maybe_null;
+ max_length=args[0]->max_length;
+ decimals=args[0]->decimals;
+ cached_result_type=args[0]->result_type();
+}
+
+void Item_func_set_user_var::update_hash(void *ptr, uint length,
+ Item_result type)
+{
+ if ((null_value=args[0]->null_value))
+ {
+ char *pos= (char*) entry+ ALIGN_SIZE(sizeof(user_var_entry));
+ if (entry->value && entry->value != pos)
+ my_free(entry->value,MYF(0));
+ entry->value=0;
+ entry->length=0;
+ }
+ else
+ {
+ if (length <= extra_size)
+ {
+ /* Save value in value struct */
+ char *pos= (char*) entry+ ALIGN_SIZE(sizeof(user_var_entry));
+ if (entry->value != pos)
+ {
+ if (entry->value)
+ my_free(entry->value,MYF(0));
+ entry->value=pos;
+ }
+ }
+ else
+ {
+ /* Allocate variable */
+ if (entry->length != length)
+ {
+ char *pos= (char*) entry+ ALIGN_SIZE(sizeof(user_var_entry));
+ if (entry->value == pos)
+ entry->value=0;
+ if (!(entry->value=(char*) my_realloc(entry->value, length,
+ MYF(MY_ALLOW_ZERO_PTR))))
+ goto err;
+ }
+ }
+ memcpy(entry->value,ptr,length);
+ entry->length= length;
+ entry->type=type;
+ }
+ return;
+
+ err:
+ current_thd->fatal_error=1; // Probably end of memory
+ null_value=1;
+ return;
+}
+
+
+bool
+Item_func_set_user_var::update()
+{
+ switch (cached_result_type) {
+ case REAL_RESULT:
+ (void) val();
+ break;
+ case INT_RESULT:
+ (void) val_int();
+ break;
+ case STRING_RESULT:
+ char buffer[MAX_FIELD_WIDTH];
+ String tmp(buffer,sizeof(buffer));
+ (void) val_str(&tmp);
+ break;
+ }
+ return current_thd->fatal_error;
+}
+
+
+double
+Item_func_set_user_var::val()
+{
+ double value=args[0]->val();
+ update_hash((void*) &value,sizeof(value), REAL_RESULT);
+ return value;
+}
+
+longlong
+Item_func_set_user_var::val_int()
+{
+ longlong value=args[0]->val_int();
+ update_hash((void*) &value,sizeof(longlong),INT_RESULT);
+ return value;
+}
+
+String *
+Item_func_set_user_var::val_str(String *str)
+{
+ String *res=args[0]->val_str(str);
+ if (!res) // Null value
+ update_hash((void*) 0,0,STRING_RESULT);
+ else
+ update_hash(res->c_ptr(),res->length()+1,STRING_RESULT);
+ return res;
+}
+
+
+user_var_entry *Item_func_get_user_var::get_entry()
+{
+ if (!entry || ! entry->value)
+ {
+ null_value=1;
+ return 0;
+ }
+ null_value=0;
+ return entry;
+}
+
+
+String *
+Item_func_get_user_var::val_str(String *str)
+{
+ user_var_entry *entry=get_entry();
+ if (!entry)
+ return NULL;
+ switch (entry->type) {
+ case REAL_RESULT:
+ str->set(*(double*) entry->value);
+ break;
+ case INT_RESULT:
+ str->set(*(longlong*) entry->value);
+ break;
+ case STRING_RESULT:
+ if (str->copy(entry->value, entry->length-1))
+ {
+ null_value=1;
+ return NULL;
+ }
+ break;
+ }
+ return str;
+}
+
+
+double Item_func_get_user_var::val()
+{
+ user_var_entry *entry=get_entry();
+ if (!entry)
+ return 0.0;
+ switch (entry->type) {
+ case REAL_RESULT:
+ return *(double*) entry->value;
+ case INT_RESULT:
+ return (double) *(longlong*) entry->value;
+ case STRING_RESULT:
+ return atof(entry->value); // This is null terminated
+ }
+ return 0.0; // Impossible
+}
+
+
+longlong Item_func_get_user_var::val_int()
+{
+ user_var_entry *entry=get_entry();
+ if (!entry)
+ return LL(0);
+ switch (entry->type) {
+ case REAL_RESULT:
+ return (longlong) *(double*) entry->value;
+ case INT_RESULT:
+ return *(longlong*) entry->value;
+ case STRING_RESULT:
+ return strtoull(entry->value,NULL,10); // String is null terminated
+ }
+ return LL(0); // Impossible
+}
+
+
+void Item_func_get_user_var::fix_length_and_dec()
+{
+ maybe_null=1;
+ decimals=NOT_FIXED_DEC;
+ max_length=MAX_BLOB_WIDTH;
+ entry= get_variable(&current_thd->user_vars, name, 0);
+}
+
+
+enum Item_result Item_func_get_user_var::result_type() const
+{
+ user_var_entry *entry;
+ if (!(entry = (user_var_entry*) hash_search(&current_thd->user_vars,
+ (byte*) name.str,
+ name.length)))
+ return STRING_RESULT;
+ return entry->type;
+}
+
+longlong Item_func_inet_aton::val_int()
+{
+ uint byte_result = 0;
+ ulonglong result = 0; // We are ready for 64 bit addresses
+ const char *p,* end;
+ char c = '.'; // we mark c to indicate invalid IP in case length is 0
+ char buff[36];
+
+ String *s,tmp(buff,sizeof(buff));
+ if (!(s = args[0]->val_str(&tmp))) // If null value
+ goto err;
+ null_value=0;
+
+ end= (p = s->ptr()) + s->length();
+ while (p < end)
+ {
+ c = *p++;
+ int digit = (int) (c - '0'); // Assume ascii
+ if (digit >= 0 && digit <= 9)
+ {
+ if ((byte_result = byte_result * 10 + digit) > 255)
+ goto err; // Wrong address
+ }
+ else if (c == '.')
+ {
+ result= (result << 8) + (ulonglong) byte_result;
+ byte_result = 0;
+ }
+ else
+ goto err; // Invalid character
+ }
+ if (c != '.') // IP number can't end on '.'
+ return (result << 8) + (ulonglong) byte_result;
+
+err:
+ null_value=1;
+ return 0;
+}
+
+double Item_func_match::val()
+{
+ int a,b,c;
+ FT_DOC *docs;
+ my_off_t docid;
+
+ docid = table->file->row_position(); // HAVE to do it here...
+
+ if (table->file->ft_handler==NULL && !auto_init_was_done)
+ {
+ /* join won't use this ft-key, but we must to init it anyway */
+ String *ft_tmp=0;
+ char tmp1[FT_QUERY_MAXLEN];
+ String tmp2(tmp1,sizeof(tmp1));
+
+ ft_tmp=key_item()->val_str(&tmp2);
+ table->file->ft_init(key, (byte*) ft_tmp->ptr(), ft_tmp->length(), FALSE);
+ auto_init_was_done=1;
+ }
+
+ // Don't know how to return an error from val(), so NULL will be returned
+ if ((null_value=(table->file->ft_handler==NULL)))
+ return 0.0;
+
+ if (auto_init_was_done)
+ {
+ /* implicit initialization was done, so nobody will set proper
+ ft_relevance for us. We'll look for it in ft_handler array */
+
+ docs = ((FT_DOCLIST *)table->file->ft_handler)->doc;
+// docid = table->file->row_position();
+
+ if ((null_value=(docid==HA_OFFSET_ERROR)))
+ return 0.0;
+
+ // Assuming docs[] is sorted by dpos...
+
+ a=0, b=((FT_DOCLIST *)table->file->ft_handler)->ndocs;
+ for (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)
+ table->file->ft_relevance=docs[a].weight;
+ else
+ table->file->ft_relevance=0;
+ }
+
+ return table->file->ft_relevance;
+}
+
+bool Item_func_match::fix_fields(THD *thd,struct st_table_list *tlist)
+{
+ List_iterator<Item> li(fields);
+ Item *item;
+
+ /* Why testing for const_item ? Monty */
+ /* I'll remove it later, but this should include modifications to
+ find_best and auto_close as complement to auto_init code above. SerG */
+ if (Item_func::fix_fields(thd,tlist) || !const_item())
+ return 1;
+
+ while ((item=li++))
+ {
+ if (item->type() != Item::FIELD_ITEM || item->fix_fields(thd,tlist) ||
+ !item->used_tables())
+ return 1;
+ used_tables_cache|=item->used_tables();
+ }
+ /* check that all columns comes from the same table */
+ if (count_bits(used_tables_cache) != 1)
+ return 1;
+ const_item_cache=0;
+ table=((Item_field *)fields.head())->field->table;
+ auto_init_was_done=0;
+ table->file->ft_close(); // It's a bad solution to do it here, I know :-(
+ return 0;
+}
+
+
+bool Item_func_match::fix_index()
+{
+ List_iterator<Item> li(fields);
+ Item_field *item;
+ uint ft_to_key[MAX_KEY], ft_cnt[MAX_KEY], fts=0, key;
+
+ for (key=0 ; key<table->keys ; key++)
+ {
+ if ((table->key_info[key].flags & HA_FULLTEXT) &&
+ (table->keys_in_use_for_query & (((key_map)1) << key)))
+ {
+ ft_to_key[fts]=key;
+ 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;
+ }
+
+ while ((item=(Item_field*)(li++)))
+ {
+ for (key=0 ; key<fts ; key++)
+ {
+ KEY *ft_key=&table->key_info[ft_to_key[key]];
+ 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]++;
+ }
+ }
+ }
+
+ uint max_cnt=0, max_key=0;
+ for (key=0 ; key<fts ; key++)
+ {
+ if (ft_cnt[key] > max_cnt)
+ {
+ max_cnt=ft_cnt[key];
+ max_key=ft_to_key[key];
+ }
+ }
+
+ // for now, partial keys won't work. SerG
+
+ if (max_cnt < fields.elements ||
+ max_cnt < table->key_info[max_key].key_parts)
+ {
+ my_printf_error(ER_FT_MATCHING_KEY_NOT_FOUND,
+ ER(ER_FT_MATCHING_KEY_NOT_FOUND),MYF(0));
+ return 1;
+ }
+
+ this->key=max_key;
+
+ maybe_null=1;
+
+ return 0;
+}
+
+
diff --git a/sql/item_func.h b/sql/item_func.h
new file mode 100644
index 00000000000..bd66ebde3c2
--- /dev/null
+++ b/sql/item_func.h
@@ -0,0 +1,857 @@
+/* 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 */
+
+
+/* Function items used by mysql */
+
+#ifdef __GNUC__
+#pragma interface /* gcc class implementation */
+#endif
+
+#ifdef HAVE_IEEEFP_H
+extern "C" /* Bug in BSDI include file */
+{
+#include <ieeefp.h>
+}
+#endif
+
+class Item_func :public Item_result_field
+{
+protected:
+ Item **args,*tmp_arg[2];
+public:
+ uint arg_count;
+ table_map used_tables_cache;
+ bool const_item_cache;
+ 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};
+ 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; }
+ Item_func(void)
+ {
+ arg_count=0; with_sum_func=0;
+ }
+ Item_func(Item *a)
+ {
+ arg_count=1;
+ args=tmp_arg;
+ args[0]=a;
+ with_sum_func=a->with_sum_func;
+ }
+ Item_func(Item *a,Item *b)
+ {
+ arg_count=2;
+ args=tmp_arg;
+ args[0]=a; args[1]=b;
+ with_sum_func=a->with_sum_func || b->with_sum_func;
+ }
+ Item_func(Item *a,Item *b,Item *c)
+ {
+ arg_count=0;
+ if ((args=(Item**) sql_alloc(sizeof(Item*)*3)))
+ {
+ arg_count=3;
+ args[0]=a; args[1]=b; args[2]=c;
+ with_sum_func=a->with_sum_func || b->with_sum_func || c->with_sum_func;
+ }
+ }
+ Item_func(Item *a,Item *b,Item *c,Item *d)
+ {
+ arg_count=0;
+ if ((args=(Item**) sql_alloc(sizeof(Item*)*4)))
+ {
+ arg_count=4;
+ args[0]=a; args[1]=b; args[2]=c; args[3]=d;
+ with_sum_func=a->with_sum_func || b->with_sum_func || c->with_sum_func ||
+ d->with_sum_func;
+ }
+ }
+ Item_func(Item *a,Item *b,Item *c,Item *d,Item* e)
+ {
+ arg_count=5;
+ if ((args=(Item**) sql_alloc(sizeof(Item*)*5)))
+ {
+ args[0]=a; args[1]=b; args[2]=c; args[3]=d; args[4]=e;
+ with_sum_func=a->with_sum_func || b->with_sum_func || c->with_sum_func ||
+ d->with_sum_func || e->with_sum_func ;
+ }
+ }
+ Item_func(List<Item> &list);
+ ~Item_func() {} /* Nothing to do; Items are freed automaticly */
+ bool fix_fields(THD *,struct st_table_list *);
+ void make_field(Send_field *field);
+ table_map used_tables() const;
+ void update_used_tables();
+ bool eq(const Item *item) const;
+ virtual optimize_type select_optimize() const { return OPTIMIZE_NONE; }
+ virtual bool have_rev_func() const { return 0; }
+ virtual Item *key_item() const { return args[0]; }
+ virtual const char *func_name() const { return "?"; }
+ virtual bool const_item() const { return const_item_cache; }
+ inline Item **arguments() const { return args; }
+ inline uint argument_count() const { return arg_count; }
+ inline void remove_arguments() { arg_count=0; }
+ virtual void split_sum_func(List<Item> &fields);
+ void print(String *str);
+ void print_op(String *str);
+ void fix_num_length_and_dec();
+ inline bool get_arg0_date(TIME *ltime,bool fuzzy_date)
+ {
+ return (null_value=args[0]->get_date(ltime,fuzzy_date));
+ }
+ inline bool get_arg0_time(TIME *ltime)
+ {
+ return (null_value=args[0]->get_time(ltime));
+ }
+ friend class udf_handler;
+};
+
+
+class Item_real_func :public Item_func
+{
+public:
+ Item_real_func() :Item_func() {}
+ Item_real_func(Item *a) :Item_func(a) {}
+ Item_real_func(Item *a,Item *b) :Item_func(a,b) {}
+ Item_real_func(List<Item> &list) :Item_func(list) {}
+ String *val_str(String*str);
+ 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); }
+};
+
+class Item_num_func :public Item_func
+{
+ protected:
+ Item_result hybrid_type;
+public:
+ Item_num_func(Item *a) :Item_func(a),hybrid_type(REAL_RESULT) {}
+ Item_num_func(Item *a,Item *b) :Item_func(a,b),hybrid_type(REAL_RESULT) {}
+ String *val_str(String*str);
+ 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(); }
+};
+
+
+class Item_num_op :public Item_func
+{
+ protected:
+ Item_result hybrid_type;
+ public:
+ Item_num_op(Item *a,Item *b) :Item_func(a,b),hybrid_type(REAL_RESULT) {}
+ String *val_str(String*str);
+ void print(String *str) { print_op(str); }
+ 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);
+};
+
+
+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) {}
+ 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; }
+};
+
+class Item_func_plus :public Item_num_op
+{
+public:
+ Item_func_plus(Item *a,Item *b) :Item_num_op(a,b) {}
+ const char *func_name() const { return "+"; }
+ double val();
+ longlong val_int();
+};
+
+class Item_func_minus :public Item_num_op
+{
+public:
+ Item_func_minus(Item *a,Item *b) :Item_num_op(a,b) {}
+ const char *func_name() const { return "-"; }
+ double val();
+ longlong val_int();
+};
+
+class Item_func_mul :public Item_num_op
+{
+public:
+ Item_func_mul(Item *a,Item *b) :Item_num_op(a,b) {}
+ const char *func_name() const { return "*"; }
+ double val();
+ longlong val_int();
+};
+
+
+class Item_func_div :public Item_num_op
+{
+public:
+ Item_func_div(Item *a,Item *b) :Item_num_op(a,b) {}
+ double val();
+ longlong val_int();
+ const char *func_name() const { return "/"; }
+ void fix_length_and_dec();
+};
+
+
+class Item_func_mod :public Item_num_op
+{
+public:
+ Item_func_mod(Item *a,Item *b) :Item_num_op(a,b) {}
+ double val();
+ longlong val_int();
+ const char *func_name() const { return "%"; }
+ void fix_length_and_dec();
+};
+
+
+class Item_func_neg :public Item_num_func
+{
+public:
+ Item_func_neg(Item *a) :Item_num_func(a) {}
+ double val();
+ longlong val_int();
+ const char *func_name() const { return "-"; }
+ void fix_length_and_dec();
+};
+
+
+class Item_func_abs :public Item_num_func
+{
+public:
+ Item_func_abs(Item *a) :Item_num_func(a) {}
+ const char *func_name() const { return "abs"; }
+ double val();
+ longlong val_int();
+ enum Item_result result_type () const
+ { return args[0]->result_type() == INT_RESULT ? INT_RESULT : REAL_RESULT; }
+ void fix_length_and_dec();
+};
+
+// A class to handle logaritmic and trigometric functions
+
+class Item_dec_func :public Item_real_func
+{
+ public:
+ Item_dec_func(Item *a) :Item_real_func(a) {}
+ Item_dec_func(Item *a,Item *b) :Item_real_func(a,b) {}
+ void fix_length_and_dec()
+ {
+ decimals=6; max_length=float_length(decimals);
+ maybe_null=1;
+ }
+ inline double fix_result(double value)
+ {
+#ifndef HAVE_FINITE
+ return value;
+#else
+ if (finite(value) && value != POSTFIX_ERROR)
+ return value;
+ null_value=1;
+ return 0.0;
+#endif
+ }
+};
+
+class Item_func_exp :public Item_dec_func
+{
+public:
+ Item_func_exp(Item *a) :Item_dec_func(a) {}
+ double val();
+ const char *func_name() const { return "exp"; }
+};
+
+class Item_func_log :public Item_dec_func
+{
+public:
+ Item_func_log(Item *a) :Item_dec_func(a) {}
+ double val();
+ const char *func_name() const { return "log"; }
+};
+
+
+class Item_func_log10 :public Item_dec_func
+{
+public:
+ Item_func_log10(Item *a) :Item_dec_func(a) {}
+ double val();
+ const char *func_name() const { return "log10"; }
+};
+
+
+class Item_func_sqrt :public Item_dec_func
+{
+public:
+ Item_func_sqrt(Item *a) :Item_dec_func(a) {}
+ double val();
+ const char *func_name() const { return "sqrt"; }
+};
+
+
+class Item_func_pow :public Item_dec_func
+{
+public:
+ Item_func_pow(Item *a,Item *b) :Item_dec_func(a,b) {}
+ double val();
+ const char *func_name() const { return "pow"; }
+};
+
+
+class Item_func_acos :public Item_dec_func
+{
+ public:
+ Item_func_acos(Item *a) :Item_dec_func(a) {}
+ double val();
+ const char *func_name() const { return "acos"; }
+};
+
+class Item_func_asin :public Item_dec_func
+{
+ public:
+ Item_func_asin(Item *a) :Item_dec_func(a) {}
+ double val();
+ const char *func_name() const { return "asin"; }
+};
+
+class Item_func_atan :public Item_dec_func
+{
+ public:
+ Item_func_atan(Item *a) :Item_dec_func(a) {}
+ Item_func_atan(Item *a,Item *b) :Item_dec_func(a,b) {}
+ double val();
+ const char *func_name() const { return "atan"; }
+};
+
+class Item_func_cos :public Item_dec_func
+{
+ public:
+ Item_func_cos(Item *a) :Item_dec_func(a) {}
+ double val();
+ const char *func_name() const { return "cos"; }
+};
+
+class Item_func_sin :public Item_dec_func
+{
+ public:
+ Item_func_sin(Item *a) :Item_dec_func(a) {}
+ double val();
+ const char *func_name() const { return "sin"; }
+};
+
+class Item_func_tan :public Item_dec_func
+{
+ public:
+ Item_func_tan(Item *a) :Item_dec_func(a) {}
+ double val();
+ const char *func_name() const { return "tan"; }
+};
+
+class Item_func_integer :public Item_int_func
+{
+public:
+ inline Item_func_integer(Item *a) :Item_int_func(a) {}
+ void fix_length_and_dec();
+};
+
+
+class Item_func_ceiling :public Item_func_integer
+{
+ Item_func_ceiling(); /* Never called */
+public:
+ Item_func_ceiling(Item *a) :Item_func_integer(a) {}
+ const char *func_name() const { return "ceiling"; }
+ longlong val_int();
+};
+
+class Item_func_floor :public Item_func_integer
+{
+public:
+ Item_func_floor(Item *a) :Item_func_integer(a) {}
+ const char *func_name() const { return "floor"; }
+ longlong val_int();
+};
+
+/* This handles round and truncate */
+
+class Item_func_round :public Item_real_func
+{
+ bool truncate;
+public:
+ Item_func_round(Item *a,Item *b,bool trunc_arg)
+ :Item_real_func(a,b),truncate(trunc_arg) {}
+ const char *func_name() const { return truncate ? "truncate" : "round"; }
+ double val();
+ void fix_length_and_dec();
+};
+
+
+class Item_func_rand :public Item_real_func
+{
+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; }
+};
+
+
+class Item_func_sign :public Item_int_func
+{
+public:
+ Item_func_sign(Item *a) :Item_int_func(a) {}
+ const char *func_name() const { return "sign"; }
+ longlong val_int();
+};
+
+
+class Item_func_units :public Item_real_func
+{
+ char *name;
+ double mul,add;
+ public:
+ Item_func_units(char *name_arg,Item *a,double mul_arg,double add_arg)
+ :Item_real_func(a),name(name_arg),mul(mul_arg),add(add_arg) {}
+ double val();
+ const char *func_name() const { return name; }
+ void fix_length_and_dec() { decimals=NOT_FIXED_DEC; max_length=float_length(decimals); }
+};
+
+
+class Item_func_min_max :public Item_func
+{
+ Item_result cmp_type;
+ String tmp_value;
+ int cmp_sign;
+public:
+ Item_func_min_max(List<Item> &list,int cmp_sign_arg) :Item_func(list),
+ cmp_sign(cmp_sign_arg) {}
+ double val();
+ longlong val_int();
+ String *val_str(String *);
+ void fix_length_and_dec();
+ enum Item_result result_type () const { return cmp_type; }
+};
+
+class Item_func_min :public Item_func_min_max
+{
+public:
+ Item_func_min(List<Item> &list) :Item_func_min_max(list,1) {}
+ const char *func_name() const { return "least"; }
+};
+
+class Item_func_max :public Item_func_min_max
+{
+public:
+ Item_func_max(List<Item> &list) :Item_func_min_max(list,-1) {}
+ const char *func_name() const { return "greatest"; }
+};
+
+
+class Item_func_length :public Item_int_func
+{
+ String value;
+public:
+ Item_func_length(Item *a) :Item_int_func(a) {}
+ longlong val_int();
+ const char *func_name() const { return "length"; }
+ void fix_length_and_dec() { max_length=10; }
+};
+
+class Item_func_char_length :public Item_int_func
+{
+ String value;
+public:
+ Item_func_char_length(Item *a) :Item_int_func(a) {}
+ longlong val_int();
+ const char *func_name() const { return "char_length"; }
+ void fix_length_and_dec() { max_length=10; }
+};
+
+class Item_func_locate :public Item_int_func
+{
+ String value1,value2;
+public:
+ Item_func_locate(Item *a,Item *b) :Item_int_func(a,b) {}
+ Item_func_locate(Item *a,Item *b,Item *c) :Item_int_func(a,b,c) {}
+ const char *func_name() const { return "locate"; }
+ longlong val_int();
+ void fix_length_and_dec() { maybe_null=0; max_length=11; }
+};
+
+
+class Item_func_field :public Item_int_func
+{
+ Item *item;
+ String value,tmp;
+public:
+ Item_func_field(Item *a,List<Item> &list) :Item_int_func(list),item(a) {}
+ ~Item_func_field() { delete item; }
+ longlong val_int();
+ bool fix_fields(THD *thd,struct st_table_list *tlist)
+ {
+ return (item->fix_fields(thd,tlist) || Item_func::fix_fields(thd,tlist));
+ }
+ void update_used_tables()
+ {
+ item->update_used_tables() ; Item_func::update_used_tables();
+ used_tables_cache|=item->used_tables();
+ }
+ const char *func_name() const { return "field"; }
+ void fix_length_and_dec()
+ { maybe_null=0; max_length=2; used_tables_cache|=item->used_tables();}
+};
+
+
+class Item_func_ascii :public Item_int_func
+{
+ String value;
+public:
+ Item_func_ascii(Item *a) :Item_int_func(a) {}
+ longlong val_int();
+ const char *func_name() const { return "ascii"; }
+ void fix_length_and_dec() { max_length=3; }
+};
+
+class Item_func_ord :public Item_int_func
+{
+ String value;
+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; }
+};
+
+class Item_func_find_in_set :public Item_int_func
+{
+ String value,value2;
+ uint enum_value;
+ ulonglong enum_bit;
+public:
+ Item_func_find_in_set(Item *a,Item *b) :Item_int_func(a,b),enum_value(0) {}
+ longlong val_int();
+ const char *func_name() const { return "find_in_set"; }
+ void fix_length_and_dec();
+};
+
+
+class Item_func_bit_or :public Item_int_func
+{
+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; }
+};
+
+class Item_func_bit_and :public Item_int_func
+{
+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; }
+};
+
+class Item_func_bit_count :public Item_int_func
+{
+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; }
+};
+
+class Item_func_shift_left :public Item_int_func
+{
+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; }
+};
+
+class Item_func_shift_right :public Item_int_func
+{
+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
+{
+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; }
+};
+
+class Item_func_set_last_insert_id :public Item_int_func
+{
+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; }
+};
+
+class Item_func_benchmark :public Item_int_func
+{
+ ulong loop_count;
+ public:
+ Item_func_benchmark(ulong loop_count_arg,Item *expr)
+ :Item_int_func(expr), loop_count(loop_count_arg)
+ {}
+ longlong val_int();
+ const char *func_name() const { return "benchmark"; }
+ void fix_length_and_dec() { decimals=0; max_length=1; maybe_null=0; }
+};
+
+
+#ifdef HAVE_DLOPEN
+
+class Item_udf_func :public Item_func
+{
+ protected:
+ udf_handler udf;
+
+public:
+ Item_udf_func(udf_func *udf_arg) :Item_func(), udf(udf_arg) {}
+ Item_udf_func(udf_func *udf_arg, List<Item> &list)
+ :Item_func(list), udf(udf_arg) {}
+ ~Item_udf_func() {}
+ const char *func_name() const { return udf.name(); }
+ bool fix_fields(THD *thd,struct st_table_list *tables)
+ {
+ bool res=udf.fix_fields(thd,tables,this,arg_count,args);
+ used_tables_cache=udf.used_tables_cache;
+ const_item_cache=udf.const_item_cache;
+ return res;
+ }
+ Item_result result_type () const { return udf.result_type(); }
+};
+
+
+class Item_func_udf_float :public Item_udf_func
+{
+ public:
+ Item_func_udf_float(udf_func *udf_arg) :Item_udf_func(udf_arg) {}
+ Item_func_udf_float(udf_func *udf_arg, List<Item> &list)
+ :Item_udf_func(udf_arg,list) {}
+ ~Item_func_udf_float() {}
+ longlong val_int() { return (longlong) Item_func_udf_float::val(); }
+ double val();
+ String *val_str(String *str);
+ void fix_length_and_dec() { fix_num_length_and_dec(); }
+};
+
+
+class Item_func_udf_int :public Item_udf_func
+{
+public:
+ Item_func_udf_int(udf_func *udf_arg) :Item_udf_func(udf_arg) {}
+ Item_func_udf_int(udf_func *udf_arg, List<Item> &list)
+ :Item_udf_func(udf_arg,list) {}
+ ~Item_func_udf_int() {}
+ longlong val_int();
+ double val() { return (double) Item_func_udf_int::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; }
+};
+
+
+class Item_func_udf_str :public Item_udf_func
+{
+public:
+ Item_func_udf_str(udf_func *udf_arg) :Item_udf_func(udf_arg) {}
+ Item_func_udf_str(udf_func *udf_arg, List<Item> &list)
+ :Item_udf_func(udf_arg,list) {}
+ ~Item_func_udf_str() {}
+ String *val_str(String *);
+ double val()
+ {
+ String *res; res=val_str(&str_value);
+ return res ? atof(res->c_ptr()) : 0.0;
+ }
+ longlong val_int()
+ {
+ String *res; res=val_str(&str_value);
+ return res ? strtoll(res->c_ptr(),(char**) 0,10) : (longlong) 0;
+ }
+ enum Item_result result_type () const { return STRING_RESULT; }
+ void fix_length_and_dec();
+};
+
+#else /* Dummy functions to get sql_yacc.cc compiled */
+
+class Item_func_udf_float :public Item_real_func
+{
+ public:
+ Item_func_udf_float(udf_func *udf_arg) :Item_real_func() {}
+ Item_func_udf_float(udf_func *udf_arg, List<Item> &list) :Item_real_func(list) {}
+ ~Item_func_udf_float() {}
+ double val() { return 0.0; }
+};
+
+
+class Item_func_udf_int :public Item_int_func
+{
+public:
+ Item_func_udf_int(udf_func *udf_arg) :Item_int_func() {}
+ Item_func_udf_int(udf_func *udf_arg, List<Item> &list) :Item_int_func(list) {}
+ ~Item_func_udf_int() {}
+ longlong val_int() { return 0; }
+};
+
+
+class Item_func_udf_str :public Item_func
+{
+public:
+ Item_func_udf_str(udf_func *udf_arg) :Item_func() {}
+ Item_func_udf_str(udf_func *udf_arg, List<Item> &list) :Item_func(list) {}
+ ~Item_func_udf_str() {}
+ String *val_str(String *) { null_value=1; return 0; }
+ double val() { null_value=1; return 0.0; }
+ longlong val_int() { null_value=1; return 0; }
+ enum Item_result result_type () const { return STRING_RESULT; }
+ void fix_length_and_dec() { maybe_null=1; max_length=0; }
+};
+
+#endif /* HAVE_DLOPEN */
+
+/*
+** User level locks
+*/
+
+class ULL;
+void item_user_lock_init(void);
+void item_user_lock_release(ULL *ull);
+void item_user_lock_free(void);
+
+class Item_func_get_lock :public Item_int_func
+{
+ String value;
+ public:
+ 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;}
+};
+
+class Item_func_release_lock :public Item_int_func
+{
+ String value;
+ public:
+ 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;}
+};
+
+
+/* Handling of user definiable variables */
+
+class user_var_entry;
+
+class Item_func_set_user_var :public Item_func
+{
+ enum Item_result cached_result_type;
+ LEX_STRING name;
+ user_var_entry *entry;
+
+public:
+ Item_func_set_user_var(LEX_STRING a,Item *b): Item_func(b), name(a) {}
+ double val();
+ longlong val_int();
+ String *val_str(String *str);
+ void update_hash(void *ptr, uint length, enum Item_result type);
+ bool update();
+ enum Item_result result_type () const { return cached_result_type; }
+ bool fix_fields(THD *thd,struct st_table_list *tables);
+ void fix_length_and_dec();
+ const char *func_name() const { return "set_user_var"; }
+};
+
+
+class Item_func_get_user_var :public Item_func
+{
+ LEX_STRING name;
+ user_var_entry *entry;
+
+public:
+ Item_func_get_user_var(LEX_STRING a): Item_func(), name(a) {}
+ user_var_entry *get_entry();
+ double val();
+ longlong val_int();
+ String *val_str(String* str);
+ void fix_length_and_dec();
+ enum Item_result result_type() const;
+ const char *func_name() const { return "get_user_var"; }
+};
+
+class Item_func_inet_aton : public Item_int_func
+{
+public:
+ Item_func_inet_aton(Item *a) :Item_int_func(a) {}
+ longlong val_int();
+ const char *func_name() const { return "inet_aton"; }
+ void fix_length_and_dec() { decimals = 0; max_length = 21; maybe_null=1;}
+};
+
+
+/* SerG: for fulltext search */
+
+class Item_func_match :public Item_real_func
+{
+public:
+ // handler *File;
+ List<Item> fields;
+ TABLE *table;
+ uint key;
+ bool auto_init_was_done;
+
+ Item_func_match(List<Item> &a, Item *b): Item_real_func(b),
+ fields(a), table(0)
+ {}
+ ~Item_func_match() {}
+ const char *func_name() const { return "match"; }
+ //optimize_type select_optimize() const { return OPTIMIZE_FT; }
+ enum Functype functype() const { return FT_FUNC; }
+ void update_used_tables() {}
+ bool fix_fields(THD *thd,struct st_table_list *tlist);
+ bool fix_index();
+
+ double val();
+ longlong val_int() { return val()!=0.0; }
+};
+
diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc
new file mode 100644
index 00000000000..3050125f410
--- /dev/null
+++ b/sql/item_strfunc.cc
@@ -0,0 +1,1719 @@
+/* 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 */
+
+
+/* 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)
+*/
+
+#ifdef __GNUC__
+#pragma implementation // gcc: Class implementation
+#endif
+
+#include "mysql_priv.h"
+#include "sql_acl.h"
+#include <m_ctype.h>
+#ifdef HAVE_CRYPT_H
+#include <crypt.h>
+#endif
+
+#include "md5.h"
+
+String empty_string("");
+
+uint nr_of_decimals(const char *str)
+{
+ if ((str=strchr(str,'.')))
+ {
+ const char *start= ++str;
+ for ( ; isdigit(*str) ; str++) ;
+ return (uint) (str-start);
+ }
+ return 0;
+}
+
+double Item_str_func::val()
+{
+ String *res;
+ res=val_str(&str_value);
+ return res ? atof(res->c_ptr()) : 0.0;
+}
+
+longlong Item_str_func::val_int()
+{
+ String *res;
+ res=val_str(&str_value);
+ return res ? strtoll(res->c_ptr(),NULL,10) : (longlong) 0;
+}
+
+
+String *Item_func_md5::val_str(String *str)
+{
+ String * sptr= args[0]->val_str(str);
+ if (sptr)
+ {
+ 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
+ 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],
+ digest[4], digest[5], digest[6], digest[7],
+ digest[8], digest[9], digest[10], digest[11],
+ digest[12], digest[13], digest[14], digest[15]);
+ str->length((uint) 32);
+ return str;
+ }
+ null_value=1;
+ return 0;
+}
+
+
+void Item_func_md5::fix_length_and_dec()
+{
+ max_length=32;
+}
+
+/*
+** 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.
+*/
+
+String *Item_func_concat::val_str(String *str)
+{
+ String *res,*res2,*use_as_buff;
+ uint i;
+
+ null_value=0;
+ if (!(res=args[0]->val_str(str)))
+ goto null;
+ use_as_buff= &tmp_value;
+ for (i=1 ; i < arg_count ; i++)
+ {
+ if (res->length() == 0)
+ {
+ if (!(res=args[i]->val_str(str)))
+ goto null;
+ }
+ else
+ {
+ if (!(res2=args[i]->val_str(use_as_buff)))
+ goto null;
+ if (res2->length() == 0)
+ continue;
+ if (res->length()+res2->length() > max_allowed_packet)
+ goto null; // Error check
+ if (res->alloced_length() >= res->length()+res2->length())
+ { // Use old buffer
+ res->append(*res2);
+ }
+ else if (str->alloced_length() >= res->length()+res2->length())
+ {
+ if (str == res2)
+ str->replace(0,0,*res);
+ else
+ {
+ str->copy(*res);
+ str->append(*res2);
+ }
+ res=str;
+ }
+ else if (res == &tmp_value)
+ {
+ if (res->append(*res2)) // Must be a blob
+ goto null;
+ }
+ else if (res2 == &tmp_value)
+ { // This can happend only 1 time
+ if (tmp_value.replace(0,0,*res))
+ goto null;
+ res= &tmp_value;
+ use_as_buff=str; // Put next arg here
+ }
+ else if (tmp_value.is_alloced() && res2->ptr() >= tmp_value.ptr() &&
+ res2->ptr() <= tmp_value.ptr() + tmp_value.alloced_length())
+ {
+ /*
+ This happens really seldom:
+ In this case res2 is sub string of tmp_value. We will
+ now work in place in tmp_value to set it to res | res2
+ */
+ /* Chop the last characters in tmp_value that isn't in res2 */
+ tmp_value.length((uint32) (res2->ptr() - tmp_value.ptr()) +
+ res2->length());
+ /* Place res2 at start of tmp_value, remove chars before res2 */
+ if (tmp_value.replace(0,(uint32) (res2->ptr() - tmp_value.ptr()),
+ *res))
+ goto null;
+ res= &tmp_value;
+ use_as_buff=str; // Put next arg here
+ }
+ else
+ { // Two big const strings
+ if (tmp_value.alloc(max_length) ||
+ tmp_value.copy(*res) ||
+ tmp_value.append(*res2))
+ goto null;
+ res= &tmp_value;
+ use_as_buff=str;
+ }
+ }
+ }
+ return res;
+
+null:
+ null_value=1;
+ return 0;
+}
+
+
+void Item_func_concat::fix_length_and_dec()
+{
+ max_length=0;
+ for (uint i=0 ; i < arg_count ; i++)
+ max_length+=args[i]->max_length;
+ if (max_length > MAX_BLOB_WIDTH)
+ {
+ max_length=MAX_BLOB_WIDTH;
+ maybe_null=1;
+ }
+}
+
+
+
+/*
+** concat with separator. First arg is the separator
+** concat_ws takes at least two arguments.
+*/
+
+String *Item_func_concat_ws::val_str(String *str)
+{
+ char tmp_str_buff[10];
+ String tmp_sep_str(tmp_str_buff, sizeof(tmp_str_buff)),
+ *sep_str, *res, *res2,*use_as_buff;
+ uint i;
+
+ null_value=0;
+ if (!(sep_str= separator->val_str(&tmp_sep_str)))
+ goto null;
+
+ use_as_buff= &tmp_value;
+ str->length(0);
+ res=str;
+
+ // Skip until non-null and non-empty argument is found.
+ // If not, return the empty string
+ for (i=0; !(res= args[i]->val_str(str)) || !res->length(); i++)
+ {
+ if ((i + 1) == arg_count)
+ return &empty_string;
+ }
+
+ for (i++; i < arg_count ; i++)
+ {
+ if (!(res2= args[i]->val_str(use_as_buff)) || !res2->length())
+ continue;
+ else
+ {
+ if (res->length() + sep_str->length() + res2->length() >
+ max_allowed_packet)
+ goto null; // Error check
+ if (res->alloced_length() >=
+ res->length() + sep_str->length() + res2->length())
+ { // Use old buffer
+ res->append(*sep_str); // res->length() > 0 always
+ res->append(*res2);
+ use_as_buff= &tmp_value;
+ }
+ else if (str->alloced_length() >=
+ res->length() + sep_str->length() + res2->length())
+ {
+ str->copy(*res);
+ str->append(*sep_str);
+ str->append(*res2);
+ res=str;
+ use_as_buff= &tmp_value;
+ }
+ else if (res == &tmp_value)
+ {
+ if ((res->length() && res->append(*sep_str)) || res->append(*res2))
+ goto null; // Must be a blob
+ }
+ else if (tmp_value.is_alloced() && res2->ptr() >= tmp_value.ptr() &&
+ res2->ptr() <= tmp_value.ptr() + tmp_value.alloced_length())
+ {
+ /*
+ This happens really seldom:
+ In this case res2 is sub string of tmp_value. We will
+ now work in place in tmp_value to set it to res | res2
+ */
+ /* Chop the last characters in tmp_value that isn't in res2 */
+ tmp_value.length((uint32) (res2->ptr() - tmp_value.ptr()) +
+ res2->length());
+ /* Place res2 at start of tmp_value, remove chars before res2 */
+ if (res->append(*sep_str))
+ goto null;
+ if (tmp_value.replace(0,(uint32) (res2->ptr() - tmp_value.ptr()),
+ *res))
+ goto null;
+ res= &tmp_value;
+ use_as_buff=str; // Put next arg here
+ }
+ else
+ { // Two big const strings
+ if (tmp_value.alloc(max_length) ||
+ tmp_value.copy(*res) ||
+ tmp_value.append(*sep_str) ||
+ tmp_value.append(*res2))
+ goto null;
+ res= &tmp_value;
+ use_as_buff=str;
+ }
+ }
+ }
+ return res;
+
+null:
+ null_value=1;
+ return 0;
+}
+
+
+void Item_func_concat_ws::fix_length_and_dec()
+{
+ max_length=0;
+ for (uint i=0 ; i < arg_count ; i++)
+ max_length+=args[i]->max_length;
+ if (max_length > MAX_BLOB_WIDTH)
+ {
+ max_length=MAX_BLOB_WIDTH;
+ maybe_null=1;
+ }
+}
+
+
+String *Item_func_reverse::val_str(String *str)
+{
+ String *res = args[0]->val_str(str);
+ char *ptr,*end;
+
+ if ((null_value=args[0]->null_value))
+ return 0;
+ /* An empty string is a special case as the string pointer may be null */
+ if (!res->length())
+ return &empty_string;
+ res=copy_if_not_alloced(str,res,res->length());
+ ptr = (char *) res->ptr();
+ end=ptr+res->length();
+#ifdef USE_MB
+ if (use_mb(default_charset_info) && !binary)
+ {
+ String tmpstr;
+ tmpstr.copy(*res);
+ char *tmp = (char *) tmpstr.ptr() + tmpstr.length();
+ register uint32 l;
+ while (ptr < end)
+ {
+ if ((l=my_ismbchar(default_charset_info, ptr,end)))
+ tmp-=l, memcpy(tmp,ptr,l), ptr+=l;
+ else
+ *--tmp=*ptr++;
+ }
+ memcpy((char *) res->ptr(),(char *) tmpstr.ptr(), res->length());
+ }
+ else
+#endif /* USE_MB */
+ {
+ char tmp;
+ while (ptr < end)
+ {
+ tmp=*ptr;
+ *ptr++=*--end;
+ *end=tmp;
+ }
+ }
+ return res;
+}
+
+
+void Item_func_reverse::fix_length_and_dec()
+{
+ max_length = args[0]->max_length;
+}
+
+/*
+** Replace all occurences of string2 in string1 with string3.
+** Don't reallocate val_str() if not neaded
+*/
+
+/* TODO: Fix that this works with binary strings when using USE_MB */
+
+String *Item_func_replace::val_str(String *str)
+{
+ String *res,*res2,*res3;
+ int offset=0;
+ uint from_length,to_length;
+ bool alloced=0;
+#ifdef USE_MB
+ const char *ptr,*end,*strend,*search,*search_end;
+ register uint32 l;
+#endif
+
+ null_value=0;
+ res=args[0]->val_str(str);
+ if (args[0]->null_value)
+ goto null;
+ res2=args[1]->val_str(&tmp_value);
+ if (args[1]->null_value)
+ goto null;
+
+ if (res2->length() == 0)
+ return res;
+#ifndef USE_MB
+ if ((offset=res->strstr(*res2)) < 0)
+ return res;
+#else
+ if (!use_mb(default_charset_info) && (offset=res->strstr(*res2)) < 0)
+ return res;
+#endif
+ if (!(res3=args[2]->val_str(&tmp_value2)))
+ goto null;
+ from_length= res2->length();
+ to_length= res3->length();
+
+#ifdef USE_MB
+ if (use_mb(default_charset_info))
+ {
+ search=res2->ptr();
+ search_end=search+from_length;
+redo:
+ ptr=res->ptr()+offset;
+ strend=res->ptr()+res->length();
+ end=strend-from_length+1;
+ while (ptr < end)
+ {
+ if (*ptr == *search)
+ {
+ register char *i,*j;
+ i=(char*) ptr+1; j=(char*) search+1;
+ while (j != search_end)
+ if (*i++ != *j++) goto skipp;
+ offset=ptr-res->ptr();
+ if (res->length()-from_length + to_length > max_allowed_packet)
+ goto null;
+ if (!alloced)
+ {
+ alloced=1;
+ res=copy_if_not_alloced(str,res,res->length()+to_length);
+ }
+ res->replace((uint) offset,from_length,*res3);
+ goto redo;
+ }
+skipp:
+ if ((l=my_ismbchar(default_charset_info, ptr,strend))) ptr+=l;
+ else ++ptr;
+ }
+ }
+ else
+#endif /* USE_MB */
+ do
+ {
+ if (res->length()-from_length + to_length > max_allowed_packet)
+ goto null;
+ if (!alloced)
+ {
+ alloced=1;
+ res=copy_if_not_alloced(str,res,res->length()+to_length);
+ }
+ res->replace((uint) offset,from_length,*res3);
+ offset+=(int) to_length;
+ }
+ while ((offset=res->strstr(*res2,(uint) offset)) >0);
+ return res;
+
+null:
+ null_value=1;
+ return 0;
+}
+
+
+void Item_func_replace::fix_length_and_dec()
+{
+ max_length=args[0]->max_length;
+ int diff=(int) (args[2]->max_length - args[1]->max_length);
+ if (diff > 0 && args[1]->max_length)
+ { // Calculate of maxreplaces
+ max_length= max_length/args[1]->max_length;
+ max_length= (max_length+1)*(uint) diff;
+ }
+ if (max_length > MAX_BLOB_WIDTH)
+ {
+ max_length=MAX_BLOB_WIDTH;
+ maybe_null=1;
+ }
+}
+
+
+String *Item_func_insert::val_str(String *str)
+{
+ String *res,*res2;
+ uint start,length;
+
+ null_value=0;
+ res=args[0]->val_str(str);
+ res2=args[3]->val_str(&tmp_value);
+ start=(uint) args[1]->val_int()-1;
+ length=(uint) args[2]->val_int();
+ if (args[0]->null_value || args[1]->null_value || args[2]->null_value ||
+ args[3]->null_value)
+ goto null; /* purecov: inspected */
+#ifdef USE_MB
+ if (use_mb(default_charset_info) && !args[0]->binary)
+ {
+ start=res->charpos(start);
+ length=res->charpos(length,start);
+ }
+#endif
+ if (start > res->length()+1)
+ return res; // Wrong param; skipp insert
+ if (length > res->length()-start)
+ length=res->length()-start;
+ if (res->length() - length + res2->length() > max_allowed_packet)
+ goto null; // OOM check
+ res=copy_if_not_alloced(str,res,res->length());
+ res->replace(start,length,*res2);
+ return res;
+null:
+ null_value=1;
+ return 0;
+}
+
+
+void Item_func_insert::fix_length_and_dec()
+{
+ max_length=args[0]->max_length+args[3]->max_length;
+ if (max_length > MAX_BLOB_WIDTH)
+ {
+ max_length=MAX_BLOB_WIDTH;
+ maybe_null=1;
+ }
+}
+
+
+String *Item_func_lcase::val_str(String *str)
+{
+ String *res;
+ if (!(res=args[0]->val_str(str)))
+ {
+ null_value=1; /* purecov: inspected */
+ return 0; /* purecov: inspected */
+ }
+ null_value=0;
+ res=copy_if_not_alloced(str,res,res->length());
+ res->casedn();
+ return res;
+}
+
+
+String *Item_func_ucase::val_str(String *str)
+{
+ String *res;
+ if (!(res=args[0]->val_str(str)))
+ {
+ null_value=1; /* purecov: inspected */
+ return 0; /* purecov: inspected */
+ }
+ null_value=0;
+ res=copy_if_not_alloced(str,res,res->length());
+ res->caseup();
+ return res;
+}
+
+
+String *Item_func_left::val_str(String *str)
+{
+ String *res =args[0]->val_str(str);
+ long length =(long) args[1]->val_int();
+
+ if ((null_value=args[0]->null_value))
+ return 0;
+ if (length <= 0)
+ return &empty_string;
+#ifdef USE_MB
+ if (use_mb(default_charset_info) && !binary)
+ length = res->charpos(length);
+#endif
+ if (res->length() > (ulong) length)
+ { // Safe even if const arg
+ if (!res->alloced_length())
+ { // Don't change const str
+ str_value= *res; // Not malloced string
+ res= &str_value;
+ }
+ res->length((uint) length);
+ }
+ return res;
+}
+
+
+void Item_str_func::left_right_max_length()
+{
+ max_length=args[0]->max_length;
+ if (args[1]->const_item())
+ {
+ int length=(int) args[1]->val_int();
+ if (length <= 0)
+ max_length=0;
+ else
+ set_if_smaller(max_length,(uint) length);
+ }
+}
+
+
+void Item_func_left::fix_length_and_dec()
+{
+ left_right_max_length();
+}
+
+
+String *Item_func_right::val_str(String *str)
+{
+ String *res =args[0]->val_str(str);
+ long length =(long) args[1]->val_int();
+
+ if ((null_value=args[0]->null_value))
+ return 0; /* purecov: inspected */
+ if (length <= 0)
+ return &empty_string; /* purecov: inspected */
+ if (res->length() <= (uint) length)
+ return res; /* purecov: inspected */
+#ifdef USE_MB
+ if (use_mb(default_charset_info) && !binary)
+ {
+ uint start=res->numchars()-(uint) length;
+ if (start<=0) return res;
+ start=res->charpos(start);
+ tmp_value.set(*res,start,res->length()-start);
+ }
+ else
+#endif
+ {
+ tmp_value.set(*res,(res->length()- (uint) length),(uint) length);
+ }
+ return &tmp_value;
+}
+
+
+void Item_func_right::fix_length_and_dec()
+{
+ left_right_max_length();
+}
+
+
+String *Item_func_substr::val_str(String *str)
+{
+ String *res = args[0]->val_str(str);
+ int32 start = (int32) args[1]->val_int()-1;
+ int32 length = arg_count == 3 ? (int32) args[2]->val_int() : INT_MAX32;
+ int32 tmp_length;
+
+ if ((null_value=(args[0]->null_value || args[1]->null_value ||
+ (arg_count == 3 && args[2]->null_value))))
+ return 0; /* purecov: inspected */
+#ifdef USE_MB
+ if (use_mb(default_charset_info) && !binary)
+ {
+ start=res->charpos(start);
+ length=res->charpos(length,start);
+ }
+#endif
+ if (start < 0 || (uint) start+1 > res->length() || length <= 0)
+ return &empty_string;
+
+ tmp_length=(int32) res->length()-start;
+ length=min(length,tmp_length);
+
+ if (!start && res->length() == (uint) length)
+ return res;
+ tmp_value.set(*res,(uint) start,(uint) length);
+ return &tmp_value;
+}
+
+
+void Item_func_substr::fix_length_and_dec()
+{
+ max_length=args[0]->max_length;
+
+ if (args[1]->const_item())
+ {
+ int32 start=(int32) args[1]->val_int()-1;
+ if (start < 0 || start >= (int32) max_length)
+ max_length=0; /* purecov: inspected */
+ else
+ max_length-= (uint) start;
+ }
+ if (arg_count == 3 && args[2]->const_item())
+ {
+ int32 length= (int32) args[2]->val_int();
+ if (length <= 0)
+ max_length=0; /* purecov: inspected */
+ else
+ set_if_smaller(max_length,(uint) length);
+ }
+}
+
+
+String *Item_func_substr_index::val_str(String *str)
+{
+ String *res =args[0]->val_str(str);
+ String *delimeter =args[1]->val_str(&tmp_value);
+ int32 count = (int32) args[2]->val_int();
+ uint offset;
+
+ if (args[0]->null_value || args[1]->null_value || args[2]->null_value)
+ { // string and/or delim are null
+ null_value=1;
+ return 0;
+ }
+ null_value=0;
+ uint delimeter_length=delimeter->length();
+ if (!res->length() || !delimeter_length || !count)
+ return &empty_string; // Wrong parameters
+
+#ifdef USE_MB
+ if (use_mb(default_charset_info) && !binary)
+ {
+ const char *ptr=res->ptr();
+ const char *strend = ptr+res->length();
+ const char *end=strend-delimeter_length+1;
+ const char *search=delimeter->ptr();
+ const char *search_end=search+delimeter_length;
+ int32 n=0,c=count,pass;
+ register uint32 l;
+ for (pass=(count>0);pass<2;++pass)
+ {
+ while (ptr < end)
+ {
+ if (*ptr == *search)
+ {
+ register char *i,*j;
+ i=(char*) ptr+1; j=(char*) search+1;
+ while (j != search_end)
+ if (*i++ != *j++) goto skipp;
+ if (pass==0) ++n;
+ else if (!--c) break;
+ ptr+=delimeter_length;
+ continue;
+ }
+ skipp:
+ if ((l=my_ismbchar(default_charset_info, ptr,strend))) ptr+=l;
+ else ++ptr;
+ } /* either not found or got total number when count<0 */
+ if (pass == 0) /* count<0 */
+ {
+ c+=n+1;
+ if (c<=0) return res; /* not found, return original string */
+ ptr=res->ptr();
+ }
+ else
+ {
+ if (c) return res; /* Not found, return original string */
+ if (count>0) /* return left part */
+ {
+ tmp_value.set(*res,0,ptr-res->ptr());
+ }
+ else /* return right part */
+ {
+ ptr+=delimeter_length;
+ tmp_value.set(*res,ptr-res->ptr(),strend-ptr);
+ }
+ }
+ }
+ }
+ else
+#endif /* USE_MB */
+ {
+ if (count > 0)
+ { // start counting from the beginning
+ for (offset=0 ;; offset+=delimeter_length)
+ {
+ if ((int) (offset=res->strstr(*delimeter,offset)) < 0)
+ return res; // Didn't find, return org string
+ if (!--count)
+ {
+ tmp_value.set(*res,0,offset);
+ break;
+ }
+ }
+ }
+ else
+ { // Start counting at end
+ for (offset=res->length() ; ; offset-=delimeter_length)
+ {
+ if ((int) (offset=res->strrstr(*delimeter,offset)) < 0)
+ return res; // Didn't find, return org string
+ if (!++count)
+ {
+ offset+=delimeter_length;
+ tmp_value.set(*res,offset,res->length()- offset);
+ break;
+ }
+ }
+ }
+ }
+ return (&tmp_value);
+}
+
+/*
+** The trim functions are extension to ANSI SQL because they trim substrings
+** They ltrim() and rtrim() functions are optimized for 1 byte strings
+** They also return the original string if possible, else they return
+** a substring that points at the original string.
+*/
+
+
+String *Item_func_ltrim::val_str(String *str)
+{
+ String *res =args[0]->val_str(str);
+ if ((null_value=args[0]->null_value))
+ return 0; /* purecov: inspected */
+ char buff[MAX_FIELD_WIDTH];
+ String tmp(buff,sizeof(buff));
+ String *remove_str=args[1]->val_str(&tmp);
+ uint remove_length;
+ LINT_INIT(remove_length);
+
+ if (!remove_str || (remove_length=remove_str->length()) == 0 ||
+ remove_length > res->length())
+ return res;
+
+ char *ptr=(char*) res->ptr();
+ char *end=ptr+res->length();
+ if (remove_length == 1)
+ {
+ char chr=(*remove_str)[0];
+ while (ptr != end && *ptr == chr)
+ ptr++;
+ }
+ else
+ {
+ const char *r_ptr=remove_str->ptr();
+ end-=remove_length;
+ while (ptr < end && !memcmp(ptr,r_ptr,remove_length))
+ ptr+=remove_length;
+ end+=remove_length;
+ }
+ if (ptr == res->ptr())
+ return res;
+ tmp_value.set(*res,(uint) (ptr - res->ptr()),(uint) (end-ptr));
+ return &tmp_value;
+}
+
+
+String *Item_func_rtrim::val_str(String *str)
+{
+ String *res =args[0]->val_str(str);
+ if ((null_value=args[0]->null_value))
+ return 0; /* purecov: inspected */
+ char buff[MAX_FIELD_WIDTH];
+ String tmp(buff,sizeof(buff));
+ String *remove_str=args[1]->val_str(&tmp);
+ uint remove_length;
+ LINT_INIT(remove_length);
+
+ if (!remove_str || (remove_length=remove_str->length()) == 0 ||
+ remove_length > res->length())
+ return res;
+
+ char *ptr=(char*) res->ptr();
+ char *end=ptr+res->length();
+#ifdef USE_MB
+ char *p=ptr;
+ register uint32 l;
+#endif
+ if (remove_length == 1)
+ {
+ char chr=(*remove_str)[0];
+#ifdef USE_MB
+ if (use_mb(default_charset_info) && !binary)
+ {
+ while (ptr < end)
+ {
+ if ((l=my_ismbchar(default_charset_info, ptr,end))) ptr+=l,p=ptr;
+ else ++ptr;
+ }
+ ptr=p;
+ }
+#endif
+ while (ptr != end && end[-1] == chr)
+ end--;
+ }
+ else
+ {
+ const char *r_ptr=remove_str->ptr();
+#ifdef USE_MB
+ if (use_mb(default_charset_info) && !binary)
+ {
+ loop:
+ while (ptr + remove_length < end)
+ {
+ if ((l=my_ismbchar(default_charset_info, ptr,end))) ptr+=l;
+ else ++ptr;
+ }
+ if (ptr + remove_length == end && !memcmp(ptr,r_ptr,remove_length))
+ {
+ end-=remove_length;
+ ptr=p;
+ goto loop;
+ }
+ }
+ else
+#endif /* USE_MB */
+ {
+ while (ptr + remove_length < end &&
+ !memcmp(end-remove_length,r_ptr,remove_length))
+ end-=remove_length;
+ }
+ }
+ if (end == res->ptr()+res->length())
+ return res;
+ tmp_value.set(*res,0,(uint) (end-res->ptr()));
+ return &tmp_value;
+}
+
+
+String *Item_func_trim::val_str(String *str)
+{
+ String *res =args[0]->val_str(str);
+ if ((null_value=args[0]->null_value))
+ return 0; /* purecov: inspected */
+ char buff[MAX_FIELD_WIDTH];
+ String tmp(buff,sizeof(buff));
+ String *remove_str=args[1]->val_str(&tmp);
+ uint remove_length;
+ LINT_INIT(remove_length);
+
+ if (!remove_str || (remove_length=remove_str->length()) == 0 ||
+ remove_length > res->length())
+ return res;
+
+ char *ptr=(char*) res->ptr();
+ char *end=ptr+res->length();
+ const char *r_ptr=remove_str->ptr();
+ while (ptr+remove_length < end && !memcmp(ptr,r_ptr,remove_length))
+ ptr+=remove_length;
+#ifdef USE_MB
+ if (use_mb(default_charset_info) && !binary)
+ {
+ char *p=ptr;
+ register uint32 l;
+ loop:
+ while (ptr + remove_length < end)
+ {
+ if ((l=my_ismbchar(default_charset_info, ptr,end))) ptr+=l;
+ else ++ptr;
+ }
+ if (ptr + remove_length == end && !memcmp(ptr,r_ptr,remove_length))
+ {
+ end-=remove_length;
+ ptr=p;
+ goto loop;
+ }
+ ptr=p;
+ }
+ else
+#endif /* USE_MB */
+ {
+ while (ptr + remove_length < end &&
+ !memcmp(end-remove_length,r_ptr,remove_length))
+ end-=remove_length;
+ }
+ if (ptr == res->ptr() && end == ptr+res->length())
+ return res;
+ tmp_value.set(*res,(uint) (ptr - res->ptr()),(uint) (end-ptr));
+ return &tmp_value;
+}
+
+
+String *Item_func_password::val_str(String *str)
+{
+ String *res =args[0]->val_str(str);
+ if ((null_value=args[0]->null_value))
+ return 0;
+ if (res->length() == 0)
+ return &empty_string;
+ make_scrambled_password(tmp_value,res->c_ptr());
+ str->set(tmp_value,16);
+ return str;
+}
+
+#define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.')
+
+String *Item_func_encrypt::val_str(String *str)
+{
+ String *res =args[0]->val_str(str);
+
+#ifdef HAVE_CRYPT
+ char salt[3],*salt_ptr;
+ if ((null_value=args[0]->null_value))
+ return 0;
+ if (res->length() == 0)
+ return &empty_string;
+
+ if (arg_count == 1)
+ { // generate random salt
+ time_t timestamp=current_thd->query_start();
+ salt[0] = bin_to_ascii(timestamp & 0x3f);
+ salt[1] = bin_to_ascii((timestamp >> 5) & 0x3f);
+ salt[2] = 0;
+ salt_ptr=salt;
+ }
+ else
+ { // obtain salt from the first two bytes
+ String *salt_str=args[1]->val_str(&tmp_value);
+ if ((null_value= (args[1]->null_value || salt_str->length() < 2)))
+ return 0;
+ salt_ptr= salt_str->c_ptr();
+ }
+ pthread_mutex_lock(&LOCK_crypt);
+ char *tmp=crypt(res->c_ptr(),salt_ptr);
+ str->set(tmp,strlen(tmp));
+ str->copy();
+ pthread_mutex_unlock(&LOCK_crypt);
+ return str;
+#else
+ null_value=1;
+ return 0;
+#endif /* HAVE_CRYPT */
+}
+
+void Item_func_encode::fix_length_and_dec()
+{
+ max_length=args[0]->max_length;
+ maybe_null=args[0]->maybe_null;
+}
+
+String *Item_func_encode::val_str(String *str)
+{
+ String *res;
+ if (!(res=args[0]->val_str(str)))
+ {
+ null_value=1; /* purecov: inspected */
+ return 0; /* purecov: inspected */
+ }
+ null_value=0;
+ res=copy_if_not_alloced(str,res,res->length());
+ sql_crypt.init();
+ sql_crypt.encode((char*) res->ptr(),res->length());
+ return res;
+}
+
+String *Item_func_decode::val_str(String *str)
+{
+ String *res;
+ if (!(res=args[0]->val_str(str)))
+ {
+ null_value=1; /* purecov: inspected */
+ return 0; /* purecov: inspected */
+ }
+ null_value=0;
+ res=copy_if_not_alloced(str,res,res->length());
+ sql_crypt.init();
+ sql_crypt.decode((char*) res->ptr(),res->length());
+ return res;
+}
+
+
+String *Item_func_database::val_str(String *str)
+{
+ if (!current_thd->db)
+ str->length(0);
+ else
+ str->set((const char*) current_thd->db,strlen(current_thd->db));
+ return str;
+}
+
+String *Item_func_user::val_str(String *str)
+{
+ THD *thd=current_thd;
+ if (str->copy((const char*) thd->user,strlen(thd->user)) ||
+ str->append('@') ||
+ str->append(thd->host ? thd->host : thd->ip ? thd->ip : ""))
+ return &empty_string;
+ return str;
+}
+
+void Item_func_soundex::fix_length_and_dec()
+{
+ max_length=args[0]->max_length;
+ set_if_bigger(max_length,4);
+}
+
+
+ /*
+ If alpha, map input letter to soundex code.
+ If not alpha and remove_garbage is set then skipp to next char
+ else return 0
+ */
+
+extern "C" {
+extern char *soundex_map; // In mysys/static.c
+}
+
+static char get_scode(char *ptr)
+{
+ uchar ch=toupper(*ptr);
+ if (ch < 'A' || ch > 'Z')
+ {
+ // Thread extended alfa (country spec)
+ return '0'; // as vokal
+ }
+ return(soundex_map[ch-'A']);
+}
+
+
+String *Item_func_soundex::val_str(String *str)
+{
+ String *res =args[0]->val_str(str);
+ char last_ch,ch;
+ if ((null_value=args[0]->null_value))
+ return 0; /* purecov: inspected */
+
+ if (str_value.alloc(max(res->length(),4)))
+ return str; /* purecov: inspected */
+ char *to= (char *) str_value.ptr();
+ char *from= (char *) res->ptr(), *end=from+res->length();
+
+ while (from != end && isspace(*from)) // Skipp pre-space
+ from++; /* purecov: inspected */
+ if (from == end)
+ return &empty_string; // No alpha characters.
+ *to++ = toupper(*from); // Copy first letter
+ last_ch = get_scode(from); // code of the first letter
+ // for the first 'double-letter check.
+ // Loop on input letters until
+ // end of input (null) or output
+ // letter code count = 3
+ for (from++ ; from < end ; from++)
+ {
+ if (!isalpha(*from))
+ continue;
+ ch=get_scode(from);
+ if ((ch != '0') && (ch != last_ch)) // if not skipped or double
+ {
+ *to++ = ch; // letter, copy to output
+ last_ch = ch; // save code of last input letter
+ } // for next double-letter check
+ }
+ for (end=(char*) str_value.ptr()+4 ; to < end ; to++)
+ *to = '0';
+ *to=0; // end string
+ str_value.length((uint) (to-str_value.ptr()));
+ return &str_value;
+}
+
+
+/*
+** Change a number to format '3,333,333,333.000'
+** This should be 'internationalized' sometimes.
+*/
+
+Item_func_format::Item_func_format(Item *org,int dec) :Item_str_func(org)
+{
+ decimals=(uint) set_zone(dec,0,30);
+}
+
+
+String *Item_func_format::val_str(String *str)
+{
+ double nr =args[0]->val();
+ uint32 diff,length,str_length;
+ uint dec;
+ if ((null_value=args[0]->null_value))
+ return 0; /* purecov: inspected */
+ dec= decimals ? decimals+1 : 0;
+ str->set(nr,decimals);
+ str_length=str->length();
+ if (nr < 0)
+ str_length--; // Don't count sign
+ length=str->length()+(diff=(str_length- dec-1)/3);
+ if (diff)
+ {
+ char *tmp,*pos;
+ str=copy_if_not_alloced(&tmp_str,str,length);
+ str->length(length);
+ tmp=(char*) str->ptr()+length - dec-1;
+ for (pos=(char*) str->ptr()+length ; pos != tmp; pos--)
+ pos[0]=pos[- (int) diff];
+ while (diff)
+ {
+ pos[0]=pos[-(int) diff]; pos--;
+ pos[0]=pos[-(int) diff]; pos--;
+ pos[0]=pos[-(int) diff]; pos--;
+ pos[0]=',';
+ pos--;
+ diff--;
+ }
+ }
+ return str;
+}
+
+
+void Item_func_elt::fix_length_and_dec()
+{
+ max_length=0;
+ decimals=0;
+ for (uint i=1 ; i < arg_count ; i++)
+ {
+ set_if_bigger(max_length,args[i]->max_length);
+ set_if_bigger(decimals,args[i]->decimals);
+ }
+ maybe_null=1; // NULL if wrong first arg
+ used_tables_cache|=item->used_tables();
+}
+
+
+void Item_func_elt::update_used_tables()
+{
+ Item_func::update_used_tables();
+ item->update_used_tables();
+ used_tables_cache|=item->used_tables();
+ const_item_cache&=item->const_item();
+}
+
+
+double Item_func_elt::val()
+{
+ uint tmp;
+ if ((tmp=(uint) item->val_int()) == 0 || tmp > arg_count)
+ {
+ null_value=1;
+ return 0.0;
+ }
+ null_value=0;
+ return args[tmp-1]->val();
+}
+
+longlong Item_func_elt::val_int()
+{
+ uint tmp;
+ if ((tmp=(uint) item->val_int()) == 0 || tmp > arg_count)
+ {
+ null_value=1;
+ return 0;
+ }
+ null_value=0;
+ return args[tmp-1]->val_int();
+}
+
+String *Item_func_elt::val_str(String *str)
+{
+ uint tmp;
+ if ((tmp=(uint) item->val_int()) == 0 || tmp > arg_count)
+ {
+ null_value=1;
+ return NULL;
+ }
+ null_value=0;
+ return args[tmp-1]->val_str(str);
+}
+
+
+void Item_func_make_set::fix_length_and_dec()
+{
+ max_length=arg_count-1;
+ for (uint i=1 ; i < arg_count ; i++)
+ max_length+=args[i]->max_length;
+ used_tables_cache|=item->used_tables();
+}
+
+
+void Item_func_make_set::update_used_tables()
+{
+ Item_func::update_used_tables();
+ item->update_used_tables();
+ used_tables_cache|=item->used_tables();
+ const_item_cache&=item->const_item();
+}
+
+
+String *Item_func_make_set::val_str(String *str)
+{
+ ulonglong bits;
+ bool first_found=0;
+ Item **ptr=args;
+ String *result=&empty_string;
+
+ bits=item->val_int();
+ if ((null_value=item->null_value))
+ return NULL;
+
+ if (arg_count < 64)
+ bits &= ((ulonglong) 1 << arg_count)-1;
+
+ for (; bits; bits >>= 1, ptr++)
+ {
+ if (bits & 1)
+ {
+ String *res= (*ptr)->val_str(str);
+ if (res) // Skipp nulls
+ {
+ if (!first_found)
+ { // First argument
+ first_found=1;
+ if (res != str)
+ result=res; // Use original string
+ else
+ {
+ if (tmp_str.copy(*res)) // Don't use 'str'
+ return &empty_string;
+ result= &tmp_str;
+ }
+ }
+ else
+ {
+ if (result != &tmp_str)
+ { // Copy data to tmp_str
+ if (tmp_str.alloc(result->length()+res->length()+1) ||
+ tmp_str.copy(*result))
+ return &empty_string;
+ result= &tmp_str;
+ }
+ if (tmp_str.append(',') || tmp_str.append(*res))
+ return &empty_string;
+ }
+ }
+ }
+ }
+ return result;
+}
+
+
+String *Item_func_char::val_str(String *str)
+{
+ str->length(0);
+ for (uint i=0 ; i < arg_count ; i++)
+ {
+ int32 num=(int32) args[i]->val_int();
+ if (!args[i]->null_value)
+#ifdef USE_MB
+ if (use_mb(default_charset_info))
+ {
+ if (num&0xFF000000L) {
+ str->append((char)(num>>24));
+ goto b2;
+ } else if (num&0xFF0000L) {
+b2: str->append((char)(num>>16));
+ goto b1;
+ } else if (num&0xFF00L) {
+b1: str->append((char)(num>>8));
+ }
+ }
+#endif
+ str->append((char)num);
+ }
+ str->realloc(str->length()); // Add end 0 (for Purify)
+ return str;
+}
+
+
+inline String* alloc_buffer(String *res,String *str,String *tmp_value,
+ ulong length)
+{
+ if (res->alloced_length() < length)
+ {
+ if (str->alloced_length() >= length)
+ {
+ (void) str->copy(*res);
+ str->length(length);
+ return str;
+ }
+ else
+ {
+ if (tmp_value->alloc(length))
+ return 0;
+ (void) tmp_value->copy(*res);
+ tmp_value->length(length);
+ return tmp_value;
+ }
+ }
+ res->length(length);
+ return res;
+}
+
+
+void Item_func_repeat::fix_length_and_dec()
+{
+ if (args[1]->const_item())
+ {
+ max_length=(long) (args[0]->max_length * args[1]->val_int());
+ if (max_length >= MAX_BLOB_WIDTH)
+ {
+ max_length=MAX_BLOB_WIDTH;
+ maybe_null=1;
+ }
+ }
+ else
+ {
+ max_length=MAX_BLOB_WIDTH;
+ maybe_null=1;
+ }
+}
+
+/*
+** Item_func_repeat::str is carefully written to avoid reallocs
+** as much as possible at the cost of a local buffer
+*/
+
+String *Item_func_repeat::val_str(String *str)
+{
+ uint length,tot_length;
+ char *to;
+ long count= (long) args[1]->val_int();
+ String *res =args[0]->val_str(str);
+
+ if (args[0]->null_value || args[1]->null_value)
+ goto err; // string and/or delim are null
+ null_value=0;
+ if (count <= 0) // For nicer SQL code
+ return &empty_string;
+ if (count == 1) // To avoid reallocs
+ return res;
+ length=res->length();
+ if (length > max_allowed_packet/count)// Safe length check
+ goto err; // Probably an error
+ tot_length= length*(uint) count;
+ if (!(res= alloc_buffer(res,str,&tmp_value,tot_length)))
+ goto err;
+
+ to=(char*) res->ptr()+length;
+ while (--count)
+ {
+ memcpy(to,res->ptr(),length);
+ to+=length;
+ }
+ return (res);
+
+err:
+ null_value=1;
+ return 0;
+}
+
+
+void Item_func_rpad::fix_length_and_dec()
+{
+ if (args[1]->const_item())
+ {
+ uint32 length= (uint32) args[1]->val_int();
+ max_length=max(args[0]->max_length,length);
+ if (max_length >= MAX_BLOB_WIDTH)
+ {
+ max_length=MAX_BLOB_WIDTH;
+ maybe_null=1;
+ }
+ }
+ else
+ {
+ max_length=MAX_BLOB_WIDTH;
+ maybe_null=1;
+ }
+}
+
+
+String *Item_func_rpad::val_str(String *str)
+{
+ uint32 res_length,length_pad;
+ char *to;
+ const char *ptr_pad;
+ int32 count= (int32) args[1]->val_int();
+ String *res =args[0]->val_str(str);
+ String *rpad = args[2]->val_str(str);
+
+ if (!res || args[1]->null_value || !rpad)
+ goto err;
+ null_value=0;
+ if (count <= (int32) (res_length=res->length()))
+ return (res); // String to pad is big enough
+ length_pad= rpad->length();
+ if ((ulong) count > max_allowed_packet || args[2]->null_value || !length_pad)
+ goto err;
+ if (!(res= alloc_buffer(res,str,&tmp_value,count)))
+ goto err;
+
+ to= (char*) res->ptr()+res_length;
+ ptr_pad=rpad->ptr();
+ for (count-= res_length; (uint32) count > length_pad; count-= length_pad)
+ {
+ memcpy(to,ptr_pad,length_pad);
+ to+= length_pad;
+ }
+ memcpy(to,ptr_pad,(size_t) count);
+ return (res);
+
+ err:
+ null_value=1;
+ return 0;
+}
+
+
+void Item_func_lpad::fix_length_and_dec()
+{
+ if (args[1]->const_item())
+ {
+ uint32 length= (uint32) args[1]->val_int();
+ max_length=max(args[0]->max_length,length);
+ if (max_length >= MAX_BLOB_WIDTH)
+ {
+ max_length=MAX_BLOB_WIDTH;
+ maybe_null=1;
+ }
+ }
+ else
+ {
+ max_length=MAX_BLOB_WIDTH;
+ maybe_null=1;
+ }
+}
+
+
+String *Item_func_lpad::val_str(String *str)
+{
+ uint32 res_length,length_pad;
+ char *to;
+ const char *ptr_pad;
+ ulong count= (long) args[1]->val_int();
+ String *res= args[0]->val_str(str);
+ String *lpad= args[2]->val_str(str);
+
+ if (!res || args[1]->null_value || !lpad)
+ goto err;
+ null_value=0;
+ if (count <= (res_length=res->length()))
+ return (res); // String to pad is big enough
+ length_pad= lpad->length();
+ if (count > max_allowed_packet || args[2]->null_value || !length_pad)
+ goto err;
+
+ if (res->alloced_length() < count)
+ {
+ if (str->alloced_length() >= count)
+ {
+ memcpy((char*) str->ptr()+(count-res_length),res->ptr(),res_length);
+ res=str;
+ }
+ else
+ {
+ if (tmp_value.alloc(count))
+ goto err;
+ memcpy((char*) tmp_value.ptr()+(count-res_length),res->ptr(),res_length);
+ res=&tmp_value;
+ }
+ }
+ else
+ bmove_upp((char*) res->ptr()+count,res->ptr()+res_length,res_length);
+ res->length(count);
+
+ to= (char*) res->ptr();
+ ptr_pad= lpad->ptr();
+ for (count-= res_length; count > length_pad; count-= length_pad)
+ {
+ memcpy(to,ptr_pad,length_pad);
+ to+= length_pad;
+ }
+ memcpy(to,ptr_pad,(size_t) count);
+ return (res);
+
+ err:
+ null_value=1;
+ return 0;
+}
+
+
+String *Item_func_conv::val_str(String *str)
+{
+ String *res= args[0]->val_str(str);
+ char *endptr,ans[65],*ptr;
+ longlong dec;
+ int from_base= (int) args[1]->val_int();
+ int to_base= (int) args[2]->val_int();
+
+ if (args[0]->null_value || args[1]->null_value || args[2]->null_value ||
+ abs(to_base) > 36 || abs(to_base) < 2 ||
+ abs(from_base) > 36 || abs(from_base) < 2 || !(res->length()))
+ {
+ null_value=1;
+ return 0;
+ }
+ null_value=0;
+ if (from_base < 0)
+ dec= strtoll(res->c_ptr(),&endptr,-from_base);
+ else
+ dec= (longlong) strtoull(res->c_ptr(),&endptr,from_base);
+ ptr= longlong2str(dec,ans,to_base);
+ if (str->copy(ans,(uint32) (ptr-ans)))
+ return &empty_string;
+ return str;
+}
+
+#include <my_dir.h> // For my_stat
+
+String *Item_load_file::val_str(String *str)
+{
+ String *file_name;
+ File file;
+ MY_STAT stat_info;
+ DBUG_ENTER("load_file");
+
+ if (!(file_name= args[0]->val_str(str)) ||
+ !(current_thd->master_access & FILE_ACL) ||
+ !my_stat(file_name->c_ptr(), &stat_info, MYF(MY_FAE)))
+ goto err;
+ if (!(stat_info.st_mode & S_IROTH))
+ {
+ /* my_error(ER_TEXTFILE_NOT_READABLE, MYF(0), file_name->c_ptr()); */
+ goto err;
+ }
+ if (stat_info.st_size > (long) max_allowed_packet)
+ {
+ /* my_error(ER_TOO_LONG_STRING, MYF(0), file_name->c_ptr()); */
+ goto err;
+ }
+ if (tmp_value.alloc(stat_info.st_size))
+ goto err;
+ if ((file = my_open(file_name->c_ptr(), O_RDONLY, MYF(0))) < 0)
+ goto err;
+ if (my_read(file, (byte*) tmp_value.ptr(), stat_info.st_size, MYF(MY_NABP)))
+ {
+ my_close(file, MYF(0));
+ goto err;
+ }
+ tmp_value.length(stat_info.st_size);
+ my_close(file, MYF(0));
+ null_value = 0;
+ return &tmp_value;
+
+err:
+ null_value = 1;
+ DBUG_RETURN(0);
+}
+
+
+String* Item_func_export_set::val_str(String* str)
+{
+ ulonglong the_set = (ulonglong) args[0]->val_int();
+ String yes_buf, *yes;
+ yes = args[1]->val_str(&yes_buf);
+ String no_buf, *no;
+ no = args[2]->val_str(&no_buf);
+ String *sep = NULL, sep_buf ;
+
+ uint num_set_values = 64;
+ ulonglong mask = 0x1;
+ str->length(0);
+
+ /* Check if some argument is a NULL value */
+ if (args[0]->null_value || args[1]->null_value || args[2]->null_value)
+ {
+ null_value=1;
+ return 0;
+ }
+ switch(arg_count) {
+ case 5:
+ num_set_values = (uint) args[4]->val_int();
+ if (num_set_values > 64)
+ num_set_values=64;
+ if (args[4]->null_value)
+ {
+ null_value=1;
+ return 0;
+ }
+ /* Fall through */
+ case 4:
+ if (!(sep = args[3]->val_str(&sep_buf))) // Only true if NULL
+ {
+ null_value=1;
+ return 0;
+ }
+ break;
+ case 3:
+ sep_buf.set(",", 1);
+ sep = &sep_buf;
+ }
+ null_value=0;
+
+ for (uint i = 0; i < num_set_values; i++, mask = (mask << 1))
+ {
+ if (the_set & mask)
+ str->append(*yes);
+ else
+ str->append(*no);
+ if(i != num_set_values - 1)
+ str->append(*sep);
+ }
+ return str;
+}
+
+void Item_func_export_set::fix_length_and_dec()
+{
+ uint length=max(args[1]->max_length,args[2]->max_length);
+ uint sep_length=(arg_count > 3 ? args[3]->max_length : 1);
+ max_length=length*64+sep_length*63;
+}
+
+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
+ // some val function on it if args[0] is not a constant!
+ if ((null_value=args[0]->null_value))
+ return 0; // Null value
+ str->length(0);
+ int8store(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-- ) ;
+ num[3]='.';
+ while (p-- > buf)
+ {
+ uint c = *p;
+ uint n1,n2; // Try to avoid divisions
+ n1= c / 100; // 100 digits
+ c-= n1*100;
+ n2= c / 10; // 10 digits
+ c-=n2*10; // last digit
+ num[0]=(char) n1+'0';
+ num[1]=(char) n2+'0';
+ num[2]=(char) c+'0';
+ uint length=(n1 ? 4 : n2 ? 3 : 2); // Remove pre-zero
+
+ (void) str->append(num+4-length,length);
+ }
+ str->length(str->length()-1); // Remove last '.';
+ return str;
+}
diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h
new file mode 100644
index 00000000000..97bc1063db3
--- /dev/null
+++ b/sql/item_strfunc.h
@@ -0,0 +1,429 @@
+/* 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 */
+
+
+/* This file defines all string functions */
+
+#ifdef __GNUC__
+#pragma interface /* gcc class implementation */
+#endif
+
+class Item_str_func :public Item_func
+{
+public:
+ Item_str_func() :Item_func() { decimals=NOT_FIXED_DEC; }
+ Item_str_func(Item *a) :Item_func(a) {decimals=NOT_FIXED_DEC; }
+ Item_str_func(Item *a,Item *b) :Item_func(a,b) { decimals=NOT_FIXED_DEC; }
+ Item_str_func(Item *a,Item *b,Item *c) :Item_func(a,b,c) { decimals=NOT_FIXED_DEC; }
+ Item_str_func(Item *a,Item *b,Item *c,Item *d) :Item_func(a,b,c,d) {decimals=NOT_FIXED_DEC; }
+ Item_str_func(Item *a,Item *b,Item *c,Item *d, Item* e) :Item_func(a,b,c,d,e) {decimals=NOT_FIXED_DEC; }
+ Item_str_func(List<Item> &list) :Item_func(list) {decimals=NOT_FIXED_DEC; }
+ longlong val_int();
+ double val();
+ enum Item_result result_type () const { return STRING_RESULT; }
+ void left_right_max_length();
+};
+
+class Item_func_md5 :public Item_str_func
+{
+ String tmp_value;
+public:
+ Item_func_md5(Item *a) :Item_str_func(a) {}
+ String *val_str(String *);
+ void fix_length_and_dec();
+ const char *func_name() const { return "md5"; }
+};
+
+class Item_func_concat :public Item_str_func
+{
+ String tmp_value;
+public:
+ Item_func_concat(List<Item> &list) :Item_str_func(list) {}
+ Item_func_concat(Item *a,Item *b) :Item_str_func(a,b) {}
+ String *val_str(String *);
+ void fix_length_and_dec();
+ const char *func_name() const { return "concat"; }
+};
+
+class Item_func_concat_ws :public Item_str_func
+{
+ Item *separator;
+ String tmp_value;
+
+public:
+ Item_func_concat_ws(Item *a,List<Item> &list)
+ :Item_str_func(list),separator(a) {}
+ ~Item_func_concat_ws() { delete separator; }
+ String *val_str(String *);
+ void fix_length_and_dec();
+ const char *func_name() const { return "concat_ws"; }
+};
+
+class Item_func_reverse :public Item_str_func
+{
+public:
+ Item_func_reverse(Item *a) :Item_str_func(a) {}
+ String *val_str(String *);
+ void fix_length_and_dec();
+};
+
+
+class Item_func_replace :public Item_str_func
+{
+ String tmp_value,tmp_value2;
+public:
+ Item_func_replace(Item *org,Item *find,Item *replace)
+ :Item_str_func(org,find,replace) {}
+ String *val_str(String *);
+ void fix_length_and_dec();
+ const char *func_name() const { return "replace"; }
+};
+
+
+class Item_func_insert :public Item_str_func
+{
+ String tmp_value;
+public:
+ Item_func_insert(Item *org,Item *start,Item *length,Item *new_str)
+ :Item_str_func(org,start,length,new_str) {}
+ String *val_str(String *);
+ void fix_length_and_dec();
+ const char *func_name() const { return "insert"; }
+};
+
+
+class Item_str_conv :public Item_str_func
+{
+public:
+ Item_str_conv(Item *item) :Item_str_func(item) {}
+ void fix_length_and_dec() { max_length = args[0]->max_length; }
+};
+
+
+class Item_func_lcase :public Item_str_conv
+{
+public:
+ Item_func_lcase(Item *item) :Item_str_conv(item) {}
+ String *val_str(String *);
+ const char *func_name() const { return "lcase"; }
+};
+
+class Item_func_ucase :public Item_str_conv
+{
+public:
+ Item_func_ucase(Item *item) :Item_str_conv(item) {}
+ String *val_str(String *);
+ const char *func_name() const { return "ucase"; }
+};
+
+
+class Item_func_left :public Item_str_func
+{
+public:
+ Item_func_left(Item *a,Item *b) :Item_str_func(a,b) {}
+ String *val_str(String *);
+ void fix_length_and_dec();
+ const char *func_name() const { return "left"; }
+};
+
+
+class Item_func_right :public Item_str_func
+{
+ String tmp_value;
+public:
+ Item_func_right(Item *a,Item *b) :Item_str_func(a,b) {}
+ String *val_str(String *);
+ void fix_length_and_dec();
+ const char *func_name() const { return "right"; }
+};
+
+
+class Item_func_substr :public Item_str_func
+{
+ String tmp_value;
+public:
+ Item_func_substr(Item *a,Item *b) :Item_str_func(a,b) {}
+ Item_func_substr(Item *a,Item *b,Item *c) :Item_str_func(a,b,c) {}
+ String *val_str(String *);
+ void fix_length_and_dec();
+ const char *func_name() const { return "substr"; }
+};
+
+
+class Item_func_substr_index :public Item_str_func
+{
+ String tmp_value;
+public:
+ Item_func_substr_index(Item *a,Item *b,Item *c) :Item_str_func(a,b,c) {}
+ String *val_str(String *);
+ void fix_length_and_dec() { max_length= args[0]->max_length; }
+ const char *func_name() const { return "substr_index"; }
+};
+
+
+class Item_func_ltrim :public Item_str_func
+{
+ String tmp_value;
+public:
+ Item_func_ltrim(Item *a,Item *b) :Item_str_func(a,b) {}
+ String *val_str(String *);
+ void fix_length_and_dec() { max_length= args[0]->max_length; }
+ const char *func_name() const { return "ltrim"; }
+};
+
+
+class Item_func_rtrim :public Item_str_func
+{
+ String tmp_value;
+public:
+ Item_func_rtrim(Item *a,Item *b) :Item_str_func(a,b) {}
+ String *val_str(String *);
+ void fix_length_and_dec() { max_length= args[0]->max_length; }
+ const char *func_name() const { return "rtrim"; }
+};
+
+class Item_func_trim :public Item_str_func
+{
+ String tmp_value;
+public:
+ Item_func_trim(Item *a,Item *b) :Item_str_func(a,b) {}
+ String *val_str(String *);
+ void fix_length_and_dec() { max_length= args[0]->max_length; }
+ const char *func_name() const { return "trim"; }
+};
+
+
+class Item_func_password :public Item_str_func
+{
+ char tmp_value[17];
+public:
+ Item_func_password(Item *a) :Item_str_func(a) {}
+ String *val_str(String *);
+ void fix_length_and_dec() { max_length = 16; }
+ const char *func_name() const { return "password"; }
+};
+
+class Item_func_encrypt :public Item_str_func
+{
+ String tmp_value;
+public:
+ Item_func_encrypt(Item *a) :Item_str_func(a) {}
+ 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; }
+};
+
+#include "sql_crypt.h"
+
+class Item_func_encode :public Item_str_func
+{
+ protected:
+ SQL_CRYPT sql_crypt;
+public:
+ Item_func_encode(Item *a, char *seed):
+ Item_str_func(a),sql_crypt(seed) {}
+ String *val_str(String *);
+ void fix_length_and_dec();
+};
+
+class Item_func_decode :public Item_func_encode
+{
+public:
+ Item_func_decode(Item *a, char *seed): Item_func_encode(a,seed) {}
+ String *val_str(String *);
+};
+
+
+class Item_func_database :public Item_str_func
+{
+public:
+ Item_func_database() {}
+ String *val_str(String *);
+ void fix_length_and_dec() { max_length= MAX_FIELD_NAME; }
+ const char *func_name() const { return "database"; }
+};
+
+class Item_func_user :public Item_str_func
+{
+public:
+ Item_func_user() {}
+ String *val_str(String *);
+ void fix_length_and_dec() { max_length= USERNAME_LENGTH+HOSTNAME_LENGTH+1; }
+ const char *func_name() const { return "user"; }
+};
+
+
+class Item_func_soundex :public Item_str_func
+{
+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"; }
+};
+
+
+class Item_func_elt :public Item_str_func
+{
+ Item *item;
+
+public:
+ Item_func_elt(Item *a,List<Item> &list) :Item_str_func(list),item(a) {}
+ ~Item_func_elt() { delete item; }
+ double val();
+ longlong val_int();
+ String *val_str(String *str);
+ bool fix_fields(THD *thd,struct st_table_list *tlist)
+ {
+ return (item->fix_fields(thd,tlist) || Item_func::fix_fields(thd,tlist));
+ }
+ void fix_length_and_dec();
+ void update_used_tables();
+ const char *func_name() const { return "elt"; }
+};
+
+
+class Item_func_make_set :public Item_str_func
+{
+ Item *item;
+ String tmp_str;
+
+public:
+ Item_func_make_set(Item *a,List<Item> &list) :Item_str_func(list),item(a) {}
+ ~Item_func_make_set() { delete item; }
+ String *val_str(String *str);
+ bool fix_fields(THD *thd,struct st_table_list *tlist)
+ {
+ return (item->fix_fields(thd,tlist) || Item_func::fix_fields(thd,tlist));
+ }
+ void fix_length_and_dec();
+ void update_used_tables();
+ const char *func_name() const { return "make_set"; }
+};
+
+
+class Item_func_format :public Item_str_func
+{
+ String tmp_str;
+public:
+ Item_func_format(Item *org,int dec);
+ String *val_str(String *);
+ void fix_length_and_dec()
+ {
+ max_length=args[0]->max_length+(args[0]->max_length-args[0]->decimals)/3;
+ }
+ const char *func_name() const { return "format"; }
+};
+
+
+class Item_func_char :public Item_str_func
+{
+public:
+ Item_func_char(List<Item> &list) :Item_str_func(list) {}
+ String *val_str(String *);
+ void fix_length_and_dec() { maybe_null=0; max_length=arg_count; binary=0;}
+ const char *func_name() const { return "char"; }
+};
+
+
+class Item_func_repeat :public Item_str_func
+{
+ String tmp_value;
+public:
+ Item_func_repeat(Item *arg1,Item *arg2) :Item_str_func(arg1,arg2) {}
+ String *val_str(String *);
+ void fix_length_and_dec();
+ const char *func_name() const { return "repeat"; }
+};
+
+
+class Item_func_rpad :public Item_str_func
+{
+ String tmp_value;
+public:
+ Item_func_rpad(Item *arg1,Item *arg2,Item *arg3)
+ :Item_str_func(arg1,arg2,arg3) {}
+ String *val_str(String *);
+ void fix_length_and_dec();
+ const char *func_name() const { return "rpad"; }
+};
+
+
+class Item_func_lpad :public Item_str_func
+{
+ String tmp_value;
+public:
+ Item_func_lpad(Item *arg1,Item *arg2,Item *arg3)
+ :Item_str_func(arg1,arg2,arg3) {}
+ String *val_str(String *);
+ void fix_length_and_dec();
+ const char *func_name() const { return "lpad"; }
+};
+
+
+class Item_func_conv :public Item_str_func
+{
+public:
+ Item_func_conv(Item *a,Item *b,Item *c) :Item_str_func(a,b,c) {}
+ const char *func_name() const { return "conv"; }
+ String *val_str(String *);
+ void fix_length_and_dec() { decimals=0; max_length=64; }
+};
+
+class Item_func_binary :public Item_str_func
+{
+public:
+ Item_func_binary(Item *a) :Item_str_func(a) {}
+ const char *func_name() const { return "binary"; }
+ String *val_str(String *a) { return (args[0]->val_str(a)); }
+ void fix_length_and_dec() { binary=1; max_length=args[0]->max_length; }
+ void print(String *str) { print_op(str); }
+};
+
+
+class Item_load_file :public Item_str_func
+{
+ String tmp_value;
+public:
+ Item_load_file(Item *a) :Item_str_func(a) {}
+ String *val_str(String *);
+ const char *func_name() const { return "load_file"; }
+ void fix_length_and_dec()
+ { binary=1; maybe_null=1; max_length=MAX_BLOB_WIDTH;}
+};
+
+
+class Item_func_export_set: public Item_str_func
+{
+ public:
+ Item_func_export_set(Item *a,Item *b,Item* c) :Item_str_func(a,b,c) {}
+ Item_func_export_set(Item *a,Item *b,Item* c,Item* d) :Item_str_func(a,b,c,d) {}
+ Item_func_export_set(Item *a,Item *b,Item* c,Item* d,Item* e) :Item_str_func(a,b,c,d,e) {}
+ String *val_str(String *str);
+ void fix_length_and_dec();
+ const char *func_name() const { return "export_set"; }
+};
+
+ class Item_func_inet_ntoa : public Item_str_func
+{
+public:
+ Item_func_inet_ntoa(Item *a) :Item_str_func(a)
+ {
+ }
+ String* val_str(String* str);
+ const char *func_name() const { return "inet_ntoa"; }
+ void fix_length_and_dec() { decimals = 0; max_length=3*8+7; }
+};
diff --git a/sql/item_sum.cc b/sql/item_sum.cc
new file mode 100644
index 00000000000..408e5d941ae
--- /dev/null
+++ b/sql/item_sum.cc
@@ -0,0 +1,941 @@
+/* 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 */
+
+
+/* Sum functions (COUNT, MIN...) */
+
+#ifdef __GNUC__
+#pragma implementation // gcc: Class implementation
+#endif
+
+#include "mysql_priv.h"
+
+
+Item_sum::Item_sum(List<Item> &list)
+{
+ arg_count=list.elements;
+ if ((args=(Item**) sql_alloc(sizeof(Item*)*arg_count)))
+ {
+ uint i=0;
+ List_iterator<Item> li(list);
+ Item *item;
+
+ while ((item=li++))
+ {
+ args[i++]= item;
+ }
+ }
+ with_sum_func=1;
+ list.empty(); // Fields are used
+}
+
+
+void Item_sum::make_field(Send_field *tmp_field)
+{
+ if (args[0]->type() == Item::FIELD_ITEM && keep_field_type())
+ ((Item_field*) args[0])->field->make_field(tmp_field);
+ else
+ {
+ tmp_field->flags=0;
+ if (!maybe_null)
+ tmp_field->flags|= NOT_NULL_FLAG;
+ tmp_field->length=max_length;
+ tmp_field->decimals=decimals;
+ tmp_field->type=(result_type() == INT_RESULT ? FIELD_TYPE_LONG :
+ result_type() == REAL_RESULT ? FIELD_TYPE_DOUBLE :
+ FIELD_TYPE_VAR_STRING);
+ }
+ tmp_field->table_name=(char*)"";
+ tmp_field->col_name=name;
+}
+
+void Item_sum::print(String *str)
+{
+ str->append(func_name());
+ str->append('(');
+ for (uint i=0 ; i < arg_count ; i++)
+ {
+ if (i)
+ str->append(',');
+ args[i]->print(str);
+ }
+ str->append(')');
+}
+
+void Item_sum::fix_num_length_and_dec()
+{
+ decimals=0;
+ for (uint i=0 ; i < arg_count ; i++)
+ set_if_bigger(decimals,args[i]->decimals);
+ max_length=float_length(decimals);
+}
+
+
+String *
+Item_sum_num::val_str(String *str)
+{
+ double nr=val();
+ if (null_value)
+ return 0;
+ str->set(nr,decimals);
+ return str;
+}
+
+
+String *
+Item_sum_int::val_str(String *str)
+{
+ longlong nr=val_int();
+ if (null_value)
+ return 0;
+ char buff[21];
+ uint length= (uint) (longlong10_to_str(nr,buff,-10)-buff);
+ str->copy(buff,length);
+ return str;
+}
+
+
+bool
+Item_sum_num::fix_fields(THD *thd,TABLE_LIST *tables)
+{
+ if (!thd->allow_sum_func)
+ {
+ my_error(ER_INVALID_GROUP_FUNC_USE,MYF(0));
+ return 1;
+ }
+ thd->allow_sum_func=0; // No included group funcs
+ decimals=0;
+ maybe_null=0;
+ for (uint i=0 ; i < arg_count ; i++)
+ {
+ if (args[i]->fix_fields(thd,tables))
+ return 1;
+ if (decimals < args[i]->decimals)
+ decimals=args[i]->decimals;
+ maybe_null |= args[i]->maybe_null;
+ }
+ result_field=0;
+ max_length=float_length(decimals);
+ null_value=1;
+ fix_length_and_dec();
+ thd->allow_sum_func=1; // Allow group functions
+ return 0;
+}
+
+
+bool
+Item_sum_hybrid::fix_fields(THD *thd,TABLE_LIST *tables)
+{
+ Item *item=args[0];
+ if (!thd->allow_sum_func)
+ {
+ my_error(ER_INVALID_GROUP_FUNC_USE,MYF(0));
+ return 1;
+ }
+ thd->allow_sum_func=0; // No included group funcs
+ if (item->fix_fields(thd,tables))
+ return 1;
+ hybrid_type=item->result_type();
+ if (hybrid_type == INT_RESULT)
+ max_length=21;
+ else if (hybrid_type == REAL_RESULT)
+ max_length=float_length(decimals);
+ else
+ max_length=item->max_length;
+ decimals=item->decimals;
+ maybe_null=item->maybe_null;
+ binary=item->binary;
+ result_field=0;
+ null_value=1;
+ fix_length_and_dec();
+ thd->allow_sum_func=1; // Allow group functions
+ return 0;
+}
+
+
+/***********************************************************************
+** reset and add of sum_func
+***********************************************************************/
+
+void Item_sum_sum::reset()
+{
+ null_value=0; sum=0.0; Item_sum_sum::add();
+}
+
+bool Item_sum_sum::add()
+{
+ sum+=args[0]->val();
+ return 0;
+}
+
+double Item_sum_sum::val()
+{
+ return sum;
+}
+
+
+void Item_sum_count::reset()
+{
+ count=0; add();
+}
+
+bool Item_sum_count::add()
+{
+ if (!args[0]->maybe_null)
+ count++;
+ else
+ {
+ (void) args[0]->val_int();
+ if (!args[0]->null_value)
+ count++;
+ }
+ return 0;
+}
+
+longlong Item_sum_count::val_int()
+{
+ return (longlong) count;
+}
+
+/*
+** Avgerage
+*/
+
+void Item_sum_avg::reset()
+{
+ sum=0.0; count=0; Item_sum_avg::add();
+}
+
+bool Item_sum_avg::add()
+{
+ double nr=args[0]->val();
+ if (!args[0]->null_value)
+ {
+ sum+=nr;
+ count++;
+ }
+ return 0;
+}
+
+double Item_sum_avg::val()
+{
+ if (!count)
+ {
+ null_value=1;
+ return 0.0;
+ }
+ null_value=0;
+ return sum/ulonglong2double(count);
+}
+
+
+/*
+** Standard deviation
+*/
+
+void Item_sum_std::reset()
+{
+ sum=sum_sqr=0.0; count=0; (void) Item_sum_std::add();
+}
+
+bool Item_sum_std::add()
+{
+ double nr=args[0]->val();
+ if (!args[0]->null_value)
+ {
+ sum+=nr;
+ sum_sqr+=nr*nr;
+ count++;
+ }
+ return 0;
+}
+
+double Item_sum_std::val()
+{
+ if (!count)
+ {
+ null_value=1;
+ return 0.0;
+ }
+ null_value=0;
+ /* Avoid problems when the precision isn't good enough */
+ double tmp=ulonglong2double(count);
+ double tmp2=(sum_sqr - sum*sum/tmp)/tmp;
+ return tmp2 <= 0.0 ? 0.0 : sqrt(tmp2);
+}
+
+
+void Item_sum_std::reset_field()
+{
+ double nr=args[0]->val();
+ char *res=result_field->ptr;
+
+ if (args[0]->null_value)
+ bzero(res,sizeof(double)*2+sizeof(longlong));
+ else
+ {
+ float8store(res,nr);
+ nr*=nr;
+ float8store(res+sizeof(double),nr);
+ longlong tmp=1;
+ int8store(res+sizeof(double)*2,tmp);
+ }
+}
+
+void Item_sum_std::update_field(int offset)
+{
+ double nr,old_nr,old_sqr;
+ longlong field_count;
+ char *res=result_field->ptr;
+
+ float8get(old_nr,res+offset);
+ float8get(old_sqr,res+offset+sizeof(double));
+ field_count=sint8korr(res+offset+sizeof(double)*2);
+
+ nr=args[0]->val();
+ if (!args[0]->null_value)
+ {
+ old_nr+=nr;
+ old_sqr+=nr*nr;
+ field_count++;
+ }
+ float8store(res,old_nr);
+ float8store(res+sizeof(double),old_sqr);
+ int8store(res+sizeof(double)*2,field_count);
+}
+
+/* min & max */
+
+double Item_sum_hybrid::val()
+{
+ if (null_value)
+ return 0.0;
+ if (hybrid_type == STRING_RESULT)
+ {
+ String *res; res=val_str(&str_value);
+ return res ? atof(res->c_ptr()) : 0.0;
+ }
+ return sum;
+}
+
+
+String *
+Item_sum_hybrid::val_str(String *str)
+{
+ if (null_value)
+ return 0;
+ if (hybrid_type == STRING_RESULT)
+ return &value;
+ str->set(sum,decimals);
+ return str;
+}
+
+
+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
+ {
+ String *result=args[0]->val_str(&tmp_value);
+ if (!args[0]->null_value &&
+ (null_value ||
+ (binary ? stringcmp(&value,result) : sortcmp(&value,result)) > 0))
+ {
+ value.copy(*result);
+ null_value=0;
+ }
+ }
+ return 0;
+}
+
+
+bool Item_sum_max::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
+ {
+ String *result=args[0]->val_str(&tmp_value);
+ if (!args[0]->null_value &&
+ (null_value ||
+ (binary ? stringcmp(&value,result) : sortcmp(&value,result)) < 0))
+ {
+ value.copy(*result);
+ null_value=0;
+ }
+ }
+ return 0;
+}
+
+
+/* bit_or and bit_and */
+
+longlong Item_sum_bit::val_int()
+{
+ return (longlong) bits;
+}
+
+void Item_sum_bit::reset()
+{
+ bits=reset_bits; add();
+}
+
+bool Item_sum_or::add()
+{
+ ulonglong value= (ulonglong) args[0]->val_int();
+ if (!args[0]->null_value)
+ bits|=value;
+ return 0;
+}
+
+bool Item_sum_and::add()
+{
+ ulonglong value= (ulonglong) args[0]->val_int();
+ if (!args[0]->null_value)
+ bits&=value;
+ return 0;
+}
+
+/************************************************************************
+** reset result of a Item_sum with is saved in a tmp_table
+*************************************************************************/
+
+void Item_sum_num::reset_field()
+{
+ double nr=args[0]->val();
+ char *res=result_field->ptr;
+
+ if (maybe_null)
+ {
+ if (args[0]->null_value)
+ {
+ nr=0.0;
+ result_field->set_null();
+ }
+ else
+ result_field->set_notnull();
+ }
+ float8store(res,nr);
+}
+
+
+void Item_sum_hybrid::reset_field()
+{
+ if (hybrid_type == STRING_RESULT)
+ {
+ char buff[MAX_FIELD_WIDTH];
+ String tmp(buff,sizeof(buff)),*res;
+
+ res=args[0]->val_str(&tmp);
+ if (args[0]->null_value)
+ {
+ result_field->set_null();
+ result_field->reset();
+ }
+ else
+ {
+ result_field->set_notnull();
+ result_field->store(res->ptr(),res->length());
+ }
+ }
+ else if (hybrid_type == INT_RESULT)
+ {
+ longlong nr=args[0]->val_int();
+
+ if (maybe_null)
+ {
+ if (args[0]->null_value)
+ {
+ nr=0;
+ result_field->set_null();
+ }
+ else
+ result_field->set_notnull();
+ }
+ result_field->store(nr);
+ }
+ else // REAL_RESULT
+ {
+ double nr=args[0]->val();
+
+ if (maybe_null)
+ {
+ if (args[0]->null_value)
+ {
+ nr=0.0;
+ result_field->set_null();
+ }
+ else
+ result_field->set_notnull();
+ }
+ result_field->store(nr);
+ }
+}
+
+
+void Item_sum_sum::reset_field()
+{
+ double nr=args[0]->val(); // Nulls also return 0
+ float8store(result_field->ptr,nr);
+ null_value=0;
+ result_field->set_notnull();
+}
+
+
+void Item_sum_count::reset_field()
+{
+ char *res=result_field->ptr;
+ longlong nr=0;
+
+ if (!args[0]->maybe_null)
+ nr=1;
+ else
+ {
+ (void) args[0]->val_int();
+ if (!args[0]->null_value)
+ nr=1;
+ }
+ int8store(res,nr);
+}
+
+
+void Item_sum_avg::reset_field()
+{
+ double nr=args[0]->val();
+ char *res=result_field->ptr;
+
+ if (args[0]->null_value)
+ bzero(res,sizeof(double)+sizeof(longlong));
+ else
+ {
+ float8store(res,nr);
+ res+=sizeof(double);
+ longlong tmp=1;
+ int8store(res,tmp);
+ }
+}
+
+void Item_sum_bit::reset_field()
+{
+ char *res=result_field->ptr;
+ ulonglong nr=(ulonglong) args[0]->val_int();
+ int8store(res,nr);
+}
+
+/*
+** calc next value and merge it with field_value
+*/
+
+void Item_sum_sum::update_field(int offset)
+{
+ double old_nr,nr;
+ char *res=result_field->ptr;
+
+ float8get(old_nr,res+offset);
+ nr=args[0]->val();
+ if (!args[0]->null_value)
+ old_nr+=nr;
+ float8store(res,old_nr);
+}
+
+
+void Item_sum_count::update_field(int offset)
+{
+ longlong nr;
+ char *res=result_field->ptr;
+
+ nr=sint8korr(res+offset);
+ if (!args[0]->maybe_null)
+ nr++;
+ else
+ {
+ (void) args[0]->val_int();
+ if (!args[0]->null_value)
+ nr++;
+ }
+ int8store(res,nr);
+}
+
+
+void Item_sum_avg::update_field(int offset)
+{
+ double nr,old_nr;
+ longlong field_count;
+ char *res=result_field->ptr;
+
+ float8get(old_nr,res+offset);
+ field_count=sint8korr(res+offset+sizeof(double));
+
+ nr=args[0]->val();
+ if (!args[0]->null_value)
+ {
+ old_nr+=nr;
+ field_count++;
+ }
+ float8store(res,old_nr);
+ res+=sizeof(double);
+ int8store(res,field_count);
+}
+
+void Item_sum_hybrid::update_field(int offset)
+{
+ if (hybrid_type == STRING_RESULT)
+ min_max_update_str_field(offset);
+ else if (hybrid_type == INT_RESULT)
+ min_max_update_int_field(offset);
+ else
+ min_max_update_real_field(offset);
+}
+
+
+void
+Item_sum_hybrid::min_max_update_str_field(int offset)
+{
+ String *res_str=args[0]->val_str(&value);
+
+ if (args[0]->null_value)
+ result_field->copy_from_tmp(offset); // Use old value
+ else
+ {
+ res_str->strip_sp();
+ result_field->ptr+=offset; // Get old max/min
+ result_field->val_str(&tmp_value,&tmp_value);
+ result_field->ptr-=offset;
+
+ if (result_field->is_null() ||
+ (cmp_sign * (binary ? stringcmp(res_str,&tmp_value) :
+ sortcmp(res_str,&tmp_value)) < 0))
+ result_field->store(res_str->ptr(),res_str->length());
+ else
+ { // Use old value
+ char *res=result_field->ptr;
+ memcpy(res,res+offset,result_field->pack_length());
+ }
+ result_field->set_notnull();
+ }
+}
+
+
+void
+Item_sum_hybrid::min_max_update_real_field(int offset)
+{
+ double nr,old_nr;
+
+ result_field->ptr+=offset;
+ old_nr=result_field->val_real();
+ nr=args[0]->val();
+ if (!args[0]->null_value)
+ {
+ if (result_field->is_null(offset) ||
+ (cmp_sign > 0 ? old_nr > nr : old_nr < nr))
+ old_nr=nr;
+ result_field->set_notnull();
+ }
+ else if (result_field->is_null(offset))
+ result_field->set_null();
+ result_field->ptr-=offset;
+ result_field->store(old_nr);
+}
+
+
+void
+Item_sum_hybrid::min_max_update_int_field(int offset)
+{
+ longlong nr,old_nr;
+
+ result_field->ptr+=offset;
+ old_nr=result_field->val_int();
+ 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))
+ old_nr=nr;
+ result_field->set_notnull();
+ }
+ else if (result_field->is_null(offset))
+ result_field->set_null();
+ result_field->ptr-=offset;
+ result_field->store(old_nr);
+}
+
+
+void Item_sum_or::update_field(int offset)
+{
+ ulonglong nr;
+ char *res=result_field->ptr;
+
+ nr=uint8korr(res+offset);
+ nr|= (ulonglong) args[0]->val_int();
+ int8store(res,nr);
+}
+
+
+void Item_sum_and::update_field(int offset)
+{
+ ulonglong nr;
+ char *res=result_field->ptr;
+
+ nr=uint8korr(res+offset);
+ nr&= (ulonglong) args[0]->val_int();
+ int8store(res,nr);
+}
+
+
+Item_avg_field::Item_avg_field(Item_sum_avg *item)
+{
+ name=item->name;
+ decimals=item->decimals;
+ max_length=item->max_length;
+ field=item->result_field;
+ maybe_null=1;
+}
+
+double Item_avg_field::val()
+{
+ double nr;
+ longlong count;
+ float8get(nr,field->ptr);
+ char *res=(field->ptr+sizeof(double));
+ count=sint8korr(res);
+
+ if (!count)
+ {
+ null_value=1;
+ return 0.0;
+ }
+ null_value=0;
+ return nr/(double) count;
+}
+
+String *Item_avg_field::val_str(String *str)
+{
+ double nr=Item_avg_field::val();
+ if (null_value)
+ return 0;
+ str->set(nr,decimals);
+ return str;
+}
+
+Item_std_field::Item_std_field(Item_sum_std *item)
+{
+ name=item->name;
+ decimals=item->decimals;
+ max_length=item->max_length;
+ field=item->result_field;
+ maybe_null=1;
+}
+
+double Item_std_field::val()
+{
+ double sum,sum_sqr;
+ longlong count;
+ float8get(sum,field->ptr);
+ float8get(sum_sqr,(field->ptr+sizeof(double)));
+ count=sint8korr(field->ptr+sizeof(double)*2);
+
+ if (!count)
+ {
+ null_value=1;
+ return 0.0;
+ }
+ null_value=0;
+ double tmp= (double) count;
+ double tmp2=(sum_sqr - sum*sum/tmp)/tmp;
+ return tmp2 <= 0.0 ? 0.0 : sqrt(tmp2);
+}
+
+String *Item_std_field::val_str(String *str)
+{
+ double nr=val();
+ if (null_value)
+ return 0;
+ str->set(nr,decimals);
+ return str;
+}
+
+/****************************************************************************
+** COUNT(DISTINCT ...)
+****************************************************************************/
+
+#include "sql_select.h"
+
+Item_sum_count_distinct::~Item_sum_count_distinct()
+{
+ if (table)
+ free_tmp_table(current_thd, table);
+ delete tmp_table_param;
+}
+
+
+bool Item_sum_count_distinct::fix_fields(THD *thd,TABLE_LIST *tables)
+{
+ if (Item_sum_num::fix_fields(thd,tables) ||
+ !(tmp_table_param= new TMP_TABLE_PARAM))
+ return 1;
+ return 0;
+}
+
+bool Item_sum_count_distinct::setup(THD *thd)
+{
+ List<Item> list;
+ /* Create a table with an unique key over all parameters */
+ for (uint i=0; i < arg_count ; i++)
+ if (list.push_back(args[i]))
+ return 1;
+ count_field_types(tmp_table_param,list);
+ if (table)
+ {
+ free_tmp_table(thd, table);
+ tmp_table_param->cleanup();
+ }
+ if (!(table=create_tmp_table(thd, tmp_table_param, list, (ORDER*) 0, 1,
+ 0, 0, current_lex->options | thd->options)))
+ return 1;
+ table->file->extra(HA_EXTRA_NO_ROWS); // Don't update rows
+ return 0;
+}
+
+
+void Item_sum_count_distinct::reset()
+{
+ table->file->extra(HA_EXTRA_NO_CACHE);
+ table->file->delete_all_rows();
+ table->file->extra(HA_EXTRA_WRITE_CACHE);
+ (void) add();
+}
+
+bool Item_sum_count_distinct::add()
+{
+ int error;
+ copy_fields(tmp_table_param);
+ copy_funcs(tmp_table_param->funcs);
+
+ if ((error=table->file->write_row(table->record[0])))
+ {
+ if (error != HA_ERR_FOUND_DUPP_KEY &&
+ error != HA_ERR_FOUND_DUPP_UNIQUE)
+ {
+ if (create_myisam_from_heap(table, tmp_table_param, error,1))
+ return 1; // Not a table_is_full error
+ }
+ }
+ return 0;
+}
+
+longlong Item_sum_count_distinct::val_int()
+{
+ if (!table) // Empty query
+ return LL(0);
+ table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
+ return table->file->records;
+}
+
+/****************************************************************************
+** Functions to handle dynamic loadable aggregates
+** Original source by: Alexis Mikhailov <root@medinf.chuvashia.su>
+** Adapted for UDAs by: Andreas F. Bobak <bobak@relog.ch>.
+** Rewritten by: Monty.
+****************************************************************************/
+
+#ifdef HAVE_DLOPEN
+
+void Item_udf_sum::reset()
+{
+ DBUG_ENTER("Item_udf_sum::reset");
+ udf.reset(&null_value);
+ DBUG_VOID_RETURN;
+}
+
+bool Item_udf_sum::add()
+{
+ DBUG_ENTER("Item_udf_sum::reset");
+ udf.add(&null_value);
+ DBUG_RETURN(0);
+}
+
+double Item_sum_udf_float::val()
+{
+ DBUG_ENTER("Item_sum_udf_float::val");
+ DBUG_PRINT("info",("result_type: %d arg_count: %d",
+ args[0]->result_type(), arg_count));
+ DBUG_RETURN(udf.val(&null_value));
+}
+
+String *Item_sum_udf_float::val_str(String *str)
+{
+ double nr=val();
+ if (null_value)
+ return 0; /* purecov: inspected */
+ else
+ str->set(nr,decimals);
+ return str;
+}
+
+
+longlong Item_sum_udf_int::val_int()
+{
+ DBUG_ENTER("Item_sum_udf_int::val_int");
+ DBUG_PRINT("info",("result_type: %d arg_count: %d",
+ args[0]->result_type(), arg_count));
+ DBUG_RETURN(udf.val_int(&null_value));
+}
+
+String *Item_sum_udf_int::val_str(String *str)
+{
+ longlong nr=val_int();
+ if (null_value)
+ return 0;
+ else
+ str->set(nr);
+ return str;
+}
+
+/* Default max_length is max argument length */
+
+void Item_sum_udf_str::fix_length_and_dec()
+{
+ DBUG_ENTER("Item_sum_udf_str::fix_length_and_dec");
+ max_length=0;
+ for (uint i = 0; i < arg_count; i++)
+ set_if_bigger(max_length,args[i]->max_length);
+ DBUG_VOID_RETURN;
+}
+
+String *Item_sum_udf_str::val_str(String *str)
+{
+ DBUG_ENTER("Item_sum_udf_str::str");
+ String *res=udf.val_str(str,&str_value);
+ null_value = !res;
+ DBUG_RETURN(res);
+}
+
+#endif /* HAVE_DLOPEN */
diff --git a/sql/item_sum.h b/sql/item_sum.h
new file mode 100644
index 00000000000..66231040747
--- /dev/null
+++ b/sql/item_sum.h
@@ -0,0 +1,464 @@
+/* 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 for sum functions */
+
+#ifdef __GNUC__
+#pragma interface /* gcc class implementation */
+#endif
+
+class Item_sum :public Item_result_field
+{
+public:
+ enum Sumfunctype {COUNT_FUNC,COUNT_DISTINCT_FUNC,SUM_FUNC,AVG_FUNC,MIN_FUNC,
+ MAX_FUNC, UNIQUE_USERS_FUNC,STD_FUNC,SUM_BIT_FUNC,
+ UDF_SUM_FUNC };
+
+ Item **args,*tmp_args[2];
+ uint arg_count;
+ bool quick_group; /* If incremental update of fields */
+
+ Item_sum() : arg_count(0),quick_group(1) { with_sum_func=1; }
+ Item_sum(Item *a) :quick_group(1)
+ {
+ arg_count=1;
+ args=tmp_args;
+ args[0]=a;
+ with_sum_func = 1;
+ }
+ Item_sum( Item *a, Item *b ) :quick_group(1)
+ {
+ arg_count=2;
+ args=tmp_args;
+ args[0]=a; args[1]=b;
+ with_sum_func=1;
+ }
+ Item_sum(List<Item> &list);
+ ~Item_sum() { result_field=0; }
+ enum Type type() const { return SUM_FUNC_ITEM; }
+ virtual enum Sumfunctype sum_func () const=0;
+ virtual void reset()=0;
+ virtual bool add()=0;
+ virtual void reset_field()=0;
+ virtual void update_field(int offset)=0;
+ virtual bool keep_field_type(void) const { return 0; }
+ virtual void fix_length_and_dec() { maybe_null=1; null_value=1; }
+ virtual const char *func_name() const { return "?"; }
+ virtual Item *result_item(Field *field)
+ { return new Item_field(field);}
+ table_map used_tables() const { return ~(table_map) 0; } /* Not used */
+ bool const_item() const { return 0; }
+ void update_used_tables() { }
+ void make_field(Send_field *field);
+ void print(String *str);
+ void fix_num_length_and_dec();
+ virtual bool setup(THD *thd) {return 0;}
+};
+
+
+class Item_sum_num :public Item_sum
+{
+public:
+ Item_sum_num() :Item_sum() {}
+ Item_sum_num(Item *item_par) :Item_sum(item_par) {}
+ Item_sum_num(Item *a, Item* b) :Item_sum(a,b) {}
+ Item_sum_num(List<Item> &list) :Item_sum(list) {}
+ bool fix_fields(THD *,struct st_table_list *);
+ longlong val_int() { return (longlong) val(); } /* Real as default */
+ String *val_str(String*str);
+ void reset_field();
+};
+
+
+class Item_sum_int :public Item_sum_num
+{
+ void fix_length_and_dec()
+ { decimals=0; max_length=21; maybe_null=null_value=0; }
+
+public:
+ Item_sum_int(Item *item_par) :Item_sum_num(item_par) {}
+ Item_sum_int(List<Item> &list) :Item_sum_num(list) {}
+ double val() { return (double) val_int(); }
+ String *val_str(String*str);
+ enum Item_result result_type () const { return INT_RESULT; }
+};
+
+
+class Item_sum_sum :public Item_sum_num
+{
+ double sum;
+ void fix_length_and_dec() { maybe_null=null_value=1; }
+
+ public:
+ Item_sum_sum(Item *item_par) :Item_sum_num(item_par),sum(0.0) {}
+ enum Sumfunctype sum_func () const {return SUM_FUNC;}
+ void reset();
+ bool add();
+ double val();
+ void reset_field();
+ void update_field(int offset);
+ const char *func_name() const { return "sum"; }
+};
+
+
+class Item_sum_count :public Item_sum_int
+{
+ longlong count;
+ table_map used_table_cache;
+
+ public:
+ Item_sum_count(Item *item_par)
+ :Item_sum_int(item_par),count(0),used_table_cache(~(table_map) 0)
+ {}
+ table_map used_tables() const { return used_table_cache; }
+ bool const_item() const { return !used_table_cache; }
+ enum Sumfunctype sum_func () const { return COUNT_FUNC; }
+ void reset();
+ 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"; }
+};
+
+
+class TMP_TABLE_PARAM;
+
+class Item_sum_count_distinct :public Item_sum_int
+{
+ TABLE *table;
+ table_map used_table_cache;
+ bool fix_fields(THD *thd,TABLE_LIST *tables);
+ TMP_TABLE_PARAM *tmp_table_param;
+
+ public:
+ Item_sum_count_distinct(List<Item> &list)
+ :Item_sum_int(list),table(0),used_table_cache(~(table_map) 0),
+ tmp_table_param(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();
+ bool add();
+ longlong val_int();
+ void reset_field() { return ;} // Never called
+ void update_field(int offset) { return ; } // Never called
+ const char *func_name() const { return "count_distinct"; }
+ bool setup(THD *thd);
+};
+
+
+/* Item to get the value of a stored sum function */
+
+class Item_avg_field :public Item_result_field
+{
+public:
+ Field *field;
+ Item_avg_field(Item_sum_avg *item);
+ enum Type type() const { return FIELD_AVG_ITEM; }
+ double val();
+ longlong val_int() { return (longlong) val(); }
+ String *val_str(String*);
+ void make_field(Send_field *field);
+ void fix_length_and_dec() {}
+};
+
+
+class Item_sum_avg :public Item_sum_num
+{
+ void fix_length_and_dec() { decimals+=4; maybe_null=1; }
+
+ double sum;
+ ulonglong count;
+
+ public:
+ Item_sum_avg(Item *item_par) :Item_sum_num(item_par),count(0) {}
+ enum Sumfunctype sum_func () const {return AVG_FUNC;}
+ void reset();
+ bool add();
+ double val();
+ void reset_field();
+ void update_field(int offset);
+ Item *result_item(Field *field)
+ { return new Item_avg_field(this); }
+ const char *func_name() const { return "avg"; }
+};
+
+
+class Item_std_field :public Item_result_field
+{
+public:
+ Field *field;
+ Item_std_field(Item_sum_std *item);
+ enum Type type() const { return FIELD_STD_ITEM; }
+ double val();
+ longlong val_int() { return (longlong) val(); }
+ String *val_str(String*);
+ void make_field(Send_field *field);
+ void fix_length_and_dec() {}
+};
+
+class Item_sum_std :public Item_sum_num
+{
+ double sum;
+ double sum_sqr;
+ ulonglong count;
+ void fix_length_and_dec() { decimals+=4; maybe_null=1; }
+
+ public:
+ Item_sum_std(Item *item_par) :Item_sum_num(item_par),count(0) {}
+ enum Sumfunctype sum_func () const { return STD_FUNC; }
+ void reset();
+ bool add();
+ double val();
+ void reset_field();
+ void update_field(int offset);
+ Item *result_item(Field *field)
+ { return new Item_std_field(this); }
+ const char *func_name() const { return "std"; }
+};
+
+
+// This class is a string or number function depending on num_func
+
+class Item_sum_hybrid :public Item_sum
+{
+ protected:
+ String value,tmp_value;
+ double sum;
+ Item_result hybrid_type;
+ int cmp_sign;
+ table_map used_table_cache;
+
+ public:
+ Item_sum_hybrid(Item *item_par,int sign) :Item_sum(item_par),cmp_sign(sign),
+ used_table_cache(~(table_map) 0)
+ {}
+ bool fix_fields(THD *,struct st_table_list *);
+ table_map used_tables() const { return used_table_cache; }
+ bool const_item() const { return !used_table_cache; }
+
+ void reset()
+ {
+ sum=0.0;
+ value.length(0);
+ null_value=1;
+ add();
+ }
+ double val();
+ longlong val_int() { return (longlong) val(); } /* Real as default */
+ void reset_field();
+ String *val_str(String *);
+ void make_const() { used_table_cache=0; }
+ bool keep_field_type(void) const { return 1; }
+ enum Item_result result_type () const { return hybrid_type; }
+ void update_field(int offset);
+ void min_max_update_str_field(int offset);
+ void min_max_update_real_field(int offset);
+ void min_max_update_int_field(int offset);
+};
+
+
+class Item_sum_min :public Item_sum_hybrid
+{
+public:
+ Item_sum_min(Item *item_par) :Item_sum_hybrid(item_par,1) {}
+ enum Sumfunctype sum_func () const {return MIN_FUNC;}
+
+ bool add();
+ const char *func_name() const { return "min"; }
+};
+
+
+class Item_sum_max :public Item_sum_hybrid
+{
+public:
+ Item_sum_max(Item *item_par) :Item_sum_hybrid(item_par,-1) {}
+ enum Sumfunctype sum_func () const {return MAX_FUNC;}
+
+ bool add();
+ const char *func_name() const { return "max"; }
+};
+
+
+class Item_sum_bit :public Item_sum_int
+{
+ protected:
+ ulonglong reset_bits,bits;
+
+ public:
+ Item_sum_bit(Item *item_par,ulonglong reset_arg)
+ :Item_sum_int(item_par),reset_bits(reset_arg),bits(reset_arg) {}
+ enum Sumfunctype sum_func () const {return SUM_BIT_FUNC;}
+ void reset();
+ longlong val_int();
+ void reset_field();
+};
+
+
+class Item_sum_or :public Item_sum_bit
+{
+ public:
+ Item_sum_or(Item *item_par) :Item_sum_bit(item_par,LL(0)) {}
+ bool add();
+ void update_field(int offset);
+ const char *func_name() const { return "bit_or"; }
+};
+
+
+class Item_sum_and :public Item_sum_bit
+{
+ public:
+ Item_sum_and(Item *item_par) :Item_sum_bit(item_par, ~(ulonglong) LL(0)) {}
+ bool add();
+ void update_field(int offset);
+ const char *func_name() const { return "bit_and"; }
+};
+
+/*
+** user defined aggregates
+*/
+#ifdef HAVE_DLOPEN
+
+class Item_udf_sum : public Item_sum
+{
+protected:
+ udf_handler udf;
+
+public:
+ Item_udf_sum(udf_func *udf_arg) :Item_sum(), udf(udf_arg) { quick_group=0;}
+ Item_udf_sum( udf_func *udf_arg, List<Item> &list )
+ :Item_sum( list ), udf(udf_arg)
+ { quick_group=0;}
+ ~Item_udf_sum() {}
+ const char *func_name() const { return udf.name(); }
+ bool fix_fields(THD *thd,struct st_table_list *tables)
+ {
+ return udf.fix_fields(thd,tables,this,this->arg_count,this->args);
+ }
+ enum Sumfunctype sum_func () const { return UDF_SUM_FUNC; }
+ virtual bool have_field_update(void) const { return 0; }
+
+ void reset();
+ bool add();
+ void reset_field() {};
+ void update_field(int offset_arg) {};
+};
+
+
+class Item_sum_udf_float :public Item_udf_sum
+{
+ public:
+ Item_sum_udf_float(udf_func *udf_arg) :Item_udf_sum(udf_arg) {}
+ Item_sum_udf_float(udf_func *udf_arg, List<Item> &list)
+ :Item_udf_sum(udf_arg,list) {}
+ ~Item_sum_udf_float() {}
+ longlong val_int() { return (longlong) Item_sum_udf_float::val(); }
+ double val();
+ String *val_str(String*str);
+ void fix_length_and_dec() { fix_num_length_and_dec(); }
+};
+
+
+class Item_sum_udf_int :public Item_udf_sum
+{
+public:
+ Item_sum_udf_int(udf_func *udf_arg) :Item_udf_sum(udf_arg) {}
+ Item_sum_udf_int(udf_func *udf_arg, List<Item> &list)
+ :Item_udf_sum(udf_arg,list) {}
+ ~Item_sum_udf_int() {}
+ longlong val_int();
+ double val() { return (double) Item_sum_udf_int::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; }
+};
+
+
+class Item_sum_udf_str :public Item_udf_sum
+{
+public:
+ Item_sum_udf_str(udf_func *udf_arg) :Item_udf_sum(udf_arg) {}
+ Item_sum_udf_str(udf_func *udf_arg, List<Item> &list)
+ :Item_udf_sum(udf_arg,list) {}
+ ~Item_sum_udf_str() {}
+ String *val_str(String *);
+ double val()
+ {
+ String *res; res=val_str(&str_value);
+ return res ? atof(res->c_ptr()) : 0.0;
+ }
+ longlong val_int()
+ {
+ String *res; res=val_str(&str_value);
+ return res ? strtoll(res->c_ptr(),(char**) 0,10) : (longlong) 0;
+ }
+ enum Item_result result_type () const { return STRING_RESULT; }
+ void fix_length_and_dec();
+};
+
+#else /* Dummy functions to get sql_yacc.cc compiled */
+
+class Item_sum_udf_float :public Item_sum_num
+{
+ public:
+ Item_sum_udf_float(udf_func *udf_arg) :Item_sum_num() {}
+ Item_sum_udf_float(udf_func *udf_arg, List<Item> &list) :Item_sum_num() {}
+ ~Item_sum_udf_float() {}
+ enum Sumfunctype sum_func () const { return UDF_SUM_FUNC; }
+ double val() { return 0.0; }
+ void reset() {}
+ bool add() { return 0; }
+ void update_field(int offset) {}
+};
+
+
+class Item_sum_udf_int :public Item_sum_num
+{
+public:
+ Item_sum_udf_int(udf_func *udf_arg) :Item_sum_num() {}
+ Item_sum_udf_int(udf_func *udf_arg, List<Item> &list) :Item_sum_num() {}
+ ~Item_sum_udf_int() {}
+ enum Sumfunctype sum_func () const { return UDF_SUM_FUNC; }
+ longlong val_int() { return 0; }
+ double val() { return 0; }
+ void reset() {}
+ bool add() { return 0; }
+ void update_field(int offset) {}
+};
+
+
+class Item_sum_udf_str :public Item_sum_num
+{
+public:
+ Item_sum_udf_str(udf_func *udf_arg) :Item_sum_num() {}
+ Item_sum_udf_str(udf_func *udf_arg, List<Item> &list) :Item_sum_num() {}
+ ~Item_sum_udf_str() {}
+ String *val_str(String *) { null_value=1; return 0; }
+ double val() { null_value=1; return 0.0; }
+ longlong val_int() { null_value=1; return 0; }
+ enum Item_result result_type () const { return STRING_RESULT; }
+ void fix_length_and_dec() { maybe_null=1; max_length=0; }
+ enum Sumfunctype sum_func () const { return UDF_SUM_FUNC; }
+ void reset() {}
+ bool add() { return 0; }
+ void update_field(int offset) {}
+};
+
+#endif /* HAVE_DLOPEN */
diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc
new file mode 100644
index 00000000000..90a6cc2910c
--- /dev/null
+++ b/sql/item_timefunc.cc
@@ -0,0 +1,1117 @@
+/* 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 */
+
+
+/* This file defines all time functions */
+
+#ifdef __GNUC__
+#pragma implementation // gcc: Class implementation
+#endif
+
+#include "mysql_priv.h"
+#include <m_ctype.h>
+#include <time.h>
+
+/*
+** Todo: Move month and days to language files
+*/
+
+static String month_names[] = { "January", "February", "March", "April",
+ "May", "June", "July", "August",
+ "September", "October", "November", "December" };
+static String day_names[] = { "Monday", "Tuesday", "Wednesday",
+ "Thursday", "Friday", "Saturday" ,"Sunday" };
+
+/*
+** Get a array of positive numbers from a string object.
+** Each number is separated by 1 non digit character
+** Return error if there is too many numbers.
+** If there is too few numbers, assume that the numbers are left out
+** from the high end. This allows one to give:
+** DAY_TO_SECOND as "D MM:HH:SS", "MM:HH:SS" "HH:SS" or as seconds.
+*/
+
+bool get_interval_info(const char *str,uint length,uint count,
+ long *values)
+{
+ const char *end=str+length;
+ uint i;
+ while (str != end && !isdigit(*str))
+ str++;
+
+ for (i=0 ; i < count ; i++)
+ {
+ long value;
+ for (value=0; str != end && isdigit(*str) ; str++)
+ value=value*10L + (long) (*str - '0');
+ values[i]= value;
+ while (str != end && !isdigit(*str))
+ str++;
+ if (str == end && i != count-1)
+ {
+ i++;
+ /* Change values[0...i-1] -> values[0...count-1] */
+ bmove_upp((char*) (values+count), (char*) (values+i),
+ sizeof(long)*i);
+ bzero((char*) values, sizeof(long)*(count-i));
+ break;
+ }
+ }
+ return (str != end);
+}
+
+longlong Item_func_period_add::val_int()
+{
+ ulong period=(ulong) args[0]->val_int();
+ int months=(int) args[1]->val_int();
+
+ if ((null_value=args[0]->null_value || args[1]->null_value) ||
+ period == 0L)
+ return 0; /* purecov: inspected */
+ return (longlong)
+ convert_month_to_period((uint) ((int) convert_period_to_month(period)+
+ months));
+}
+
+
+longlong Item_func_period_diff::val_int()
+{
+ ulong period1=(ulong) args[0]->val_int();
+ ulong period2=(ulong) args[1]->val_int();
+
+ if ((null_value=args[0]->null_value || args[1]->null_value))
+ return 0; /* purecov: inspected */
+ return (longlong) ((long) convert_period_to_month(period1)-
+ (long) convert_period_to_month(period2));
+}
+
+
+
+longlong Item_func_to_days::val_int()
+{
+ TIME ltime;
+ if (get_arg0_date(&ltime,0))
+ return 0;
+ return (longlong) calc_daynr(ltime.year,ltime.month,ltime.day);
+}
+
+longlong Item_func_dayofyear::val_int()
+{
+ TIME ltime;
+ if (get_arg0_date(&ltime,0))
+ return 0;
+ return (longlong) calc_daynr(ltime.year,ltime.month,ltime.day) -
+ calc_daynr(ltime.year,1,1) + 1;
+}
+
+longlong Item_func_dayofmonth::val_int()
+{
+ TIME ltime;
+ (void) get_arg0_date(&ltime,1);
+ return (longlong) ltime.day;
+}
+
+longlong Item_func_month::val_int()
+{
+ TIME ltime;
+ (void) get_arg0_date(&ltime,1);
+ return (longlong) ltime.month;
+}
+
+String* Item_func_monthname::val_str(String* str)
+{
+ uint month=(uint) Item_func_month::val_int();
+ if (null_value)
+ return (String*) 0;
+ return &month_names[month-1];
+}
+
+// Returns the quarter of the year
+
+longlong Item_func_quarter::val_int()
+{
+ TIME ltime;
+ (void) get_arg0_date(&ltime,1);
+ return (longlong) ((ltime.month+2)/3);
+}
+
+longlong Item_func_hour::val_int()
+{
+ TIME ltime;
+ (void) get_arg0_time(&ltime);
+ return ltime.hour;
+}
+
+longlong Item_func_minute::val_int()
+{
+ TIME ltime;
+ (void) get_arg0_time(&ltime);
+ return ltime.minute;
+}
+// Returns the second in time_exp in the range of 0 - 59
+
+longlong Item_func_second::val_int()
+{
+ TIME ltime;
+ (void) get_arg0_time(&ltime);
+ return ltime.second;
+}
+
+
+// Returns the week of year in the range of 0 - 53
+
+longlong Item_func_week::val_int()
+{
+ uint year;
+ TIME ltime;
+ if (get_arg0_date(&ltime,0))
+ return 0;
+ return (longlong) calc_week(&ltime, 0, args[1]->val_int() == 0, &year);
+}
+
+
+longlong Item_func_yearweek::val_int()
+{
+ uint year,week;
+ TIME ltime;
+ if (get_arg0_date(&ltime,0))
+ return 0;
+ week=calc_week(&ltime, 1, args[1]->val_int() == 0, &year);
+ return week+year*100;
+}
+
+
+/* weekday() has a automatic to_days() on item */
+
+longlong Item_func_weekday::val_int()
+{
+ ulong tmp_value=(ulong) args[0]->val_int();
+ if ((null_value=(args[0]->null_value || !tmp_value)))
+ return 0; /* purecov: inspected */
+
+ return (longlong) calc_weekday(tmp_value,odbc_type)+test(odbc_type);
+}
+
+String* Item_func_dayname::val_str(String* str)
+{
+ uint weekday=(uint) val_int(); // Always Item_func_daynr()
+ if (null_value)
+ return (String*) 0;
+ return &day_names[weekday];
+}
+
+
+longlong Item_func_year::val_int()
+{
+ TIME ltime;
+ (void) get_arg0_date(&ltime,1);
+ return (longlong) ltime.year;
+}
+
+
+longlong Item_func_unix_timestamp::val_int()
+{
+ if (arg_count == 0)
+ return (longlong) current_thd->query_start();
+ if (args[0]->type() == FIELD_ITEM)
+ { // Optimize timestamp field
+ Field *field=((Item_field*) args[0])->field;
+ if (field->type() == FIELD_TYPE_TIMESTAMP)
+ return ((Field_timestamp*) field)->get_timestamp();
+ }
+ String *str=args[0]->val_str(&value);
+ if ((null_value=args[0]->null_value))
+ {
+ return 0; /* purecov: inspected */
+ }
+ return (longlong) str_to_timestamp(str->ptr(),str->length());
+}
+
+
+longlong Item_func_time_to_sec::val_int()
+{
+ TIME ltime;
+ (void) get_arg0_time(&ltime);
+ return ltime.hour*3600L+ltime.minute*60+ltime.second;
+}
+
+
+/*
+** Convert a string to a interval value
+** To make code easy, allow interval objects without separators.
+*/
+
+static bool get_interval_value(Item *args,interval_type int_type,
+ String *str_value, INTERVAL *t)
+{
+ long array[4],value;
+ const char *str;
+ uint32 length;
+ LINT_INIT(value); LINT_INIT(str); LINT_INIT(length);
+
+ bzero((char*) t,sizeof(*t));
+ if ((int) int_type <= INTERVAL_SECOND)
+ {
+ value=(long) args->val_int();
+ if (args->null_value)
+ return 1;
+ if (value < 0)
+ {
+ t->neg=1;
+ value= -value;
+ }
+ }
+ else
+ {
+ String *res;
+ if (!(res=args->val_str(str_value)))
+ return (1);
+
+ /* record negative intervalls in t->neg */
+ str=res->ptr();
+ const char *end=str+res->length();
+ while (str != end && isspace(*str))
+ str++;
+ if (str != end && *str == '-')
+ {
+ t->neg=1;
+ str++;
+ }
+ length=(uint32) (end-str); // Set up pointers to new str
+ }
+
+ switch (int_type) {
+ case INTERVAL_YEAR:
+ t->year=value;
+ break;
+ case INTERVAL_MONTH:
+ t->month=value;
+ break;
+ case INTERVAL_DAY:
+ t->day=value;
+ break;
+ case INTERVAL_HOUR:
+ t->hour=value;
+ break;
+ case INTERVAL_MINUTE:
+ t->minute=value;
+ break;
+ case INTERVAL_SECOND:
+ t->second=value;
+ break;
+ case INTERVAL_YEAR_MONTH: // Allow YEAR-MONTH YYYYYMM
+ if (get_interval_info(str,length,2,array))
+ return (1);
+ t->year=array[0];
+ t->month=array[1];
+ break;
+ case INTERVAL_DAY_HOUR:
+ if (get_interval_info(str,length,2,array))
+ return (1);
+ t->day=array[0];
+ t->hour=array[1];
+ break;
+ case INTERVAL_DAY_MINUTE:
+ if (get_interval_info(str,length,3,array))
+ return (1);
+ t->day=array[0];
+ t->hour=array[1];
+ t->minute=array[2];
+ break;
+ case INTERVAL_DAY_SECOND:
+ if (get_interval_info(str,length,4,array))
+ return (1);
+ t->day=array[0];
+ t->hour=array[1];
+ t->minute=array[2];
+ t->second=array[3];
+ break;
+ case INTERVAL_HOUR_MINUTE:
+ if (get_interval_info(str,length,2,array))
+ return (1);
+ t->hour=array[0];
+ t->minute=array[1];
+ break;
+ case INTERVAL_HOUR_SECOND:
+ if (get_interval_info(str,length,3,array))
+ return (1);
+ t->hour=array[0];
+ t->minute=array[1];
+ t->second=array[2];
+ break;
+ case INTERVAL_MINUTE_SECOND:
+ if (get_interval_info(str,length,2,array))
+ return (1);
+ t->minute=array[0];
+ t->second=array[1];
+ break;
+ }
+ return 0;
+}
+
+
+String *Item_date::val_str(String *str)
+{
+ ulong value=(ulong) val_int();
+ if (null_value)
+ return (String*) 0;
+ if (!value) // zero daynr
+ {
+ str->copy("0000-00-00");
+ return str;
+ }
+ if (str->alloc(11))
+ return &empty_string; /* purecov: inspected */
+ sprintf((char*) str->ptr(),"%04d-%02d-%02d",
+ (int) (value/10000L) % 10000,
+ (int) (value/100)%100,
+ (int) (value%100));
+ str->length(10);
+ return str;
+}
+
+
+bool Item_date::save_in_field(Field *field)
+{
+ TIME ltime;
+ timestamp_type t_type=TIMESTAMP_FULL;
+ if (get_date(&ltime,1))
+ {
+ if (null_value)
+ return set_field_to_null(field);
+ t_type=TIMESTAMP_NONE; // Error
+ }
+ field->set_notnull();
+ field->store_time(&ltime,t_type);
+ return 0;
+}
+
+
+longlong Item_func_from_days::val_int()
+{
+ longlong value=args[0]->val_int();
+ if ((null_value=args[0]->null_value))
+ return 0; /* purecov: inspected */
+
+ uint year,month,day;
+ get_date_from_daynr((long) value,&year,&month,&day);
+ return (longlong) (year*10000L+month*100+day);
+}
+
+
+void Item_func_curdate::fix_length_and_dec()
+{
+ struct tm tm_tmp,*start;
+ time_t query_start=current_thd->query_start();
+ decimals=0; max_length=10;
+ localtime_r(&query_start,&tm_tmp);
+ start=&tm_tmp;
+ value=(longlong) ((ulong) ((uint) start->tm_year+1900)*10000L+
+ ((uint) start->tm_mon+1)*100+
+ (uint) start->tm_mday);
+ /* For getdate */
+ ltime.year= start->tm_year+1900;
+ ltime.month= start->tm_mon+1;
+ ltime.day= start->tm_mday;
+ ltime.hour= 0;
+ ltime.minute= 0;
+ ltime.second= 0;
+ ltime.second_part=0;
+ ltime.neg=0;
+}
+
+bool Item_func_curdate::get_date(TIME *res,
+ bool fuzzy_date __attribute__((unused)))
+{
+ *res=ltime;
+ return 0;
+}
+
+void Item_func_curtime::fix_length_and_dec()
+{
+ struct tm tm_tmp,*start;
+ time_t query_start=current_thd->query_start();
+ decimals=0; max_length=8;
+ localtime_r(&query_start,&tm_tmp);
+ start=&tm_tmp;
+ value=(longlong) ((ulong) ((uint) start->tm_hour)*10000L+
+ (ulong) (((uint) start->tm_min)*100L+
+ (uint) start->tm_sec));
+ sprintf(buff,"%02d:%02d:%02d",
+ (int) start->tm_hour,
+ (int) start->tm_min,
+ (int) start->tm_sec);
+ buff_length=strlen(buff);
+}
+
+void Item_func_now::fix_length_and_dec()
+{
+ struct tm tm_tmp,*start;
+ time_t query_start=current_thd->query_start();
+ decimals=0; max_length=19;
+ localtime_r(&query_start,&tm_tmp);
+ start=&tm_tmp;
+ value=((longlong) ((ulong) ((uint) start->tm_year+1900)*10000L+
+ (((uint) start->tm_mon+1)*100+
+ (uint) start->tm_mday))*(longlong) 1000000L+
+ (longlong) ((ulong) ((uint) start->tm_hour)*10000L+
+ (ulong) (((uint) start->tm_min)*100L+
+ (uint) start->tm_sec)));
+ sprintf(buff,"%04d-%02d-%02d %02d:%02d:%02d",
+ ((int) (start->tm_year+1900)) % 10000,
+ (int) start->tm_mon+1,
+ (int) start->tm_mday,
+ (int) start->tm_hour,
+ (int) start->tm_min,
+ (int) start->tm_sec);
+ buff_length=strlen(buff);
+ /* For getdate */
+ ltime.year= start->tm_year+1900;
+ ltime.month= start->tm_mon+1;
+ ltime.day= start->tm_mday;
+ ltime.hour= start->tm_hour;
+ ltime.minute= start->tm_min;
+ ltime.second= start->tm_sec;
+ ltime.second_part=0;
+ ltime.neg=0;
+}
+
+bool Item_func_now::get_date(TIME *res,
+ bool fuzzy_date __attribute__((unused)))
+{
+ *res=ltime;
+ return 0;
+}
+
+
+bool Item_func_now::save_in_field(Field *to)
+{
+ to->set_notnull();
+ to->store_time(&ltime,TIMESTAMP_FULL);
+ return 0;
+}
+
+
+String *Item_func_sec_to_time::val_str(String *str)
+{
+ char buff[23];
+ const char *sign="";
+ longlong seconds=(longlong) args[0]->val_int();
+ if ((null_value=args[0]->null_value))
+ return (String*) 0;
+ if (seconds < 0)
+ {
+ seconds= -seconds;
+ sign= "-";
+ }
+ uint sec= (uint) (seconds % 3600);
+ sprintf(buff,"%s%02lu:%02u:%02u",sign,(long) (seconds/3600),
+ sec/60, sec % 60);
+ str->copy(buff,strlen(buff));
+ return str;
+}
+
+
+longlong Item_func_sec_to_time::val_int()
+{
+ longlong seconds=args[0]->val_int();
+ longlong sign=1;
+ if ((null_value=args[0]->null_value))
+ return 0;
+ if (seconds < 0)
+ {
+ seconds= -seconds;
+ sign= -1;
+ }
+ return sign*((seconds / 3600)*10000+((seconds/60) % 60)*100+ (seconds % 60));
+}
+
+
+void Item_func_date_format::fix_length_and_dec()
+{
+ decimals=0;
+ if (args[1]->type() == STRING_ITEM)
+ { // Optimize the normal case
+ fixed_length=1;
+ max_length=format_length(((Item_string*) args[1])->const_string());
+ }
+ else
+ {
+ fixed_length=0;
+ max_length=args[1]->max_length*10;
+ set_if_smaller(max_length,MAX_BLOB_WIDTH);
+ }
+ maybe_null=1; // If wrong date
+}
+
+
+uint Item_func_date_format::format_length(const String *format)
+{
+ uint size=0;
+ const char *ptr=format->ptr();
+ const char *end=ptr+format->length();
+
+ for (; ptr != end ; ptr++)
+ {
+ if (*ptr != '%' || ptr == end-1)
+ size++;
+ else
+ {
+ switch(*++ptr) {
+ case 'M': /* month, textual */
+ case 'W': /* day (of the week), textual */
+ size += 9;
+ break;
+ case 'D': /* day (of the month), numeric plus english suffix */
+ case 'Y': /* year, numeric, 4 digits */
+ case 'x': /* Year, used with 'v' */
+ case 'X': /* Year, used with 'v, where week starts with Monday' */
+ size += 4;
+ break;
+ case 'a': /* locale's abbreviated weekday name (Sun..Sat) */
+ case 'b': /* locale's abbreviated month name (Jan.Dec) */
+ case 'j': /* day of year (001..366) */
+ size += 3;
+ break;
+ case 'U': /* week (00..52) */
+ case 'u': /* week (00..52), where week starts with Monday */
+ case 'V': /* week 1..53 used with 'x' */
+ case 'v': /* week 1..53 used with 'x', where week starts with Monday */
+ case 'H': /* hour (00..23) */
+ case 'y': /* year, numeric, 2 digits */
+ case 'm': /* month, numeric */
+ case 'd': /* day (of the month), numeric */
+ case 'h': /* hour (01..12) */
+ case 'I': /* --||-- */
+ case 'i': /* minutes, numeric */
+ case 'k': /* hour ( 0..23) */
+ case 'l': /* hour ( 1..12) */
+ case 'p': /* locale's AM or PM */
+ case 'S': /* second (00..61) */
+ case 's': /* seconds, numeric */
+ case 'c': /* month (0..12) */
+ case 'e': /* day (0..31) */
+ size += 2;
+ break;
+ case 'r': /* time, 12-hour (hh:mm:ss [AP]M) */
+ size += 11;
+ break;
+ case 'T': /* time, 24-hour (hh:mm:ss) */
+ size += 8;
+ break;
+ case 'w': /* day (of the week), numeric */
+ case '%':
+ default:
+ size++;
+ break;
+ }
+ }
+ }
+ return size;
+}
+
+
+String *Item_func_date_format::val_str(String *str)
+{
+ String *format;
+ TIME l_time;
+ char intbuff[15];
+ uint size,weekday;
+
+ if (!date_or_time)
+ {
+ if (get_arg0_date(&l_time,1))
+ return 0;
+ }
+ else
+ {
+ String *res;
+ if (!(res=args[0]->val_str(str)))
+ {
+ null_value=1;
+ return 0;
+ }
+ if (str_to_time(res->ptr(),res->length(),&l_time))
+ {
+ null_value=1;
+ return 0;
+ }
+ l_time.year=l_time.month=l_time.day=0;
+ null_value=0;
+ }
+
+ if (!(format = args[1]->val_str(str)) || !format->length())
+ {
+ null_value=1;
+ return 0;
+ }
+
+ if (fixed_length)
+ size=max_length;
+ else
+ size=format_length(format);
+ if (format == str)
+ str=&str_value; // Save result here
+ if (str->alloc(size))
+ {
+ null_value=1;
+ return 0;
+ }
+ str->length(0);
+
+ /* Create the result string */
+ const char *ptr=format->ptr();
+ const char *end=ptr+format->length();
+ for ( ; ptr != end ; ptr++)
+ {
+ if (*ptr != '%' || ptr+1 == end)
+ str->append(*ptr);
+ else
+ {
+ switch (*++ptr) {
+ case 'M':
+ if(!l_time.month)
+ {
+ null_value=1;
+ return 0;
+ }
+ str->append(month_names[l_time.month-1]);
+ break;
+ case 'b':
+ if(!l_time.month)
+ {
+ null_value=1;
+ return 0;
+ }
+ str->append(month_names[l_time.month-1].ptr(),3);
+ break;
+ case 'W':
+ if(date_or_time)
+ {
+ null_value=1;
+ return 0;
+ }
+ weekday=calc_weekday(calc_daynr(l_time.year,l_time.month,l_time.day),0);
+ str->append(day_names[weekday]);
+ break;
+ case 'a':
+ if(date_or_time)
+ {
+ null_value=1;
+ return 0;
+ }
+ weekday=calc_weekday(calc_daynr(l_time.year,l_time.month,l_time.day),0);
+ str->append(day_names[weekday].ptr(),3);
+ break;
+ case 'D':
+ if(date_or_time)
+ {
+ null_value=1;
+ return 0;
+ }
+ sprintf(intbuff,"%d",l_time.day);
+ str->append(intbuff);
+ if (l_time.day >= 10 && l_time.day <= 19)
+ str->append("th");
+ else
+ {
+ switch (l_time.day %10)
+ {
+ case 1:
+ str->append("st");
+ break;
+ case 2:
+ str->append("nd");
+ break;
+ case 3:
+ str->append("rd");
+ break;
+ default:
+ str->append("th");
+ break;
+ }
+ }
+ break;
+ case 'Y':
+ sprintf(intbuff,"%04d",l_time.year);
+ str->append(intbuff);
+ break;
+ case 'y':
+ sprintf(intbuff,"%02d",l_time.year%100);
+ str->append(intbuff);
+ break;
+ case 'm':
+ sprintf(intbuff,"%02d",l_time.month);
+ str->append(intbuff);
+ break;
+ case 'c':
+ sprintf(intbuff,"%d",l_time.month);
+ str->append(intbuff);
+ break;
+ case 'd':
+ sprintf(intbuff,"%02d",l_time.day);
+ str->append(intbuff);
+ break;
+ case 'e':
+ sprintf(intbuff,"%d",l_time.day);
+ str->append(intbuff);
+ break;
+ case 'H':
+ sprintf(intbuff,"%02d",l_time.hour);
+ str->append(intbuff);
+ break;
+ case 'h':
+ case 'I':
+ sprintf(intbuff,"%02d", (l_time.hour+11)%12+1);
+ str->append(intbuff);
+ break;
+ case 'i': /* minutes */
+ sprintf(intbuff,"%02d",l_time.minute);
+ str->append(intbuff);
+ break;
+ case 'j':
+ if(date_or_time)
+ {
+ null_value=1;
+ return 0;
+ }
+ sprintf(intbuff,"%03d",
+ (int) (calc_daynr(l_time.year,l_time.month,l_time.day) -
+ calc_daynr(l_time.year,1,1)) + 1);
+ str->append(intbuff);
+ break;
+ case 'k':
+ sprintf(intbuff,"%d",l_time.hour);
+ str->append(intbuff);
+ break;
+ case 'l':
+ sprintf(intbuff,"%d", (l_time.hour+11)%12+1);
+ str->append(intbuff);
+ break;
+ case 'p':
+ str->append(l_time.hour < 12 ? "AM" : "PM");
+ break;
+ case 'r':
+ sprintf(intbuff,(l_time.hour < 12) ? "%02d:%02d:%02d AM" :
+ "%02d:%02d:%02d PM",(l_time.hour+11)%12+1,l_time.minute,
+ l_time.second);
+ str->append(intbuff);
+ break;
+ case 'S':
+ case 's':
+ sprintf(intbuff,"%02d",l_time.second);
+ str->append(intbuff);
+ break;
+ case 'T':
+ sprintf(intbuff,"%02d:%02d:%02d",l_time.hour,l_time.minute,l_time.second);
+ str->append(intbuff);
+ break;
+ case 'U':
+ case 'u':
+ {
+ uint year;
+ sprintf(intbuff,"%02d",calc_week(&l_time, 0, (*ptr) == 'U', &year));
+ str->append(intbuff);
+ }
+ break;
+ case 'v':
+ case 'V':
+ {
+ uint year;
+ sprintf(intbuff,"%02d",calc_week(&l_time, 1, (*ptr) == 'V', &year));
+ str->append(intbuff);
+ }
+ break;
+ case 'x':
+ case 'X':
+ {
+ uint year;
+ (void) calc_week(&l_time, 1, (*ptr) == 'X', &year);
+ sprintf(intbuff,"%04d",year);
+ str->append(intbuff);
+ }
+ break;
+ case 'w':
+ weekday=calc_weekday(calc_daynr(l_time.year,l_time.month,l_time.day),1);
+ sprintf(intbuff,"%01d",weekday);
+ str->append(intbuff);
+ break;
+ default:
+ str->append(*ptr);
+ break;
+ }
+ }
+ }
+ return str;
+}
+
+
+String *Item_func_from_unixtime::val_str(String *str)
+{
+ struct tm tm_tmp,*start;
+ time_t tmp=(time_t) args[0]->val_int();
+ if ((null_value=args[0]->null_value))
+ return 0;
+ localtime_r(&tmp,&tm_tmp);
+ start=&tm_tmp;
+ if (str->alloc(20))
+ return str; /* purecov: inspected */
+ sprintf((char*) str->ptr(),"%04d-%02d-%02d %02d:%02d:%02d",
+ (int) start->tm_year+1900,
+ (int) start->tm_mon+1,
+ (int) start->tm_mday,
+ (int) start->tm_hour,
+ (int) start->tm_min,
+ (int) start->tm_sec);
+ str->length(19);
+ return str;
+}
+
+
+longlong Item_func_from_unixtime::val_int()
+{
+ time_t tmp=(time_t) (ulong) args[0]->val_int();
+ if ((null_value=args[0]->null_value))
+ return 0;
+ struct tm tm_tmp,*start;
+ localtime_r(&tmp,&tm_tmp);
+ start= &tm_tmp;
+ return ((longlong) ((ulong) ((uint) start->tm_year+1900)*10000L+
+ (((uint) start->tm_mon+1)*100+
+ (uint) start->tm_mday))*LL(1000000)+
+ (longlong) ((ulong) ((uint) start->tm_hour)*10000L+
+ (ulong) (((uint) start->tm_min)*100L+
+ (uint) start->tm_sec)));
+}
+
+bool Item_func_from_unixtime::get_date(TIME *ltime,
+ bool fuzzy_date __attribute__((unused)))
+{
+ time_t tmp=(time_t) (ulong) args[0]->val_int();
+ if ((null_value=args[0]->null_value))
+ return 1;
+ struct tm tm_tmp,*start;
+ localtime_r(&tmp,&tm_tmp);
+ start= &tm_tmp;
+ ltime->year= start->tm_year+1900;
+ ltime->month= start->tm_mon+1;
+ ltime->day= start->tm_mday;
+ ltime->hour= start->tm_hour;
+ ltime->minute=start->tm_min;
+ ltime->second=start->tm_sec;
+ ltime->second_part=0;
+ ltime->neg=0;
+ return 0;
+}
+
+ /* Here arg[1] is a Item_interval object */
+
+bool Item_date_add_interval::get_date(TIME *ltime, bool fuzzy_date)
+{
+ long period,sign;
+ INTERVAL interval;
+
+ if (args[0]->get_date(ltime,0) ||
+ get_interval_value(args[1],int_type,&value,&interval))
+ goto null_date;
+ sign= (interval.neg ? -1 : 1);
+ if (date_sub_interval)
+ sign = -sign;
+
+ null_value=0;
+ switch (int_type) {
+ case INTERVAL_SECOND:
+ case INTERVAL_MINUTE:
+ case INTERVAL_HOUR:
+ case INTERVAL_MINUTE_SECOND:
+ case INTERVAL_HOUR_SECOND:
+ case INTERVAL_HOUR_MINUTE:
+ case INTERVAL_DAY_SECOND:
+ case INTERVAL_DAY_MINUTE:
+ case INTERVAL_DAY_HOUR:
+ long sec,days,daynr;
+ ltime->time_type=TIMESTAMP_FULL; // Return full date
+
+ sec=((ltime->day-1)*3600*24L+ltime->hour*3600+ltime->minute*60+
+ ltime->second +
+ sign*(interval.day*3600*24L +
+ interval.hour*3600+interval.minute*60+interval.second));
+ days=sec/(3600*24L); sec=sec-days*3600*24L;
+ if (sec < 0)
+ {
+ days--;
+ sec+=3600*24L;
+ }
+ ltime->second=sec % 60;
+ ltime->minute=sec/60 % 60;
+ ltime->hour=sec/3600;
+ daynr= calc_daynr(ltime->year,ltime->month,1) + days;
+ get_date_from_daynr(daynr,&ltime->year,&ltime->month,&ltime->day);
+ if (daynr < 0 || daynr >= 3652424) // Day number from year 0 to 9999-12-31
+ goto null_date;
+ break;
+ case INTERVAL_DAY:
+ period= calc_daynr(ltime->year,ltime->month,ltime->day) +
+ sign*interval.day;
+ if (period < 0 || period >= 3652424) // Daynumber from year 0 to 9999-12-31
+ goto null_date;
+ get_date_from_daynr((long) period,&ltime->year,&ltime->month,&ltime->day);
+ break;
+ case INTERVAL_YEAR:
+ ltime->year += sign*interval.year;
+ if ((int) ltime->year < 0 || ltime->year >= 10000L)
+ goto null_date;
+ if (ltime->month == 2 && ltime->day == 29 &&
+ calc_days_in_year(ltime->year) != 366)
+ ltime->day=28; // Was leap-year
+ break;
+ case INTERVAL_YEAR_MONTH:
+ case INTERVAL_MONTH:
+ period= (ltime->year*12 + sign*interval.year*12 +
+ ltime->month-1 + sign*interval.month);
+ if (period < 0 || period >= 120000L)
+ goto null_date;
+ ltime->year= (uint) (period / 12);
+ ltime->month= (uint) (period % 12L)+1;
+ /* Adjust day if the new month doesn't have enough days */
+ if (ltime->day > days_in_month[ltime->month-1])
+ {
+ ltime->day = days_in_month[ltime->month-1];
+ if (ltime->month == 2 && calc_days_in_year(ltime->year) == 366)
+ ltime->day++; // Leap-year
+ }
+ break;
+ default:
+ goto null_date;
+ }
+ return 0; // Ok
+
+ null_date:
+ return (null_value=1);
+}
+
+
+String *Item_date_add_interval::val_str(String *str)
+{
+ TIME ltime;
+
+ if (Item_date_add_interval::get_date(&ltime,0))
+ return 0;
+ if (ltime.time_type == TIMESTAMP_DATE)
+ {
+ if (str->alloc(11))
+ goto null_date;
+ sprintf((char*) str->ptr(),"%04d-%02d-%02d",
+ ltime.year,ltime.month,ltime.day);
+ str->length(10);
+ }
+ else
+ {
+ if (str->alloc(20))
+ goto null_date;
+ sprintf((char*) str->ptr(),"%04d-%02d-%02d %02d:%02d:%02d",
+ ltime.year,ltime.month,ltime.day,
+ ltime.hour,ltime.minute,ltime.second);
+ str->length(19);
+ }
+ return str;
+
+ null_date:
+ null_value=1;
+ return 0;
+}
+
+longlong Item_date_add_interval::val_int()
+{
+ TIME ltime;
+ if (Item_date_add_interval::get_date(&ltime,0))
+ return (longlong) 0;
+ return ((longlong) (((ulong) ltime.year)*10000L+
+ (((uint) ltime.month)*100+
+ (uint) ltime.day))*(longlong) 1000000L+
+ (longlong) ((ulong) ((uint) ltime.hour)*10000L+
+ (ulong) (((uint)ltime.minute)*100L+
+ (uint) ltime.second)));
+}
+
+void Item_extract::fix_length_and_dec()
+{
+ value.alloc(32); // alloc buffer
+
+ maybe_null=1; // If wrong date
+ switch (int_type) {
+ case INTERVAL_YEAR: max_length=4; date_value=1; break;
+ case INTERVAL_YEAR_MONTH: max_length=6; date_value=1; break;
+ case INTERVAL_MONTH: max_length=2; date_value=1; break;
+ case INTERVAL_DAY: max_length=2; date_value=1; break;
+ case INTERVAL_DAY_HOUR: max_length=9; date_value=0; break;
+ case INTERVAL_DAY_MINUTE: max_length=11; date_value=0; break;
+ case INTERVAL_DAY_SECOND: max_length=13; date_value=0; break;
+ case INTERVAL_HOUR: max_length=2; date_value=0; break;
+ case INTERVAL_HOUR_MINUTE: max_length=4; date_value=0; break;
+ case INTERVAL_HOUR_SECOND: max_length=6; date_value=0; break;
+ case INTERVAL_MINUTE: max_length=2; date_value=0; break;
+ case INTERVAL_MINUTE_SECOND: max_length=4; date_value=0; break;
+ case INTERVAL_SECOND: max_length=2; date_value=0; break;
+ }
+}
+
+
+longlong Item_extract::val_int()
+{
+ TIME ltime;
+ long neg;
+ if (date_value)
+ {
+ if (get_arg0_date(&ltime,1))
+ return 0;
+ neg=1;
+ }
+ else
+ {
+ String *res= args[0]->val_str(&value);
+ if (!res || str_to_time(res->ptr(),res->length(),&ltime))
+ {
+ null_value=1;
+ return 0;
+ }
+ neg= ltime.neg ? -1 : 1;
+ null_value=0;
+ }
+
+ switch (int_type) {
+ case INTERVAL_YEAR: return ltime.year;
+ case INTERVAL_YEAR_MONTH: return ltime.year*100L+ltime.month;
+ case INTERVAL_MONTH: return ltime.month;
+ case INTERVAL_DAY: return ltime.day;
+ case INTERVAL_DAY_HOUR: return (long) (ltime.day*100L+ltime.hour)*neg;
+ case INTERVAL_DAY_MINUTE: return (long) (ltime.day*10000L+
+ ltime.hour*100L+
+ ltime.minute)*neg;
+ case INTERVAL_DAY_SECOND: return ((longlong) ltime.day*1000000L+
+ (longlong) (ltime.hour*10000L+
+ ltime.minute*100+
+ ltime.second))*neg;
+ case INTERVAL_HOUR: return (long) ltime.hour*neg;
+ case INTERVAL_HOUR_MINUTE: return (long) (ltime.hour*100+ltime.minute)*neg;
+ case INTERVAL_HOUR_SECOND: return (long) (ltime.hour*10000+ltime.minute*100+
+ ltime.second)*neg;
+ case INTERVAL_MINUTE: return (long) ltime.minute*neg;
+ case INTERVAL_MINUTE_SECOND: return (long) (ltime.minute*100+ltime.second)*neg;
+ case INTERVAL_SECOND: return (long) ltime.second*neg;
+ }
+ return 0; // Impossible
+}
diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h
new file mode 100644
index 00000000000..1343cdad390
--- /dev/null
+++ b/sql/item_timefunc.h
@@ -0,0 +1,370 @@
+/* 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 */
+
+
+/* Function items used by mysql */
+
+#ifdef __GNUC__
+#pragma interface /* gcc class implementation */
+#endif
+
+class Item_func_period_add :public Item_int_func
+{
+public:
+ Item_func_period_add(Item *a,Item *b) :Item_int_func(a,b) {}
+ longlong val_int();
+ const char *func_name() const { return "period_add"; }
+ void fix_length_and_dec() { max_length=6; }
+};
+
+
+class Item_func_period_diff :public Item_int_func
+{
+public:
+ Item_func_period_diff(Item *a,Item *b) :Item_int_func(a,b) {}
+ longlong val_int();
+ const char *func_name() const { return "period_diff"; }
+ void fix_length_and_dec() { decimals=0; max_length=6; }
+};
+
+
+class Item_func_to_days :public Item_int_func
+{
+public:
+ Item_func_to_days(Item *a) :Item_int_func(a) {}
+ longlong val_int();
+ const char *func_name() const { return "to_days"; }
+ void fix_length_and_dec() { decimals=0; max_length=6; }
+};
+
+
+class Item_func_dayofmonth :public Item_int_func
+{
+public:
+ Item_func_dayofmonth(Item *a) :Item_int_func(a) {}
+ longlong val_int();
+ const char *func_name() const { return "dayofmonth"; }
+ void fix_length_and_dec() { decimals=0; max_length=2; maybe_null=1; }
+};
+
+
+class Item_func_month :public Item_func
+{
+public:
+ Item_func_month(Item *a) :Item_func(a) {}
+ longlong val_int();
+ double val() { return (double) Item_func_month::val_int(); }
+ String *val_str(String *str) { str->set(val_int()); return null_value ? 0 : str;}
+ const char *func_name() const { return "month"; }
+ enum Item_result result_type () const { return INT_RESULT; }
+ void fix_length_and_dec() { decimals=0; max_length=2; maybe_null=1; }
+};
+
+class Item_func_monthname :public Item_func_month
+{
+public:
+ Item_func_monthname(Item *a) :Item_func_month(a) {}
+ const char *func_name() const { return "monthname"; }
+ String *val_str(String *str);
+ enum Item_result result_type () const { return STRING_RESULT; }
+ void fix_length_and_dec() { decimals=0; max_length=10; maybe_null=1; }
+};
+
+
+class Item_func_dayofyear :public Item_int_func
+{
+public:
+ Item_func_dayofyear(Item *a) :Item_int_func(a) {}
+ longlong val_int();
+ const char *func_name() const { return "dayofyear"; }
+ void fix_length_and_dec() { decimals=0; max_length=3; maybe_null=1; }
+};
+
+
+class Item_func_hour :public Item_int_func
+{
+public:
+ Item_func_hour(Item *a) :Item_int_func(a) {}
+ longlong val_int();
+ const char *func_name() const { return "hour"; }
+ void fix_length_and_dec() { decimals=0; max_length=2; maybe_null=1; }
+};
+
+
+class Item_func_minute :public Item_int_func
+{
+public:
+ Item_func_minute(Item *a) :Item_int_func(a) {}
+ longlong val_int();
+ const char *func_name() const { return "minute"; }
+ void fix_length_and_dec() { decimals=0; max_length=2; maybe_null=1; }
+};
+
+
+class Item_func_quarter :public Item_int_func
+{
+public:
+ Item_func_quarter(Item *a) :Item_int_func(a) {}
+ longlong val_int();
+ const char *func_name() const { return "quarter"; }
+ void fix_length_and_dec() { decimals=0; max_length=1; maybe_null=1; }
+};
+
+
+class Item_func_second :public Item_int_func
+{
+public:
+ Item_func_second(Item *a) :Item_int_func(a) {}
+ longlong val_int();
+ const char *func_name() const { return "second"; }
+ void fix_length_and_dec() { decimals=0; max_length=2; maybe_null=1; }
+};
+
+
+class Item_func_week :public Item_int_func
+{
+public:
+ Item_func_week(Item *a,Item *b) :Item_int_func(a,b) {}
+ longlong val_int();
+ const char *func_name() const { return "week"; }
+ void fix_length_and_dec() { decimals=0; max_length=2; maybe_null=1; }
+};
+
+class Item_func_yearweek :public Item_int_func
+{
+public:
+ Item_func_yearweek(Item *a,Item *b) :Item_int_func(a,b) {}
+ longlong val_int();
+ const char *func_name() const { return "yearweek"; }
+ void fix_length_and_dec() { decimals=0; max_length=6; maybe_null=1; }
+};
+
+
+class Item_func_year :public Item_int_func
+{
+public:
+ Item_func_year(Item *a) :Item_int_func(a) {}
+ longlong val_int();
+ const char *func_name() const { return "year"; }
+ void fix_length_and_dec() { decimals=0; max_length=4; maybe_null=1; }
+};
+
+
+class Item_func_weekday :public Item_func
+{
+ bool odbc_type;
+public:
+ Item_func_weekday(Item *a,bool type_arg)
+ :Item_func(a), odbc_type(type_arg) {}
+ longlong val_int();
+ double val() { return (double) val_int(); }
+ String *val_str(String *str) { str->set(val_int()); return null_value ? 0 : str;}
+ 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; }
+};
+
+class Item_func_dayname :public Item_func_weekday
+{
+ public:
+ Item_func_dayname(Item *a) :Item_func_weekday(a,0) {}
+ const char *func_name() const { return "dayname"; }
+ String *val_str(String *str);
+ enum Item_result result_type () const { return STRING_RESULT; }
+ void fix_length_and_dec() { decimals=0; max_length=9; maybe_null=1; }
+};
+
+
+class Item_func_unix_timestamp :public Item_int_func
+{
+ String value;
+public:
+ Item_func_unix_timestamp() :Item_int_func() {}
+ Item_func_unix_timestamp(Item *a) :Item_int_func(a) {}
+ longlong val_int();
+ const char *func_name() const { return "timestamp"; }
+ void fix_length_and_dec()
+ {
+ decimals=0; max_length=10;
+ }
+};
+
+
+class Item_func_time_to_sec :public Item_int_func
+{
+public:
+ Item_func_time_to_sec(Item *item) :Item_int_func(item) {}
+ longlong val_int();
+ const char *func_name() const { return "time_to_sec"; }
+ void fix_length_and_dec()
+ {
+ decimals=0; max_length=10;
+ }
+};
+
+
+/* This can't be a Item_str_func, because the val() functions are special */
+
+class Item_date :public Item_func
+{
+public:
+ Item_date() :Item_func() {}
+ Item_date(Item *a) :Item_func(a) {}
+ enum Item_result result_type () const { return STRING_RESULT; }
+ String *val_str(String *str);
+ 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);
+};
+
+
+class Item_func_curtime :public Item_func
+{
+ longlong value;
+ char buff[9];
+ uint buff_length;
+public:
+ Item_func_curtime() :Item_func() {}
+ Item_func_curtime(Item *a) :Item_func(a) {}
+ enum Item_result result_type () const { return STRING_RESULT; }
+ double val() { return (double) value; }
+ longlong val_int() { return value; }
+ String *val_str(String *str)
+ { str_value.set(buff,buff_length); return &str_value; }
+ const char *func_name() const { return "curtime"; }
+ void fix_length_and_dec();
+};
+
+
+class Item_func_curdate :public Item_date
+{
+ longlong value;
+ TIME ltime;
+public:
+ Item_func_curdate() :Item_date() {}
+ longlong val_int() { return (value) ; }
+ const char *func_name() const { return "curdate"; }
+ void fix_length_and_dec(); /* Retrieves curtime */
+ bool get_date(TIME *res,bool fuzzy_date);
+};
+
+
+class Item_func_now :public Item_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) {}
+ 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);
+ 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);
+};
+
+
+class Item_func_from_days :public Item_date
+{
+public:
+ Item_func_from_days(Item *a) :Item_date(a) {}
+ longlong val_int();
+ const char *func_name() const { return "from_days"; }
+};
+
+
+class Item_func_date_format :public Item_str_func
+{
+ int fixed_length;
+ const bool date_or_time;
+public:
+ Item_func_date_format(Item *a,Item *b,bool date_or_time_arg)
+ :Item_str_func(a,b),date_or_time(date_or_time_arg) {}
+ String *val_str(String *str);
+ const char *func_name() const { return "date_format"; }
+ void fix_length_and_dec();
+ uint format_length(const String *format);
+};
+
+
+class Item_func_from_unixtime :public Item_func
+{
+ public:
+ Item_func_from_unixtime(Item *a) :Item_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; }
+ bool get_date(TIME *res,bool fuzzy_date);
+};
+
+
+class Item_func_sec_to_time :public Item_str_func
+{
+public:
+ Item_func_sec_to_time(Item *item) :Item_str_func(item) {}
+ double val() { return (double) Item_func_sec_to_time::val_int(); }
+ longlong val_int();
+ String *val_str(String *);
+ void fix_length_and_dec() { maybe_null=1; max_length=13; }
+ const char *func_name() const { return "sec_to_time"; }
+};
+
+enum interval_type { INTERVAL_YEAR, INTERVAL_MONTH, INTERVAL_DAY,
+ INTERVAL_HOUR, INTERVAL_MINUTE, INTERVAL_SECOND,
+ INTERVAL_YEAR_MONTH, INTERVAL_DAY_HOUR,
+ INTERVAL_DAY_MINUTE, INTERVAL_DAY_SECOND,
+ INTERVAL_HOUR_MINUTE, INTERVAL_HOUR_SECOND,
+ INTERVAL_MINUTE_SECOND};
+
+class Item_date_add_interval :public Item_str_func
+{
+ const interval_type int_type;
+ String value;
+ const bool date_sub_interval;
+
+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) {}
+ 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);
+};
+
+class Item_extract :public Item_int_func
+{
+ const interval_type int_type;
+ String value;
+ bool date_value;
+ public:
+ Item_extract(interval_type type_arg, Item *a)
+ :Item_int_func(a), int_type(type_arg) {}
+ longlong val_int();
+ const char *func_name() const { return "extract"; }
+ void fix_length_and_dec();
+};
diff --git a/sql/item_uniq.cc b/sql/item_uniq.cc
new file mode 100755
index 00000000000..80ed6433fd8
--- /dev/null
+++ b/sql/item_uniq.cc
@@ -0,0 +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 */
+
+/* Compability file */
+#ifdef __GNUC__
+#pragma implementation
+#endif
+
+#include "mysql_priv.h"
diff --git a/sql/item_uniq.h b/sql/item_uniq.h
new file mode 100755
index 00000000000..ff11222e2ee
--- /dev/null
+++ b/sql/item_uniq.h
@@ -0,0 +1,46 @@
+/* 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 */
+
+/* Compability file ; This file only contains dummy functions */
+
+#ifdef __GNUC__
+#pragma interface
+#endif
+
+#include <queues.h>
+
+class Item_func_unique_users :public Item_real_func
+{
+public:
+ Item_func_unique_users(Item *name_arg,int start,int end,List<Item> &list)
+ :Item_real_func(list) {}
+ double val() { return 0.0; }
+ void fix_length_and_dec() { decimals=0; max_length=6; }
+};
+
+class Item_sum_unique_users :public Item_sum_num
+{
+public:
+ Item_sum_unique_users(Item *name_arg,int start,int end,Item *item_arg)
+ :Item_sum_num(item_arg) {}
+ double val() { return 0.0; }
+ enum Sumfunctype sum_func () const {return UNIQUE_USERS_FUNC;}
+ void reset() {}
+ bool add() { return 0; }
+ void reset_field() {}
+ void update_field(int offset) {}
+ bool fix_fields(THD *thd,struct st_table_list *tlist) { return 0;}
+};
diff --git a/sql/key.cc b/sql/key.cc
new file mode 100644
index 00000000000..df3f0fe25d5
--- /dev/null
+++ b/sql/key.cc
@@ -0,0 +1,266 @@
+/* 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 */
+
+
+/* Functions to handle keys and fields in forms */
+
+#include "mysql_priv.h"
+
+ /*
+ ** Search after with key field is. If no key starts with field test
+ ** if field is part of some key.
+ **
+ ** returns number of key. keylength is set to length of key before
+ ** (not including) field
+ ** Used when calculating key for NEXT_NUMBER
+ */
+
+int find_ref_key(TABLE *table,Field *field, uint *key_length)
+{
+ reg2 int i;
+ reg3 KEY *key_info;
+ uint fieldpos;
+
+ fieldpos= field->offset();
+
+ /* Test if some key starts as fieldpos */
+
+ for (i=0, key_info=table->key_info ; i < (int) table->keys ; i++, key_info++)
+ {
+ if (key_info->key_part[0].offset == fieldpos)
+ { /* Found key. Calc keylength */
+ *key_length=0;
+ return(i); /* Use this key */
+ }
+ }
+ /* Test if some key contains fieldpos */
+
+ for (i=0, key_info=table->key_info ; i < (int) table->keys ; i++, key_info++)
+ {
+ uint j;
+ KEY_PART_INFO *key_part;
+ *key_length=0;
+ for (j=0, key_part=key_info->key_part ;
+ j < key_info->key_parts ;
+ j++, key_part++)
+ {
+ if (key_part->offset == fieldpos)
+ return(i); /* Use this key */
+ *key_length+=key_part->length;
+ }
+ }
+ return(-1); /* No key is ok */
+}
+
+
+ /* Copy a key from record to some buffer */
+ /* if length == 0 then copy hole key */
+
+void key_copy(byte *key,TABLE *table,uint idx,uint key_length)
+{
+ uint length;
+ KEY *key_info=table->key_info+idx;
+ KEY_PART_INFO *key_part;
+
+ if (key_length == 0)
+ key_length=key_info->key_length+key_info->extra_length;
+ for (key_part=key_info->key_part;
+ (int) key_length > 0 ;
+ key_part++)
+ {
+ if (key_part->null_bit)
+ {
+ *key++= test(table->record[0][key_part->null_offset] &
+ key_part->null_bit);
+ key_length--;
+ }
+ if (key_part->key_part_flag & HA_BLOB_PART)
+ {
+ char *pos;
+ ulong blob_length=((Field_blob*) key_part->field)->get_length();
+ key_length-=2;
+ ((Field_blob*) key_part->field)->get_ptr(&pos);
+ length=min(key_length,key_part->length);
+ set_if_smaller(blob_length,length);
+ int2store(key,(uint) blob_length);
+ key+=2; // Skipp length info
+ memcpy(key,pos,blob_length);
+ }
+ else
+ {
+ length=min(key_length,key_part->length);
+ memcpy(key,table->record[0]+key_part->offset,(size_t) length);
+ }
+ key+=length;
+ key_length-=length;
+ }
+} /* key_copy */
+
+
+ /* restore a key from some buffer to record */
+
+void key_restore(TABLE *table,byte *key,uint idx,uint key_length)
+{
+ uint length;
+ KEY *key_info=table->key_info+idx;
+ KEY_PART_INFO *key_part;
+
+ if (key_length == 0)
+ {
+ if (idx == (uint) -1)
+ return;
+ key_length=key_info->key_length+key_info->extra_length;
+ }
+ for (key_part=key_info->key_part;
+ (int) key_length > 0 ;
+ key_part++)
+ {
+ if (key_part->null_bit)
+ {
+ if (*key++)
+ table->record[0][key_part->null_offset]|= key_part->null_bit;
+ else
+ table->record[0][key_part->null_offset]&= ~key_part->null_bit;
+ key_length--;
+ }
+ if (key_part->key_part_flag & HA_BLOB_PART)
+ {
+ uint blob_length=uint2korr(key);
+ key+=2;
+ key_length-=2;
+ ((Field_blob*) key_part->field)->set_ptr((ulong) blob_length,
+ (char*) key);
+ length=key_part->length;
+ }
+ else
+ {
+ length=min(key_length,key_part->length);
+ memcpy(table->record[0]+key_part->offset,key,(size_t) length);
+ }
+ key+=length;
+ key_length-=length;
+ }
+} /* key_restore */
+
+
+ /* Compare if a key has changed */
+
+int key_cmp(TABLE *table,const byte *key,uint idx,uint key_length)
+{
+ uint length;
+ KEY_PART_INFO *key_part;
+
+ for (key_part=table->key_info[idx].key_part;
+ (int) key_length > 0;
+ key_part++, key+=length, key_length-=length)
+ {
+ if (key_part->null_bit)
+ {
+ key_length--;
+ if (*key != test(table->record[0][key_part->null_offset] &
+ key_part->null_bit))
+ return 1;
+ if (*key)
+ {
+ length=key_part->store_length;
+ continue;
+ }
+ key++;
+ }
+ if (key_part->key_part_flag & (HA_BLOB_PART | HA_VAR_LENGTH))
+ {
+ if (key_part->field->key_cmp(key, key_part->length+2))
+ return 1;
+ length=key_part->length+2;
+ }
+ else
+ {
+ length=min(key_length,key_part->length);
+ if (!(key_part->key_type & (FIELDFLAG_NUMBER+FIELDFLAG_BINARY+
+ FIELDFLAG_PACK)))
+ {
+ if (my_sortcmp((char*) key,(char*) table->record[0]+key_part->offset,
+ length))
+ return 1;
+ }
+ else if (memcmp(key,table->record[0]+key_part->offset,length))
+ return 1;
+ }
+ }
+ return 0;
+}
+
+ /* unpack key-fields from record to some buffer */
+ /* This is used to get a good error message */
+
+void key_unpack(String *to,TABLE *table,uint idx)
+{
+ KEY_PART_INFO *key_part,*key_part_end;
+ Field *field;
+ String tmp;
+ DBUG_ENTER("key_unpack");
+
+ to->length(0);
+ for (key_part=table->key_info[idx].key_part,key_part_end=key_part+
+ table->key_info[idx].key_parts ;
+ key_part < key_part_end;
+ key_part++)
+ {
+ if (to->length())
+ to->append('-');
+ if (key_part->null_bit)
+ {
+ if (table->record[0][key_part->null_offset] & key_part->null_bit)
+ {
+ to->append("NULL");
+ continue;
+ }
+ }
+ if ((field=key_part->field))
+ {
+ field->val_str(&tmp,&tmp);
+ if (key_part->length < field->pack_length())
+ tmp.length(min(tmp.length(),key_part->length));
+ to->append(tmp);
+ }
+ else
+ to->append("???");
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+/* Return 1 if any field in a list is part of key */
+
+bool check_if_key_used(TABLE *table, uint idx, List<Item> &fields)
+{
+ List_iterator<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 ;
+ key_part < key_part_end;
+ key_part++)
+ {
+ Item_field *field;
+ f.rewind();
+ while ((field=(Item_field*) f++))
+ {
+ if (key_part->field == field->field)
+ return 1;
+ }
+ }
+ return 0;
+}
diff --git a/sql/lex.h b/sql/lex.h
new file mode 100644
index 00000000000..75c11a52273
--- /dev/null
+++ b/sql/lex.h
@@ -0,0 +1,446 @@
+/* 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 */
+
+
+/* This file includes all reserved words and functions */
+
+#include "lex_symbol.h"
+
+/* We don't want to include sql_yacc.h into gen_lex_hash */
+
+#ifdef NO_YACC_SYMBOLS
+#define SYM(A) 0
+#define CREATE_FUNC(A) 0
+#else
+#define SYM(A) A
+#define CREATE_FUNC(A) (void*) (A)
+#endif
+
+/*
+** Symbols are breaked in to separated arrays to allow fieldnames with
+** same name as functions
+** Theese are kept sorted for human lookup (the symbols are hashed)
+*/
+
+static SYMBOL symbols[] = {
+ { "&&", SYM(AND),0,0},
+ { "<", SYM(LT),0,0},
+ { "<=", SYM(LE),0,0},
+ { "<>", SYM(NE),0,0},
+ { "!=", SYM(NE),0,0},
+ { "=", SYM(EQ),0,0},
+ { ">", SYM(GT_SYM),0,0},
+ { ">=", SYM(GE),0,0},
+ { "<<", SYM(SHIFT_LEFT),0,0},
+ { ">>", SYM(SHIFT_RIGHT),0,0},
+ { "<=>", SYM(EQUAL_SYM),0,0},
+ { "ACTION", SYM(ACTION),0,0},
+ { "ADD", SYM(ADD),0,0},
+ { "AGGREGATE", SYM(AGGREGATE_SYM),0,0},
+ { "ALL", SYM(ALL),0,0},
+ { "ALTER", SYM(ALTER),0,0},
+ { "AFTER", SYM(AFTER_SYM),0,0},
+ { "AGAINST", SYM(AGAINST),0,0},
+ { "ANALYZE", SYM(ANALYZE_SYM),0,0},
+ { "AND", SYM(AND),0,0},
+ { "AS", SYM(AS),0,0},
+ { "ASC", SYM(ASC),0,0},
+ { "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},
+ { "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},
+ { "BLOB", SYM(BLOB_SYM),0,0},
+ { "BOOL", SYM(BOOL_SYM),0,0},
+ { "BOTH", SYM(BOTH),0,0},
+ { "BY", SYM(BY),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},
+ { "CHANGE", SYM(CHANGE),0,0},
+ { "CHECK", SYM(CHECK_SYM),0,0},
+ { "CHECKSUM", SYM(CHECKSUM_SYM),0,0},
+ { "COLLECTION", SYM(COLLECTION),0,0},
+ { "COLUMN", SYM(COLUMN_SYM),0,0},
+ { "COLUMNS", SYM(COLUMNS),0,0},
+ { "COMMENT", SYM(COMMENT_SYM),0,0},
+ { "COMMIT", SYM(COMMIT_SYM),0,0},
+ { "COMPRESSED", SYM(COMPRESSED_SYM),0,0},
+ { "CONSTRAINT", SYM(CONSTRAINT),0,0},
+ { "CREATE", SYM(CREATE),0,0},
+ { "CROSS", SYM(CROSS),0,0},
+ { "CURRENT_DATE", SYM(CURDATE),0,0},
+ { "CURRENT_TIME", SYM(CURTIME),0,0},
+ { "CURRENT_TIMESTAMP", SYM(NOW_SYM),0,0},
+ { "DATA", SYM(DATA_SYM),0,0},
+ { "DATABASE", SYM(DATABASE),0,0},
+ { "DATABASES", SYM(DATABASES),0,0},
+ { "DATE", SYM(DATE_SYM),0,0},
+ { "DATETIME", SYM(DATETIME),0,0},
+ { "DAY", SYM(DAY_SYM),0,0},
+ { "DAY_HOUR", SYM(DAY_HOUR_SYM),0,0},
+ { "DAY_MINUTE", SYM(DAY_MINUTE_SYM),0,0},
+ { "DAY_SECOND", SYM(DAY_SECOND_SYM),0,0},
+ { "DEC", SYM(DECIMAL_SYM),0,0},
+ { "DECIMAL", SYM(DECIMAL_SYM),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},
+ { "DISTINCT", SYM(DISTINCT),0,0},
+ { "DISTINCTROW", SYM(DISTINCT),0,0}, /* Access likes this */
+ { "DOUBLE", SYM(DOUBLE_SYM),0,0},
+ { "DROP", SYM(DROP),0,0},
+ { "DUMPFILE", SYM(DUMPFILE),0,0},
+ { "DYNAMIC", SYM(DYNAMIC_SYM),0,0},
+ { "END", SYM(END),0,0},
+ { "ELSE", SYM(ELSE),0,0},
+ { "ESCAPE", SYM(ESCAPE_SYM),0,0},
+ { "ESCAPED", SYM(ESCAPED),0,0},
+ { "ENCLOSED", SYM(ENCLOSED),0,0},
+ { "ENUM", SYM(ENUM),0,0},
+ { "EXPLAIN", SYM(DESCRIBE),0,0},
+ { "EXISTS", SYM(EXISTS),0,0},
+ { "EXTENDED", SYM(EXTENDED_SYM),0,0},
+ { "FIELDS", SYM(COLUMNS),0,0},
+ { "FILE", SYM(FILE_SYM),0,0},
+ { "FIRST", SYM(FIRST_SYM),0,0},
+ { "FIXED", SYM(FIXED_SYM),0,0},
+ { "FLOAT", SYM(FLOAT_SYM),0,0},
+ { "FLOAT4", SYM(FLOAT_SYM),0,0},
+ { "FLOAT8", SYM(DOUBLE_SYM),0,0},
+ { "FLUSH", SYM(FLUSH_SYM),0,0},
+ { "FOREIGN", SYM(FOREIGN),0,0},
+ { "RAID_TYPE", SYM(RAID_TYPE),0,0},
+ { "RAID_CHUNKS", SYM(RAID_CHUNKS),0,0},
+ { "RAID_CHUNKSIZE", SYM(RAID_CHUNKSIZE),0,0},
+ { "ROW_FORMAT", SYM(ROW_FORMAT_SYM),0,0},
+ { "FROM", SYM(FROM),0,0},
+ { "FOR", SYM(FOR_SYM),0,0},
+ { "FULL", SYM(FULL),0,0},
+ { "FUNCTION", SYM(UDF_SYM),0,0},
+ { "GRANT", SYM(GRANT),0,0},
+ { "GRANTS", SYM(GRANTS),0,0},
+ { "GROUP", SYM(GROUP),0,0},
+ { "HAVING", SYM(HAVING),0,0},
+ { "HEAP", SYM(HEAP_SYM),0,0},
+ { "HIGH_PRIORITY", SYM(HIGH_PRIORITY),0,0},
+ { "HOUR", SYM(HOUR_SYM),0,0},
+ { "HOUR_MINUTE", SYM(HOUR_MINUTE_SYM),0,0},
+ { "HOUR_SECOND", SYM(HOUR_SECOND_SYM),0,0},
+ { "HOSTS", SYM(HOSTS_SYM),0,0},
+ { "IDENTIFIED", SYM(IDENTIFIED_SYM),0,0},
+ { "IGNORE", SYM(IGNORE_SYM),0,0},
+ { "IN", SYM(IN_SYM),0,0},
+ { "INDEX", SYM(INDEX),0,0},
+ { "INFILE", SYM(INFILE),0,0},
+ { "INNER", SYM(INNER_SYM),0,0},
+ { "INSERT", SYM(INSERT),0,0},
+ { "INSERT_ID", SYM(INSERT_ID),0,0},
+ { "INT", SYM(INT_SYM),0,0},
+ { "INTEGER", SYM(INT_SYM),0,0},
+ { "INTERVAL", SYM(INTERVAL_SYM),0,0},
+ { "INT1", SYM(TINYINT),0,0},
+ { "INT2", SYM(SMALLINT),0,0},
+ { "INT3", SYM(MEDIUMINT),0,0},
+ { "INT4", SYM(INT_SYM),0,0},
+ { "INT8", SYM(BIGINT),0,0},
+ { "INTO", SYM(INTO),0,0},
+ { "IF", SYM(IF),0,0},
+ { "IS", SYM(IS),0,0},
+ { "ISAM", SYM(ISAM_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},
+ { "LEADING", SYM(LEADING),0,0},
+ { "LEFT", SYM(LEFT),0,0},
+ { "LIKE", SYM(LIKE),0,0},
+ { "LINES", SYM(LINES),0,0},
+ { "LIMIT", SYM(LIMIT),0,0},
+ { "LOAD", SYM(LOAD),0,0},
+ { "LOCAL", SYM(LOCAL_SYM),0,0},
+ { "LOCK", SYM(LOCK_SYM),0,0},
+ { "LOGS", SYM(LOGS_SYM),0,0},
+ { "LONG", SYM(LONG_SYM),0,0},
+ { "LONGBLOB", SYM(LONGBLOB),0,0},
+ { "LONGTEXT", SYM(LONGTEXT),0,0},
+ { "LOW_PRIORITY", SYM(LOW_PRIORITY),0,0},
+ { "MAX", SYM(MAX_SYM),0,0},
+ { "MASTER", SYM(MASTER_SYM),0,0},
+ { "MASTER_CONNECT_RETRY", SYM(MASTER_CONNECT_RETRY_SYM),0,0},
+ { "MASTER_HOST", SYM(MASTER_HOST_SYM),0,0},
+ { "MASTER_LOG_FILE", SYM(MASTER_LOG_FILE_SYM),0,0},
+ { "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_USER", SYM(MASTER_USER_SYM),0,0},
+ { "MAX_ROWS", SYM(MAX_ROWS),0,0},
+ { "MATCH", SYM(MATCH),0,0},
+ { "MEDIUMBLOB", SYM(MEDIUMBLOB),0,0},
+ { "MEDIUMTEXT", SYM(MEDIUMTEXT),0,0},
+ { "MEDIUMINT", SYM(MEDIUMINT),0,0},
+ { "MERGE", SYM(MERGE_SYM),0,0},
+ { "MIDDLEINT", SYM(MEDIUMINT),0,0}, /* For powerbuilder */
+ { "MIN_ROWS", SYM(MIN_ROWS),0,0},
+ { "MINUTE", SYM(MINUTE_SYM),0,0},
+ { "MINUTE_SECOND", SYM(MINUTE_SECOND_SYM),0,0},
+ { "MODIFY", SYM(MODIFY_SYM),0,0},
+ { "MONTH", SYM(MONTH_SYM),0,0},
+ { "MYISAM", SYM(MYISAM_SYM),0,0},
+ { "NATURAL", SYM(NATURAL),0,0},
+ { "NATIONAL", SYM(NATIONAL_SYM),0,0},
+ { "NCHAR", SYM(NCHAR_SYM),0,0},
+ { "NUMERIC", SYM(NUMERIC_SYM),0,0},
+ { "NO", SYM(NO_SYM),0,0},
+ { "NOT", SYM(NOT),0,0},
+ { "NULL", SYM(NULL_SYM),0,0},
+ { "ON", SYM(ON),0,0},
+ { "OPTIMIZE", SYM(OPTIMIZE),0,0},
+ { "OPTION", SYM(OPTION),0,0},
+ { "OPTIONALLY", SYM(OPTIONALLY),0,0},
+ { "OR", SYM(OR),0,0},
+ { "ORDER", SYM(ORDER_SYM),0,0},
+ { "OUTER", SYM(OUTER),0,0},
+ { "OUTFILE", SYM(OUTFILE),0,0},
+ { "PACK_KEYS", SYM(PACK_KEYS_SYM),0,0},
+ { "PARTIAL", SYM(PARTIAL),0,0},
+ { "PASSWORD", SYM(PASSWORD),0,0},
+ { "PRECISION", SYM(PRECISION),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},
+ { "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},
+ { "RELOAD", SYM(RELOAD),0,0},
+ { "REGEXP", SYM(REGEXP),0,0},
+ { "RENAME", SYM(RENAME),0,0},
+ { "REPAIR", SYM(REPAIR),0,0},
+ { "REPLACE", SYM(REPLACE),0,0},
+ { "RESTRICT", SYM(RESTRICT),0,0},
+ { "RETURNS", SYM(UDF_RETURNS_SYM),0,0},
+ { "REVOKE", SYM(REVOKE),0,0},
+ { "RLIKE", SYM(REGEXP),0,0}, /* Like in mSQL2 */
+ { "ROLLBACK", SYM(ROLLBACK_SYM),0,0},
+ { "ROW", SYM(ROW_SYM),0,0},
+ { "ROWS", SYM(ROWS_SYM),0,0},
+ { "SECOND", SYM(SECOND_SYM),0,0},
+ { "SELECT", SYM(SELECT_SYM),0,0},
+ { "SET", SYM(SET),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_SAFE_UPDATES", SYM(SQL_SAFE_UPDATES),0,0},
+ { "SQL_SELECT_LIMIT", SYM(SQL_SELECT_LIMIT),0,0},
+ { "SQL_SMALL_RESULT", SYM(SQL_SMALL_RESULT),0,0},
+ { "SQL_WARNINGS", SYM(SQL_WARNINGS),0,0},
+ { "STRAIGHT_JOIN", SYM(STRAIGHT_JOIN),0,0},
+ { "START", SYM(START_SYM),0,0},
+ { "STARTING", SYM(STARTING),0,0},
+ { "STATUS", SYM(STATUS_SYM),0,0},
+ { "STRING", SYM(STRING_SYM),0,0},
+ { "STOP", SYM(STOP_SYM),0,0},
+ { "STRIPED", SYM(RAID_STRIPED_SYM),0,0},
+ { "TABLE", SYM(TABLE_SYM),0,0},
+ { "TABLES", SYM(TABLES),0,0},
+ { "TEMPORARY", SYM(TEMPORARY),0,0},
+ { "TERMINATED", SYM(TERMINATED),0,0},
+ { "TEXT", SYM(TEXT_SYM),0,0},
+ { "THEN", SYM(THEN_SYM),0,0},
+ { "TIME", SYM(TIME_SYM),0,0},
+ { "TIMESTAMP", SYM(TIMESTAMP),0,0},
+ { "TINYBLOB", SYM(TINYBLOB),0,0},
+ { "TINYTEXT", SYM(TINYTEXT),0,0},
+ { "TINYINT", SYM(TINYINT),0,0},
+ { "TRAILING", SYM(TRAILING),0,0},
+ { "TO", SYM(TO_SYM),0,0},
+ { "TYPE", SYM(TYPE_SYM),0,0},
+ { "USE", SYM(USE_SYM),0,0},
+ { "USING", SYM(USING),0,0},
+ { "UNIQUE", SYM(UNIQUE_SYM),0,0},
+ { "UNLOCK", SYM(UNLOCK_SYM),0,0},
+ { "UNSIGNED", SYM(UNSIGNED),0,0},
+ { "UPDATE", SYM(UPDATE_SYM),0,0},
+ { "USAGE", SYM(USAGE),0,0},
+ { "VALUES", SYM(VALUES),0,0},
+ { "VARCHAR", SYM(VARCHAR),0,0},
+ { "VARIABLES", SYM(VARIABLES),0,0},
+ { "VARYING", SYM(VARYING),0,0},
+ { "VARBINARY", SYM(VARBINARY),0,0},
+ { "WITH", SYM(WITH),0,0},
+ { "WORK", SYM(WORK_SYM),0,0},
+ { "WRITE", SYM(WRITE_SYM),0,0},
+ { "WHEN", SYM(WHEN_SYM),0,0},
+ { "WHERE", SYM(WHERE),0,0},
+ { "YEAR", SYM(YEAR_SYM),0,0},
+ { "YEAR_MONTH", SYM(YEAR_MONTH_SYM),0,0},
+ { "ZEROFILL", SYM(ZEROFILL),0,0},
+ { "||", SYM(OR_OR_CONCAT),0,0}
+};
+
+
+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},
+ { "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},
+ { "ATAN2", SYM(ATAN),0,0},
+ { "BENCHMARK", SYM(BENCHMARK_SYM),0,0},
+ { "BIN", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_bin)},
+ { "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},
+ { "CEILING", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ceiling)},
+ { "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},
+ { "CONCAT", SYM(CONCAT),0,0},
+ { "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)},
+ { "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)},
+ { "CURDATE", SYM(CURDATE),0,0},
+ { "CURTIME", SYM(CURTIME),0,0},
+ { "DATE_ADD", SYM(DATE_ADD_INTERVAL),0,0},
+ { "DATE_FORMAT", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_date_format)},
+ { "DATE_SUB", SYM(DATE_SUB_INTERVAL),0,0},
+ { "DAYNAME", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_dayname)},
+ { "DAYOFMONTH", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_dayofmonth)},
+ { "DAYOFWEEK", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_dayofweek)},
+ { "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)},
+ { "ELT", SYM(ELT_FUNC),0,0},
+ { "ENCODE", SYM(ENCODE_SYM),0,0},
+ { "ENCRYPT", SYM(ENCRYPT),0,0},
+ { "EXTRACT", SYM(EXTRACT_SYM),0,0},
+ { "EXP", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_exp)},
+ { "EXPORT_SET", SYM(EXPORT_SET),0,0},
+ { "FIELD", SYM(FIELD_FUNC),0,0}, /* For compability */
+ { "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},
+ { "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)},
+ { "GREATEST", SYM(GREATEST_SYM),0,0},
+ { "GROUP_UNIQUE_USERS", SYM(GROUP_UNIQUE_USERS),0,0},
+ { "HEX", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_hex)},
+ { "IFNULL", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_ifnull)},
+ { "INET_ATON", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_inet_aton)},
+ { "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)},
+ { "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)},
+ { "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)},
+ { "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)},
+ { "MAKE_SET", SYM(MAKE_SET_SYM),0,0},
+ { "MD5", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_md5)},
+ { "MID", SYM(SUBSTRING),0,0}, /* unireg function */
+ { "MIN", SYM(MIN_SYM),0,0},
+ { "MOD", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_mod)},
+ { "MONTHNAME", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_monthname)},
+ { "NOW", SYM(NOW_SYM),0,0},
+ { "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)},
+ { "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)},
+ { "PI", SYM(FUNC_ARG0),0,CREATE_FUNC(create_func_pi)},
+ { "POSITION", SYM(POSITION_SYM),0,0},
+ { "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)},
+ { "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)},
+ { "REPEAT", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_repeat)},
+ { "REVERSE", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_reverse)},
+ { "RIGHT", SYM(RIGHT),0,0},
+ { "ROUND", SYM(ROUND),0,0},
+ { "RPAD", SYM(FUNC_ARG3),0,CREATE_FUNC(create_func_rpad)},
+ { "RTRIM", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_rtrim)},
+ { "SEC_TO_TIME", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_sec_to_time)},
+ { "SESSION_USER", SYM(USER),0,0},
+ { "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)},
+ { "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)},
+ { "STD", SYM(STD_SYM),0,0},
+ { "STDDEV", SYM(STD_SYM),0,0},
+ { "STRCMP", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_strcmp)},
+ { "SUBSTRING", SYM(SUBSTRING),0,0},
+ { "SUBSTRING_INDEX", SYM(SUBSTRING_INDEX),0,0},
+ { "SUM", SYM(SUM_SYM),0,0},
+ { "SYSDATE", SYM(NOW_SYM),0,0},
+ { "SYSTEM_USER", SYM(USER),0,0},
+ { "TAN", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_tan)},
+ { "TIME_FORMAT", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_time_format)},
+ { "TIME_TO_SEC", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_time_to_sec)},
+ { "TO_DAYS", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_to_days)},
+ { "TRIM", SYM(TRIM),0,0},
+ { "TRUNCATE", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_truncate )},
+ { "UCASE", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ucase)},
+ { "UPPER", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ucase)},
+ { "UNIQUE_USERS", SYM(UNIQUE_USERS),0,0},
+ { "UNIX_TIMESTAMP", SYM(UNIX_TIMESTAMP),0,0},
+ { "USER", SYM(USER),0,0},
+ { "VERSION", SYM(FUNC_ARG0),0,CREATE_FUNC(create_func_version)},
+ { "WEEK", SYM(WEEK_SYM),0,0},
+ { "WEEKDAY", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_weekday)},
+ { "YEARWEEK", SYM(YEARWEEK),0,0}
+};
diff --git a/sql/lex_symbol.h b/sql/lex_symbol.h
new file mode 100644
index 00000000000..a011e27b59e
--- /dev/null
+++ b/sql/lex_symbol.h
@@ -0,0 +1,37 @@
+/* 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 */
+
+
+/* This struct includes all reserved words and functions */
+
+#ifndef _lex_symbol_h
+#define _lex_symbol_h
+
+typedef struct st_symbol {
+ const char *name;
+ uint tok;
+ uint length;
+ void *create_func;
+} SYMBOL;
+
+typedef struct st_lex_symbol
+{
+ SYMBOL *symbol;
+ char *str;
+ uint length;
+} LEX_SYMBOL;
+
+#endif /* _lex_symbol_h */
diff --git a/sql/lock.cc b/sql/lock.cc
new file mode 100644
index 00000000000..b65ae5ddc3e
--- /dev/null
+++ b/sql/lock.cc
@@ -0,0 +1,386 @@
+/* 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 */
+
+
+/* locking functions for mysql */
+/*
+ Because of the new concurrent inserts, we must first get external locks
+ 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.
+
+TODO:
+ Change to use my_malloc() ONLY when using LOCK TABLES command or when
+ we are forced to use mysql_lock_merge.
+*/
+
+#include "mysql_priv.h"
+#include <hash.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 unlock_external(THD *thd, TABLE **table,uint count);
+
+
+MYSQL_LOCK *mysql_lock_tables(THD *thd,TABLE **tables,uint count)
+{
+ MYSQL_LOCK *sql_lock;
+ TABLE *write_lock_used;
+ DBUG_ENTER("mysql_lock_tables");
+
+ for (;;)
+ {
+ if (!(sql_lock = get_lock_data(thd,tables,count, 0,&write_lock_used)))
+ break;
+
+ if (global_read_lock && write_lock_used)
+ {
+ /*
+ 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
+ {
+ 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);
+ pthread_mutex_lock(&thd->mysys_var->mutex);
+ thd->mysys_var->current_mutex= &LOCK_open;
+ thd->mysys_var->current_cond= &COND_refresh;
+ thd->proc_info="Waiting for table";
+ pthread_mutex_unlock(&thd->mysys_var->mutex);
+
+ 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)
+ {
+ my_free((gptr) sql_lock,MYF(0));
+ goto retry;
+ }
+ }
+
+ thd->proc_info="System lock";
+ if (lock_external(tables,count))
+ {
+ my_free((gptr) sql_lock,MYF(0));
+ sql_lock=0;
+ thd->proc_info=0;
+ break;
+ }
+ thd->proc_info=0;
+ thd->locked=1;
+ if (thr_multi_lock(sql_lock->locks,sql_lock->lock_count))
+ {
+ thd->some_tables_deleted=1; // Try again
+ sql_lock->lock_count=0; // Locks are alread freed
+ }
+ else if (!thd->some_tables_deleted)
+ {
+ thd->locked=0;
+ break;
+ }
+
+ /* some table was altered or deleted. reopen tables marked deleted */
+ mysql_unlock_tables(thd,sql_lock);
+ thd->locked=0;
+retry:
+ sql_lock=0;
+ if (wait_for_tables(thd))
+ break; // Couldn't open tables
+ }
+ if (thd->killed)
+ {
+ my_error(ER_SERVER_SHUTDOWN,MYF(0));
+ if (sql_lock)
+ {
+ mysql_unlock_tables(thd,sql_lock);
+ sql_lock=0;
+ }
+ }
+ DBUG_RETURN (sql_lock);
+}
+
+
+static int lock_external(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++)
+ {
+ lock_type=F_WRLCK; /* Lock exclusive */
+ if ((*tables)->db_stat & HA_READ_ONLY ||
+ ((*tables)->reginfo.lock_type >= TL_READ &&
+ (*tables)->reginfo.lock_type <= TL_READ_NO_INSERT))
+ lock_type=F_RDLCK;
+
+ if ((error=(*tables)->file->external_lock(thd,lock_type)))
+ {
+ for ( ; i-- ; tables--)
+ {
+ (*tables)->file->external_lock(thd, F_UNLCK);
+ (*tables)->current_lock=F_UNLCK;
+ }
+ my_error(ER_CANT_LOCK,MYF(ME_BELL+ME_OLDWIN+ME_WAITTANG),error);
+ DBUG_RETURN(error);
+ }
+ else
+ {
+ (*tables)->db_stat &= ~ HA_BLOCK_LOCK;
+ (*tables)->current_lock= lock_type;
+ }
+ }
+ DBUG_RETURN(0);
+}
+
+
+void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock)
+{
+ DBUG_ENTER("mysql_unlock_tables");
+ thr_multi_unlock(sql_lock->locks,sql_lock->lock_count);
+ VOID(unlock_external(thd,sql_lock->table,sql_lock->table_count));
+ my_free((gptr) sql_lock,MYF(0));
+ DBUG_VOID_RETURN;
+}
+
+/*
+ Unlock some of the tables locked by mysql_lock_tables
+ This will work even if get_lock_data fails (next unlock will free all)
+ */
+
+void mysql_unlock_some_tables(THD *thd, TABLE **table,uint count)
+{
+ MYSQL_LOCK *sql_lock;
+ TABLE *write_lock_used;
+ if ((sql_lock = get_lock_data(thd, table, count, 1, &write_lock_used)))
+ mysql_unlock_tables(thd, sql_lock);
+}
+
+
+/*
+** unlock all tables locked for read.
+*/
+
+void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock)
+{
+ uint i,found;
+ DBUG_ENTER("mysql_unlock_read_tables");
+
+ /* Move all write locks first */
+ THR_LOCK_DATA **lock=sql_lock->locks;
+ for (i=found=0 ; i < sql_lock->lock_count ; i++)
+ {
+ if (sql_lock->locks[i]->type >= TL_WRITE_ALLOW_READ)
+ {
+ swap(THR_LOCK_DATA *,*lock,sql_lock->locks[i]);
+ lock++;
+ found++;
+ }
+ }
+ /* unlock the read locked tables */
+ if (i != found)
+ {
+ thr_multi_unlock(lock,i-found);
+ sql_lock->lock_count-=found;
+ }
+
+ /* Then to 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++)
+ {
+ if ((uint) sql_lock->table[i]->reginfo.lock_type >= TL_WRITE_ALLOW_READ)
+ {
+ swap(TABLE *,*table,sql_lock->table[i]);
+ table++;
+ found++;
+ }
+ }
+ /* Unlock all read locked tables */
+ if (i != found)
+ {
+ VOID(unlock_external(thd,table,i-found));
+ sql_lock->table_count-=found;
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+
+void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table)
+{
+ mysql_unlock_some_tables(thd, &table,1);
+ if (locked)
+ {
+ reg1 uint i;
+ for (i=0; i < locked->table_count; i++)
+ {
+ if (locked->table[i] == table)
+ {
+ locked->table_count--;
+ bmove((char*) (locked->table+i),
+ (char*) (locked->table+i+1),
+ (locked->table_count-i)* sizeof(TABLE*));
+ break;
+ }
+ }
+ THR_LOCK_DATA **prev=locked->locks;
+ for (i=0 ; i < locked->lock_count ; i++)
+ {
+ if (locked->locks[i]->type != TL_UNLOCK)
+ *prev++ = locked->locks[i];
+ }
+ locked->lock_count=(prev - locked->locks);
+ }
+}
+
+/* abort all other threads waiting to get lock in table */
+
+void mysql_lock_abort(THD *thd, TABLE *table)
+{
+ MYSQL_LOCK *locked;
+ TABLE *write_lock_used;
+ if ((locked = get_lock_data(thd,&table,1,1,&write_lock_used)))
+ {
+ for (uint i=0; i < locked->lock_count; i++)
+ thr_abort_locks(locked->locks[i]->lock);
+ my_free((gptr) locked,MYF(0));
+ }
+}
+
+
+MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b)
+{
+ MYSQL_LOCK *sql_lock;
+ DBUG_ENTER("mysql_lock_merge");
+ if (!(sql_lock= (MYSQL_LOCK*)
+ my_malloc(sizeof(*sql_lock)+
+ sizeof(THR_LOCK_DATA*)*(a->lock_count+b->lock_count)+
+ sizeof(TABLE*)*(a->table_count+b->table_count),MYF(MY_WME))))
+ DBUG_RETURN(0); // Fatal error
+ sql_lock->lock_count=a->lock_count+b->lock_count;
+ sql_lock->table_count=a->table_count+b->table_count;
+ sql_lock->locks=(THR_LOCK_DATA**) (sql_lock+1);
+ sql_lock->table=(TABLE**) (sql_lock->locks+sql_lock->lock_count);
+ memcpy(sql_lock->locks,a->locks,a->lock_count*sizeof(*a->locks));
+ memcpy(sql_lock->locks+a->lock_count,b->locks,
+ b->lock_count*sizeof(*b->locks));
+ memcpy(sql_lock->table,a->table,a->table_count*sizeof(*a->table));
+ memcpy(sql_lock->table+a->table_count,b->table,
+ b->table_count*sizeof(*b->table));
+ my_free((gptr) a,MYF(0));
+ my_free((gptr) b,MYF(0));
+ DBUG_RETURN(sql_lock);
+}
+
+
+ /* unlock a set of external */
+
+static int unlock_external(THD *thd, TABLE **table,uint count)
+{
+ int error,error_code;
+ DBUG_ENTER("unlock_external");
+
+ error_code=0;
+ for (; count-- ; table++)
+ {
+ if ((*table)->current_lock != F_UNLCK)
+ {
+ (*table)->current_lock = F_UNLCK;
+ if ((error=(*table)->file->external_lock(thd, F_UNLCK)))
+ error_code=error;
+ }
+ }
+ if (error_code)
+ my_error(ER_CANT_LOCK,MYF(ME_BELL+ME_OLDWIN+ME_WAITTANG),error_code);
+ DBUG_RETURN(error_code);
+}
+
+
+/*
+** Get lock structures from table structs and initialize locks
+*/
+
+
+static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
+ bool get_old_locks, TABLE **write_lock_used)
+{
+ uint i,tables,lock_count;
+ MYSQL_LOCK *sql_lock;
+ THR_LOCK_DATA **locks;
+ TABLE **to;
+
+ *write_lock_used=0;
+ for (i=tables=lock_count=0 ; i < count ; i++)
+ {
+ if (!table_ptr[i]->tmp_table)
+ {
+ tables+=table_ptr[i]->file->lock_count();
+ lock_count++;
+ }
+ }
+
+ if (!(sql_lock= (MYSQL_LOCK*)
+ my_malloc(sizeof(*sql_lock)+
+ sizeof(THR_LOCK_DATA*)*tables+sizeof(table_ptr)*lock_count,
+ MYF(0))))
+ return 0;
+ locks=sql_lock->locks=(THR_LOCK_DATA**) (sql_lock+1);
+ to=sql_lock->table=(TABLE**) (locks+tables);
+ sql_lock->table_count=lock_count;
+ sql_lock->lock_count=tables;
+
+ for (i=0 ; i < count ; i++)
+ {
+ TABLE *table;
+ if ((table=table_ptr[i])->tmp_table)
+ continue;
+ *to++=table;
+ enum thr_lock_type lock_type= table->reginfo.lock_type;
+ if (lock_type >= TL_WRITE_ALLOW_WRITE)
+ {
+ *write_lock_used=table;
+ if (table->db_stat & HA_READ_ONLY)
+ {
+ my_error(ER_OPEN_AS_READONLY,MYF(0),table->table_name);
+ my_free((gptr) sql_lock,MYF(0));
+ return 0;
+ }
+ }
+ locks=table->file->store_lock(thd, locks, get_old_locks ? TL_IGNORE :
+ lock_type);
+ }
+ return sql_lock;
+}
diff --git a/sql/log.cc b/sql/log.cc
new file mode 100644
index 00000000000..3c21a1a9422
--- /dev/null
+++ b/sql/log.cc
@@ -0,0 +1,733 @@
+/* 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 */
+
+
+/* logging of commands */
+
+#include "mysql_priv.h"
+#include "sql_acl.h"
+
+#include <my_dir.h>
+#include <stdarg.h>
+#include <m_ctype.h> // For test_if_number
+
+
+
+MYSQL_LOG mysql_log,mysql_update_log,mysql_slow_log,mysql_bin_log;
+extern I_List<i_string> binlog_do_db, binlog_ignore_db;
+
+static bool test_if_number(const char *str,
+ long *res, bool allow_wildcards);
+
+/****************************************************************************
+** Find a uniq filename for 'filename.#'.
+** Set # to a number as low as possible
+** returns != 0 if not possible to get uniq filename
+****************************************************************************/
+
+static int find_uniq_filename(char *name)
+{
+ long number;
+ uint i,length;
+ char buff[FN_REFLEN];
+ struct st_my_dir *dir_info;
+ reg1 struct fileinfo *file_info;
+ ulong max_found=0;
+ DBUG_ENTER("find_uniq_filename");
+
+ length=dirname_part(buff,name);
+ char *start=name+length,*end=strend(start);
+ *end='.';
+ length=end-start+1;
+
+ if (!(dir_info = my_dir(buff,MYF(MY_DONT_SORT))))
+ { // This shouldn't happen
+ strmov(end,".1"); // use name+1
+ DBUG_RETURN(0);
+ }
+ file_info= dir_info->dir_entry;
+ for (i=dir_info->number_off_files ; i-- ; file_info++)
+ {
+ if (bcmp(file_info->name,start,length) == 0 &&
+ test_if_number(file_info->name+length, &number,0))
+ {
+ set_if_bigger(max_found,(ulong) number);
+ }
+ }
+ my_dirend(dir_info);
+
+ *end++='.';
+ sprintf(end,"%03ld",max_found+1);
+ DBUG_RETURN(0);
+}
+
+
+
+MYSQL_LOG::MYSQL_LOG(): file(0),index_file(0),last_time(0),query_start(0),
+ name(0), log_type(LOG_CLOSED),write_error(0),inited(0)
+{
+ /*
+ We don't want to intialize LOCK_Log here as the thread system may
+ not have been initailized yet. We do it instead at 'open'.
+ */
+ index_file_name[0] = 0;
+}
+
+MYSQL_LOG::~MYSQL_LOG()
+{
+ if (inited)
+ {
+ (void) pthread_mutex_destroy(&LOCK_log);
+ (void) pthread_mutex_destroy(&LOCK_index);
+ }
+}
+
+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 (!fn_ext(log_name)[0])
+ {
+ if (find_uniq_filename(new_name))
+ {
+ sql_print_error(ER(ER_NO_UNIQUE_LOGFILE), log_name);
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+
+void MYSQL_LOG::open(const char *log_name, enum_log_type log_type_arg,
+ const char *new_name)
+{
+
+ if (!inited)
+ {
+ inited=1;
+ (void) pthread_mutex_init(&LOCK_log,NULL);
+ (void) pthread_mutex_init(&LOCK_index, NULL);
+ }
+
+ log_type=log_type_arg;
+ name=my_strdup(log_name,MYF(0));
+ if (new_name)
+ strmov(log_file_name,new_name);
+ else if (generate_new_name(log_file_name, name))
+ return;
+
+ if (log_type == LOG_BIN && !index_file_name[0])
+ fn_format(index_file_name, name, mysql_data_home, ".index", 6);
+
+ db[0]=0;
+ file=my_fopen(log_file_name,O_APPEND | O_WRONLY,MYF(MY_WME | ME_WAITTANG));
+ if (!file)
+ {
+ my_free(name,MYF(0));
+ name=0;
+ log_type=LOG_CLOSED;
+ return;
+ }
+
+ if (log_type == LOG_NORMAL)
+ {
+#ifdef __NT__
+ fprintf( file, "%s, Version: %s, started with:\nTCP Port: %d, Named Pipe: %s\n", my_progname, server_version, mysql_port, mysql_unix_port);
+#else
+ fprintf(file,"%s, Version: %s, started with:\nTcp port: %d Unix socket: %s\n", my_progname,server_version,mysql_port,mysql_unix_port);
+#endif
+ fprintf(file,"Time Id Command Argument\n");
+ (void) fflush(file);
+ }
+ else if (log_type == LOG_NEW)
+ {
+ time_t skr=time(NULL);
+ struct tm tm_tmp;
+ localtime_r(&skr,&tm_tmp);
+
+ fprintf(file,"# %s, Version: %s at %02d%02d%02d %2d:%02d:%02d\n",
+ my_progname,server_version,
+ tm_tmp.tm_year % 100,
+ tm_tmp.tm_mon+1,
+ tm_tmp.tm_mday,
+ tm_tmp.tm_hour,
+ tm_tmp.tm_min,
+ tm_tmp.tm_sec);
+ (void) fflush(file);
+ }
+ else if (log_type == LOG_BIN)
+ {
+ Start_log_event s;
+ if(!index_file &&
+ !(index_file = my_fopen(index_file_name,O_APPEND | O_RDWR,
+ MYF(MY_WME))))
+ {
+ my_fclose(file,MYF(MY_WME));
+ my_free(name,MYF(0));
+ name=0;
+ file=0;
+ log_type=LOG_CLOSED;
+ return;
+ }
+ s.write(file);
+ pthread_mutex_lock(&LOCK_index);
+ my_fseek(index_file, 0L, MY_SEEK_END, MYF(MY_WME));
+ fprintf(index_file, "%s\n", log_file_name);
+ fflush(index_file);
+ pthread_mutex_unlock(&LOCK_index);
+ }
+}
+
+int MYSQL_LOG::get_current_log(LOG_INFO* linfo)
+{
+ pthread_mutex_lock(&LOCK_log);
+ strmake(linfo->log_file_name, log_file_name, sizeof(linfo->log_file_name));
+ linfo->pos = my_ftell(file, MYF(MY_WME));
+ pthread_mutex_unlock(&LOCK_log);
+ 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)
+{
+ // mutex needed because we need to make sure the file pointer does not move
+ // from under our feet
+ if(!index_file) return LOG_INFO_INVALID;
+ int error = 0;
+ char* fname = linfo->log_file_name;
+ int log_name_len = strlen(log_name);
+
+ pthread_mutex_lock(&LOCK_index);
+ if(my_fseek(index_file, 0L, MY_SEEK_SET, MYF(MY_WME) ) == MY_FILEPOS_ERROR)
+ {
+ error = LOG_INFO_SEEK;
+ goto err;
+ }
+
+ for(;;)
+ {
+ if(!fgets(fname, FN_REFLEN, index_file))
+ {
+ error = feof(index_file) ? LOG_INFO_EOF : LOG_INFO_IO;
+ goto err;
+ }
+
+ // if the log entry matches, empty string matching anything
+ if(!log_name_len || (fname[log_name_len] == '\n' && !memcmp(fname, log_name, log_name_len)))
+ {
+ if(log_name_len)
+ fname[log_name_len] = 0; // to kill \n
+ else
+ {
+ *(strend(fname) - 1) = 0;
+ }
+ linfo->index_file_offset = my_ftell(index_file, MYF(MY_WME));
+ break;
+ }
+ }
+
+ error = 0;
+err:
+ pthread_mutex_unlock(&LOCK_index);
+ return error;
+
+}
+int MYSQL_LOG::find_next_log(LOG_INFO* linfo)
+{
+ // mutex needed because we need to make sure the file pointer does not move
+ // from under our feet
+ if(!index_file) return LOG_INFO_INVALID;
+ int error = 0;
+ char* fname = linfo->log_file_name;
+ char* end ;
+
+ pthread_mutex_lock(&LOCK_index);
+ if(my_fseek(index_file, linfo->index_file_offset, MY_SEEK_SET, MYF(MY_WME) ) == MY_FILEPOS_ERROR)
+ {
+ error = LOG_INFO_SEEK;
+ goto err;
+ }
+
+ if(!fgets(fname, FN_REFLEN, index_file))
+ {
+ error = feof(index_file) ? LOG_INFO_EOF : LOG_INFO_IO;
+ goto err;
+ }
+
+ end = strend(fname) - 1;
+ *end = 0; // kill /n
+ linfo->index_file_offset = ftell(index_file);
+ error = 0;
+err:
+ pthread_mutex_unlock(&LOCK_index);
+ return error;
+}
+
+
+// we assume that buf has at least FN_REFLEN bytes alloced
+void MYSQL_LOG::make_log_name(char* buf, const char* log_ident)
+{
+ if(inited)
+ {
+ int dir_len = dirname_length(log_file_name);
+ int ident_len = strlen(log_ident);
+ if(dir_len + ident_len + 1 > FN_REFLEN)
+ {
+ buf[0] = 0;
+ return; // protection agains malicious buffer overflow
+ }
+
+ memcpy(buf, log_file_name, dir_len);
+ memcpy(buf + dir_len, log_ident, ident_len + 1); // this takes care of \0
+ // at the end
+ }
+ else
+ buf[0] = 0;
+}
+
+bool MYSQL_LOG::is_active(const char* log_file_name)
+{
+ return inited && !strcmp(log_file_name, this->log_file_name);
+}
+
+void MYSQL_LOG::new_file()
+{
+ if (file)
+ {
+ char new_name[FN_REFLEN], *old_name=name;
+ VOID(pthread_mutex_lock(&LOCK_log));
+ if (generate_new_name(new_name, name))
+ return; // Something went wrong
+ if (log_type == LOG_BIN)
+ {
+ /*
+ We log the whole file name for log file as the user may decide
+ to change base names at some point.
+ */
+ Rotate_log_event r(new_name+dirname_length(new_name));
+ r.write(file);
+ VOID(pthread_cond_broadcast(&COND_binlog_update));
+ }
+ name=0;
+ close();
+ open(old_name, log_type, new_name);
+ my_free(old_name,MYF(0));
+ if (!file) // Something got wrong
+ log_type=LOG_CLOSED;
+ last_time=query_start=0;
+ write_error=0;
+ VOID(pthread_mutex_unlock(&LOCK_log));
+ }
+}
+
+
+void MYSQL_LOG::write(enum enum_server_command command,
+ const char *format,...)
+{
+ if (name && (what_to_log & (1L << (uint) command)))
+ {
+ va_list args;
+ va_start(args,format);
+ VOID(pthread_mutex_lock(&LOCK_log));
+ if (log_type != LOG_CLOSED)
+ {
+ time_t skr;
+ ulong id;
+ THD *thd=current_thd;
+ int error=0;
+ if (thd)
+ { // Normal thread
+ if ((thd->options & OPTION_LOG_OFF) &&
+ (thd->master_access & PROCESS_ACL))
+ {
+ VOID(pthread_mutex_unlock(&LOCK_log));
+ return; // No logging
+ }
+ id=thd->thread_id;
+ if (thd->user_time || !(skr=thd->query_start()))
+ skr=time(NULL); // Connected
+ }
+ else
+ { // Log from connect handler
+ skr=time(NULL);
+ id=0;
+ }
+ if (skr != last_time)
+ {
+ last_time=skr;
+ struct tm tm_tmp;
+ struct tm *start;
+ localtime_r(&skr,&tm_tmp);
+ start=&tm_tmp;
+ if (fprintf(file,"%02d%02d%02d %2d:%02d:%02d\t",
+ start->tm_year % 100,
+ start->tm_mon+1,
+ start->tm_mday,
+ start->tm_hour,
+ start->tm_min,
+ start->tm_sec) < 0)
+ error=errno;
+ }
+ else if (fputs("\t\t",file) < 0)
+ error=errno;
+ if (fprintf(file,"%7ld %-10.10s",
+ id,command_name[(uint) command]) < 0)
+ error=errno;
+ if (format)
+ {
+ if (fputc(' ',file) < 0 || vfprintf(file,format,args) < 0)
+ error=errno;
+ }
+ if (fputc('\n',file) < 0)
+ error=errno;
+ if (fflush(file) < 0)
+ error=errno;
+ if (error && ! write_error)
+ {
+ write_error=1;
+ sql_print_error(ER(ER_ERROR_ON_WRITE),name,error);
+ }
+ }
+ va_end(args);
+ VOID(pthread_mutex_unlock(&LOCK_log));
+ }
+}
+
+/* Write to binary log in a format to be used for replication */
+
+void MYSQL_LOG::write(Query_log_event* event_info)
+{
+ if (name)
+ {
+ VOID(pthread_mutex_lock(&LOCK_log));
+ if(file)
+ {
+ THD *thd=event_info->thd;
+ if ((!(thd->options & OPTION_BIN_LOG) &&
+ thd->master_access & PROCESS_ACL) ||
+ !db_ok(event_info->db, binlog_do_db, binlog_ignore_db))
+ {
+ VOID(pthread_mutex_unlock(&LOCK_log));
+ return;
+ }
+
+ if (thd->last_insert_id_used)
+ {
+ Intvar_log_event e((uchar)LAST_INSERT_ID_EVENT, thd->last_insert_id);
+ if (e.write(file))
+ {
+ sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno);
+ goto err;
+ }
+ }
+
+ if (thd->insert_id_used)
+ {
+ Intvar_log_event e((uchar)INSERT_ID_EVENT, thd->last_insert_id);
+ if(e.write(file))
+ {
+ sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno);
+ goto err;
+ }
+ }
+
+ if(event_info->write(file))
+ {
+ sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno);
+ }
+ err:
+ VOID(pthread_cond_broadcast(&COND_binlog_update));
+
+ VOID(pthread_mutex_unlock(&LOCK_log));
+ }
+ }
+
+}
+
+void MYSQL_LOG::write(Load_log_event* event_info)
+{
+ if(name)
+ {
+ VOID(pthread_mutex_lock(&LOCK_log));
+ if(file)
+ {
+ THD *thd=event_info->thd;
+ if (!(thd->options & OPTION_BIN_LOG) &&
+ (thd->master_access & PROCESS_ACL))
+ {
+ VOID(pthread_mutex_unlock(&LOCK_log));
+ return;
+ }
+
+
+ if (event_info->write(file))
+ sql_print_error(ER(ER_ERROR_ON_WRITE), name, errno);
+ VOID(pthread_cond_broadcast(&COND_binlog_update));
+
+ VOID(pthread_mutex_unlock(&LOCK_log));
+ }
+ }
+}
+
+
+/* Write update log in a format suitable for incremental backup */
+
+void MYSQL_LOG::write(const char *query, uint query_length,
+ ulong time_for_query)
+{
+ if (name)
+ {
+ VOID(pthread_mutex_lock(&LOCK_log));
+ if (file)
+ { // Safety agains reopen
+ int error=0;
+ THD *thd=current_thd;
+ char buff[80],*end;
+ end=buff;
+ if (!(thd->options & OPTION_UPDATE_LOG) &&
+ (thd->master_access & PROCESS_ACL))
+ {
+ VOID(pthread_mutex_unlock(&LOCK_log));
+ return;
+ }
+ if (specialflag & SPECIAL_LONG_LOG_FORMAT)
+ {
+ time_t skr=time(NULL);
+ if (skr != last_time)
+ {
+ last_time=skr;
+ struct tm tm_tmp;
+ struct tm *start;
+ localtime_r(&skr,&tm_tmp);
+ start=&tm_tmp;
+ if (fprintf(file,"# Time: %02d%02d%02d %2d:%02d:%02d\n",
+ start->tm_year % 100,
+ start->tm_mon+1,
+ start->tm_mday,
+ start->tm_hour,
+ start->tm_min,
+ start->tm_sec) < 0)
+ error=errno;
+ }
+ if (fprintf(file, "# User@Host: %s [%s] @ %s [%s]\n",
+ thd->priv_user,
+ thd->user,
+ thd->host ? thd->host : "",
+ thd->ip ? thd->ip : "") < 0)
+ error=errno;;
+ }
+ if (time_for_query)
+ fprintf(file,"# Time: %lu\n",time_for_query);
+ if (thd->db && strcmp(thd->db,db))
+ { // Database changed
+ if (fprintf(file,"use %s;\n",thd->db) < 0)
+ error=errno;
+ strmov(db,thd->db);
+ }
+ if (thd->last_insert_id_used)
+ {
+ end=strmov(end,",last_insert_id=");
+ end=longlong10_to_str((longlong) thd->current_insert_id,end,-10);
+ }
+ // Save value if we do an insert.
+ if (thd->insert_id_used)
+ {
+ if (specialflag & SPECIAL_LONG_LOG_FORMAT)
+ {
+ end=strmov(end,",insert_id=");
+ end=longlong10_to_str((longlong) thd->last_insert_id,end,-10);
+ }
+ }
+ if (thd->query_start_used)
+ {
+ if (query_start != thd->query_start())
+ {
+ query_start=thd->query_start();
+ end=strmov(end,",timestamp=");
+ end=int10_to_str((long) query_start,end,10);
+ }
+ }
+ if (end != buff)
+ {
+ *end++=';';
+ *end++='\n';
+ *end=0;
+ if (fputs("SET ",file) < 0 || fputs(buff+1,file) < 0)
+ error=errno;
+ }
+ if (!query)
+ {
+ query="#adminstrator command";
+ query_length=21;
+ }
+ if (my_fwrite(file,(byte*) query,query_length,MYF(MY_NABP)) ||
+ fputc(';',file) < 0 || fputc('\n',file) < 0)
+ error=errno;
+ if (fflush(file) < 0)
+ error=errno;
+ if (error && ! write_error)
+ {
+ write_error=1;
+ sql_print_error(ER(ER_ERROR_ON_WRITE),name,error);
+ }
+ }
+ VOID(pthread_mutex_unlock(&LOCK_log));
+ }
+}
+
+
+
+void MYSQL_LOG::flush()
+{
+ if (file)
+ if (fflush(file) < 0 && ! write_error)
+ {
+ write_error=1;
+ sql_print_error(ER(ER_ERROR_ON_WRITE),name,errno);
+ }
+}
+
+
+void MYSQL_LOG::close(bool exiting)
+{ // One can't set log_type here!
+ if (file)
+ {
+ if (log_type == LOG_BIN)
+ {
+ Stop_log_event s;
+ s.write(file);
+ VOID(pthread_cond_broadcast(&COND_binlog_update));
+ }
+ if (my_fclose(file,MYF(0)) < 0 && ! write_error)
+ {
+ write_error=1;
+ sql_print_error(ER(ER_ERROR_ON_WRITE),name,errno);
+ }
+ file=0;
+ }
+ if (name)
+ {
+ my_free(name,MYF(0));
+ name=0;
+ }
+
+ if(exiting && index_file)
+ {
+ if (my_fclose(index_file,MYF(0)) < 0 && ! write_error)
+ {
+ write_error=1;
+ sql_print_error(ER(ER_ERROR_ON_WRITE),name,errno);
+ }
+ index_file=0;
+
+ }
+}
+
+
+ /* Check if a string is a valid number */
+ /* Output: TRUE -> number */
+
+static bool test_if_number(register const char *str,
+ long *res, bool allow_wildcards)
+{
+ reg2 int flag;
+ const char *start;
+ DBUG_ENTER("test_if_number");
+
+ flag=0; start=str;
+ while (*str++ == ' ') ;
+ if (*--str == '-' || *str == '+')
+ str++;
+ while (isdigit(*str) || (allow_wildcards &&
+ (*str == wild_many || *str == wild_one)))
+ {
+ flag=1;
+ str++;
+ }
+ if (*str == '.')
+ {
+ for (str++ ;
+ isdigit(*str) ||
+ (allow_wildcards && (*str == wild_many || *str == wild_one)) ;
+ str++, flag=1) ;
+ }
+ if (*str != 0 || flag == 0)
+ DBUG_RETURN(0);
+ if (res)
+ *res=atol(start);
+ DBUG_RETURN(1); /* Number ok */
+} /* test_if_number */
+
+
+void sql_print_error(const char *format,...)
+{
+ va_list args;
+ time_t skr;
+ struct tm tm_tmp;
+ struct tm *start;
+ va_start(args,format);
+ DBUG_ENTER("sql_print_error");
+
+ VOID(pthread_mutex_lock(&LOCK_error_log));
+#ifndef DBUG_OFF
+ {
+ char buff[1024];
+ vsprintf(buff,format,args);
+ DBUG_PRINT("error",("%s",buff));
+ }
+#endif
+ skr=time(NULL);
+ localtime_r(&skr,&tm_tmp);
+ start=&tm_tmp;
+ fprintf(stderr,"%02d%02d%02d %2d:%02d:%02d ",
+ start->tm_year % 100,
+ start->tm_mon+1,
+ start->tm_mday,
+ start->tm_hour,
+ start->tm_min,
+ start->tm_sec);
+ (void) vfprintf(stderr,format,args);
+ (void) fputc('\n',stderr);
+ fflush(stderr);
+ va_end(args);
+
+ VOID(pthread_mutex_unlock(&LOCK_error_log));
+ DBUG_VOID_RETURN;
+}
+
+
+
+void sql_perror(const char *message)
+{
+#ifdef HAVE_STRERROR
+ sql_print_error("%s: %s",message, strerror(errno));
+#else
+ perror(message);
+#endif
+}
diff --git a/sql/log_event.cc b/sql/log_event.cc
new file mode 100644
index 00000000000..eae8d7c1e88
--- /dev/null
+++ b/sql/log_event.cc
@@ -0,0 +1,685 @@
+/* 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 MYSQL_CLIENT
+#ifdef __GNUC__
+#pragma implementation // gcc: Class implementation
+#endif
+#include "mysql_priv.h"
+#endif /* MYSQL_CLIENT */
+
+#define LOG_EVENT_HEADER_LEN 9
+#define QUERY_HEADER_LEN (sizeof(uint) + sizeof(uint) + sizeof(uchar))
+#define LOAD_HEADER_LEN (sizeof(uint) + sizeof(uint) + \
+ + sizeof(uint) + 2 + sizeof(uint))
+#define EVENT_LEN_OFFSET 5
+#define EVENT_TYPE_OFFSET 4
+#define MAX_EVENT_LEN 4*1024*1024
+#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))
+
+static void pretty_print_char(FILE* file, int c)
+{
+ 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;
+ }
+ fputc( '\'', file);
+}
+
+int Query_log_event::write(FILE* file)
+{
+ return query ? Log_event::write(file) : -1;
+}
+
+int Log_event::write(FILE* file)
+{
+ if (write_header(file)
+ || write_data(file) || fflush(file)) return -1;
+ return 0;
+}
+
+int Log_event::write_header(FILE* file)
+{
+ char buf[LOG_EVENT_HEADER_LEN];
+ // make sure to change this when the header gets bigger
+ char* pos = buf;
+ int4store(pos, when); // timestamp
+ pos += 4;
+ *pos++ = get_type_code(); // event type code
+ int4store(pos, get_data_size() +
+ sizeof(time_t) // timestamp
+ + sizeof(char) // event code
+ + sizeof(uint) // event entry size
+ );
+ pos += 4;
+ return (my_fwrite(file, (byte*) buf, (uint) (pos - buf),
+ MYF(MY_NABP | MY_WME)));
+}
+
+#ifndef MYSQL_CLIENT
+
+int Log_event::read_log_event(FILE* file, String* packet)
+{
+ ulong data_len;
+ char buf[LOG_EVENT_HEADER_LEN];
+ if (my_fread(file, (byte*)buf, sizeof(buf), MYF(MY_NABP)))
+ return feof(file) ? LOG_READ_EOF: LOG_READ_IO;
+
+ data_len = uint4korr(buf + EVENT_LEN_OFFSET);
+ if (data_len < LOG_EVENT_HEADER_LEN || data_len > MAX_EVENT_LEN)
+ return LOG_READ_BOGUS;
+
+ packet->append(buf, sizeof(buf));
+ data_len -= LOG_EVENT_HEADER_LEN;
+ if (!data_len)
+ return 0; // the event does not have a data section
+ if (packet->append(file, data_len, MYF(MY_WME|MY_NABP)))
+ return feof(file) ? LOG_READ_BOGUS: LOG_READ_IO;
+ return 0;
+}
+
+#endif // MYSQL_CLIENT
+
+// allocates memory - the caller is responsible for clean-up
+
+Log_event* Log_event::read_log_event(FILE* file)
+{
+ time_t timestamp;
+ char buf[5];
+ if (my_fread(file, (byte *) buf, sizeof(buf), MY_NABP))
+ return NULL;
+ timestamp = uint4korr(buf);
+
+ switch(buf[EVENT_TYPE_OFFSET])
+ {
+ case QUERY_EVENT:
+ {
+ Query_log_event* q = new Query_log_event(file, timestamp);
+ if (!q->query)
+ {
+ delete q;
+ return NULL;
+ }
+
+ return q;
+ }
+
+ case LOAD_EVENT:
+ {
+ Load_log_event* l = new Load_log_event(file, timestamp);
+ if (!l->table_name)
+ {
+ delete l;
+ return NULL;
+ }
+
+ return l;
+ }
+
+
+ case ROTATE_EVENT:
+ {
+ Rotate_log_event* r = new Rotate_log_event(file, timestamp);
+ if (!r->new_log_ident)
+ {
+ delete r;
+ return NULL;
+ }
+
+ return r;
+ }
+
+ case INTVAR_EVENT:
+ {
+ Intvar_log_event* e = new Intvar_log_event(file, timestamp);
+ if (e->type == INVALID_INT_EVENT)
+ {
+ delete e;
+ return NULL;
+ }
+
+ return e;
+ }
+
+ case START_EVENT: return new Start_log_event(file, timestamp);
+ case STOP_EVENT: return new Stop_log_event(file, timestamp);
+ default: return NULL;
+ }
+
+ //impossible
+ return NULL;
+}
+
+Log_event* Log_event::read_log_event(const char* buf, int max_buf)
+{
+
+ switch(buf[EVENT_TYPE_OFFSET])
+ {
+ case QUERY_EVENT:
+ {
+ Query_log_event* q = new Query_log_event(buf, max_buf);
+ if (!q->query)
+ {
+ delete q;
+ return NULL;
+ }
+
+ return q;
+ }
+
+ case LOAD_EVENT:
+ {
+ Load_log_event* l = new Load_log_event(buf, max_buf);
+ if (!l->table_name)
+ {
+ delete l;
+ return NULL;
+ }
+
+ return l;
+ }
+
+ case ROTATE_EVENT:
+ {
+ Rotate_log_event* r = new Rotate_log_event(buf, max_buf);
+ 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);
+ default: return NULL;
+ }
+
+ //impossible
+ return NULL;
+}
+
+void Log_event::print_timestamp(FILE* file)
+{
+ struct tm tm_tmp;
+ localtime_r(&when,&tm_tmp);
+
+ fprintf(file,"#%02d%02d%02d %2d:%02d:%02d",
+ tm_tmp.tm_year % 100,
+ tm_tmp.tm_mon+1,
+ tm_tmp.tm_mday,
+ tm_tmp.tm_hour,
+ tm_tmp.tm_min,
+ tm_tmp.tm_sec);
+}
+
+
+void Start_log_event::print(FILE* file, bool short_form)
+{
+ if (short_form)
+ return;
+
+ print_timestamp(file);
+ fprintf(file, "\tStart\n");
+ fflush(file);
+}
+
+void Stop_log_event::print(FILE* file, bool short_form)
+{
+ if (short_form)
+ return;
+
+ print_timestamp(file);
+ fprintf(file, "\tStop\n");
+ fflush(file);
+}
+
+void Rotate_log_event::print(FILE* file, bool short_form)
+{
+ if (short_form)
+ return;
+
+ print_timestamp(file);
+ fprintf(file, "\tRotate to ");
+ if (new_log_ident)
+ my_fwrite(file, (byte*) new_log_ident, (uint)ident_len,
+ MYF(MY_NABP | MY_WME));
+ fprintf(file, "\n");
+ fflush(file);
+}
+
+Rotate_log_event::Rotate_log_event(FILE* file, time_t when_arg):
+ Log_event(when_arg),new_log_ident(NULL),alloced(0)
+{
+ char *tmp_ident;
+ char buf[4];
+
+ if (my_fread(file, (byte*) buf, sizeof(buf), MYF(MY_NABP | MY_WME)))
+ return;
+
+ ulong event_len;
+ event_len = uint4korr(buf);
+ if(event_len < ROTATE_EVENT_OVERHEAD)
+ return;
+
+ ident_len = (uchar)(event_len - ROTATE_EVENT_OVERHEAD);
+
+ if (!(tmp_ident = (char*) my_malloc((uint)ident_len, MYF(MY_WME))))
+ return;
+ if (my_fread( file, (byte*) tmp_ident, (uint)ident_len, MYF(MY_NABP | MY_WME)))
+ {
+ my_free((gptr) tmp_ident, MYF(0));
+ return;
+ }
+
+ new_log_ident = tmp_ident;
+ alloced = 1;
+}
+
+Rotate_log_event::Rotate_log_event(const char* buf, int max_buf):
+ Log_event(buf),new_log_ident(NULL),alloced(0)
+{
+ ulong event_len;
+ event_len = uint4korr(buf + EVENT_LEN_OFFSET);
+ if(event_len < ROTATE_EVENT_OVERHEAD || event_len > (ulong) max_buf)
+ 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))))
+ return;
+
+ alloced = 1;
+}
+
+int Rotate_log_event::write_data(FILE* file)
+{
+ if (my_fwrite(file, (byte*) new_log_ident, (uint) ident_len,
+ MYF(MY_NABP | MY_WME)))
+ return -1;
+ return 0;
+}
+
+Query_log_event::Query_log_event(FILE* file, time_t when_arg):
+ Log_event(when_arg),data_buf(0),query(NULL),db(NULL)
+{
+ char buf[QUERY_HEADER_LEN + 4];
+ ulong data_len;
+ if (my_fread(file, (byte*) buf, sizeof(buf), MYF(MY_NABP | MY_WME)))
+ 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];
+
+ if (!(data_buf = (char*) my_malloc(data_len+1, MYF(MY_WME))))
+ return;
+ if (my_fread( file, (byte*) data_buf, data_len, MYF(MY_NABP | MY_WME)))
+ {
+ 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;
+}
+
+Query_log_event::Query_log_event(const char* buf, int max_buf):
+ Log_event(buf),data_buf(0), query(NULL), db(NULL)
+{
+ ulong data_len;
+ buf += EVENT_LEN_OFFSET;
+ data_len = uint4korr(buf);
+ if (data_len < QUERY_EVENT_OVERHEAD || data_len > (ulong) max_buf)
+ return; // tear-drop attack protection :)
+
+ data_len -= QUERY_EVENT_OVERHEAD;
+ exec_time = uint4korr(buf + 8);
+
+ if (!(data_buf = (char*) my_malloc( data_len + 1, MYF(MY_WME))))
+ return;
+
+ memcpy(data_buf, buf + 13, data_len);
+ thread_id = uint4korr(buf + 4);
+ db = data_buf;
+ db_len = (uint)buf[12];
+ query=data_buf + db_len + 1;
+ q_len = data_len - 1 - db_len;
+ *((char*)query+q_len) = 0;
+}
+
+void Query_log_event::print(FILE* file, bool short_form)
+{
+ if (!short_form)
+ {
+ print_timestamp(file);
+ fprintf(file, "\tQuery\tthread_id=%lu\texec_time=%lu\n",
+ (ulong) thread_id, (ulong) exec_time);
+ }
+
+ if(db && db[0])
+ fprintf(file, "use %s;\n", db);
+ my_fwrite(file, (byte*) query, q_len, MYF(MY_NABP | MY_WME));
+ fprintf(file, ";\n");
+}
+
+int Query_log_event::write_data(FILE* file)
+{
+ 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;
+
+
+ if (my_fwrite(file, (byte*) buf, (uint)(pos - buf), MYF(MY_NABP | MY_WME)) ||
+ my_fwrite(file, (db) ? (byte*) db : (byte*)"",
+ db_len + 1, MYF(MY_NABP | MY_WME)) ||
+ my_fwrite(file, (byte*) query, q_len, MYF(MY_NABP | MY_WME)))
+ return -1;
+ return 0;
+}
+
+Intvar_log_event:: Intvar_log_event(FILE* file, time_t when_arg)
+ :Log_event(when_arg), type(INVALID_INT_EVENT)
+{
+ my_fseek(file, 4L, MY_SEEK_CUR, MYF(MY_WME)); // skip the event length
+ char buf[9];
+ if(my_fread(file, (byte*)buf, sizeof(buf), MYF(MY_NABP|MY_WME))) return;
+ type = buf[0];
+ val = uint8korr(buf+1);
+}
+
+Intvar_log_event::Intvar_log_event(const char* buf):Log_event(buf)
+{
+ buf += LOG_EVENT_HEADER_LEN;
+ type = buf[0];
+ val = uint8korr(buf+1);
+}
+
+int Intvar_log_event::write_data(FILE* file)
+{
+ char buf[9];
+ buf[0] = type;
+ int8store(buf + 1, val);
+ return my_fwrite(file, (byte*) buf, sizeof(buf), MYF(MY_NABP|MY_WME));
+}
+
+void Intvar_log_event::print(FILE* file, bool short_form)
+{
+ char llbuff[22];
+ if(!short_form)
+ {
+ print_timestamp(file);
+ fprintf(file, "\tIntvar\n");
+ }
+
+ fprintf(file, "SET ");
+ switch(type)
+ {
+ case LAST_INSERT_ID_EVENT:
+ fprintf(file, "LAST_INSERT_ID = ");
+ break;
+ case INSERT_ID_EVENT:
+ fprintf(file, "INSERT_ID = ");
+ break;
+ }
+ fprintf(file, "%s;\n", llstr(val,llbuff));
+ fflush(file);
+
+}
+
+int Load_log_event::write_data(FILE* file __attribute__((unused)))
+{
+ 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_fwrite(file, (byte*)buf, sizeof(buf), MYF(MY_NABP|MY_WME)) ||
+ my_fwrite(file, (byte*)&sql_ex, sizeof(sql_ex), MYF(MY_NABP|MY_WME)))
+ return 1;
+
+ if(num_fields && fields && field_lens)
+ {
+ if(my_fwrite(file, (byte*)field_lens, num_fields, MYF(MY_NABP|MY_WME)) ||
+ my_fwrite(file, (byte*)fields, field_block_len, MYF(MY_NABP|MY_WME)))
+ return 1;
+ }
+
+ if(my_fwrite(file, (byte*)table_name, table_name_len + 1, MYF(MY_NABP|MY_WME)) ||
+ my_fwrite(file, (byte*)db, db_len + 1, MYF(MY_NABP|MY_WME)) ||
+ my_fwrite(file, (byte*)fname, fname_len, MYF(MY_NABP|MY_WME)) )
+ return 1;
+
+
+ return 0;
+}
+
+Load_log_event::Load_log_event(FILE* file, time_t when):
+ Log_event(when),data_buf(0),num_fields(0),fields(0),field_lens(0),field_block_len(0),
+ table_name(0),db(0),fname(0)
+
+{
+ char buf[LOAD_HEADER_LEN + 4];
+ ulong data_len;
+ if(my_fread(file, (byte*)buf, sizeof(buf), MYF(MY_NABP|MY_WME)) ||
+ my_fread(file, (byte*)&sql_ex, sizeof(sql_ex), MYF(MY_NABP|MY_WME)))
+ return;
+
+ data_len = uint4korr(buf);
+ 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);
+
+ data_len -= LOAD_EVENT_OVERHEAD;
+ if(!(data_buf = (char*)my_malloc(data_len + 1, MYF(MY_WME))))
+ return;
+
+ if(my_fread(file, (byte*)data_buf, data_len, MYF(MY_NABP|MY_WME)))
+ return;
+
+ 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++)
+ {
+ 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;
+}
+
+Load_log_event::Load_log_event(const char* buf, int max_buf):
+ Log_event(when),data_buf(0),num_fields(0),fields(0),field_lens(0),field_block_len(0),
+ table_name(0),db(0),fname(0)
+
+{
+ ulong data_len;
+
+ if((uint)max_buf < (LOAD_EVENT_OVERHEAD + LOG_EVENT_HEADER_LEN))
+ return;
+
+ buf += EVENT_LEN_OFFSET;
+
+ data_len = uint4korr(buf);
+ if((uint)data_len > (uint)max_buf)
+ return;
+
+ 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);
+
+ data_len -= LOAD_EVENT_OVERHEAD;
+ memcpy(&sql_ex, buf + 22, sizeof(sql_ex));
+
+ if(!(data_buf = (char*)my_malloc(data_len + 1, MYF(MY_WME))))
+ return;
+
+ memcpy(data_buf, buf + 22 + sizeof(sql_ex), data_len);
+
+ 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++)
+ {
+ 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;
+
+}
+
+
+void Load_log_event::print(FILE* file, bool short_form)
+{
+ if (!short_form)
+ {
+ print_timestamp(file);
+ fprintf(file, "\tQuery\tthread_id=%d\texec_time=%ld\n",
+ thread_id, exec_time);
+ }
+
+ if(db && db[0])
+ fprintf(file, "use %s;\n", db);
+
+ fprintf(file, "LOAD DATA INFILE '%s' ", fname);
+
+ if(sql_ex.opt_flags && REPLACE_FLAG )
+ fprintf(file," REPLACE ");
+ 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))
+ {
+ fprintf(file, " FIELDS TERMINATED BY ");
+ pretty_print_char(file, sql_ex.field_term);
+ }
+
+ if(!(sql_ex.empty_flags & ENCLOSED_EMPTY))
+ {
+ if(sql_ex.opt_flags && OPT_ENCLOSED_FLAG )
+ fprintf(file," OPTIONALLY ");
+ fprintf(file, " ENCLOSED BY ");
+ pretty_print_char(file, sql_ex.enclosed);
+ }
+
+ if(!(sql_ex.empty_flags & ESCAPED_EMPTY))
+ {
+ fprintf(file, " ESCAPED BY ");
+ pretty_print_char(file, sql_ex.escaped);
+ }
+
+ if(!(sql_ex.empty_flags & LINE_TERM_EMPTY))
+ {
+ fprintf(file," LINES TERMINATED BY ");
+ pretty_print_char(file, sql_ex.line_term);
+ }
+
+ if(!(sql_ex.empty_flags & LINE_START_EMPTY))
+ {
+ fprintf(file," LINES STARTING BY ");
+ pretty_print_char(file, sql_ex.line_start);
+ }
+
+ if((int)skip_lines > 0)
+ fprintf(file, " IGNORE %d LINES ", skip_lines);
+
+ if(num_fields)
+ {
+ uint i;
+ const char* field = fields;
+ fprintf( file, " (");
+ for(i = 0; i < num_fields; i++)
+ {
+ if(i)
+ fputc(',', file);
+ fprintf(file, field);
+
+ field += field_lens[i] + 1;
+ }
+ fputc(')', file);
+ }
+
+ fprintf(file, ";\n");
+}
+
+#ifndef MYSQL_CLIENT
+
+void Load_log_event::set_fields(List<Item> &fields)
+{
+ uint i;
+ const char* field = this->fields;
+ for(i = 0; i < num_fields; i++)
+ {
+ fields.push_back(new Item_field(db, table_name, field));
+ field += field_lens[i] + 1;
+ }
+
+}
+
+#endif
diff --git a/sql/log_event.h b/sql/log_event.h
new file mode 100644
index 00000000000..fb585f01dea
--- /dev/null
+++ b/sql/log_event.h
@@ -0,0 +1,354 @@
+/* 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 _LOG_EVENT_H
+#define _LOG_EVENT_H
+
+#if defined(__GNUC__) && !defined(MYSQL_CLIENT)
+#pragma interface /* gcc class implementation */
+#endif
+
+#define LOG_READ_EOF -1
+#define LOG_READ_BOGUS -2
+#define LOG_READ_IO -3
+#define LOG_READ_MEM -5
+
+#define LOG_EVENT_OFFSET 4
+
+
+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
+ };
+
+#ifndef MYSQL_CLIENT
+class String;
+#endif
+
+class Log_event
+{
+public:
+ time_t when;
+ ulong exec_time;
+ int valid_exec_time; // if false, the exec time setting is bogus and needs
+
+ int write(FILE* file);
+ int write_header(FILE* file);
+ virtual int write_data(FILE* 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): when(when_arg),
+ exec_time(exec_time_arg), valid_exec_time(valid_exec_time_arg) {}
+
+ Log_event(const char* buf): valid_exec_time(1)
+ {
+ when = uint4korr(buf);
+ exec_time = uint4korr(buf + 5);
+ }
+
+ virtual ~Log_event() {}
+
+ virtual int get_data_size() { return 0;}
+ virtual void print(FILE* file, bool short_form = 0) = 0;
+
+ void print_timestamp(FILE* file);
+
+ static Log_event* read_log_event(FILE* file);
+ static Log_event* read_log_event(const char* buf, int max_buf);
+
+#ifndef MYSQL_CLIENT
+ static int read_log_event(FILE* file, String* packet);
+#endif
+
+};
+
+
+class Query_log_event: public Log_event
+{
+protected:
+ char* data_buf;
+public:
+ const char* query;
+ const char* db;
+ uint 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()
+ uint db_len;
+ int thread_id;
+#if !defined(MYSQL_CLIENT)
+ THD* thd;
+ Query_log_event(THD* thd_arg, const char* query_arg):
+ Log_event(thd_arg->start_time), data_buf(0),
+ query(query_arg), db(thd_arg->db), q_len(thd_arg->query_length),
+ thread_id(thd_arg->thread_id), thd(thd_arg)
+ {
+ time_t end_time;
+ time(&end_time);
+ exec_time = end_time - thd->start_time;
+ valid_exec_time = 1;
+ db_len = (db) ? strlen(db) : 0;
+ }
+#endif
+
+ Query_log_event(FILE* file, time_t when);
+ Query_log_event(const char* buf, int max_buf);
+ ~Query_log_event()
+ {
+ if (data_buf)
+ {
+ my_free((gptr)data_buf, MYF(0));
+ }
+ }
+ Log_event_type get_type_code() { return QUERY_EVENT; }
+ int write(FILE* file);
+ int write_data(FILE* file); // returns 0 on success, -1 on error
+ int get_data_size()
+ {
+ return q_len + db_len + 2 +
+ sizeof(uint) // thread_id
+ + sizeof(uint) // exec_time
+ ;
+ }
+
+ void print(FILE* file, bool short_form = 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
+
+
+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
+ } ;
+
+class Load_log_event: public Log_event
+{
+protected:
+ char* data_buf;
+public:
+ int thread_id;
+ uint table_name_len;
+ uint db_len;
+ uint fname_len;
+ uint num_fields;
+ const char* fields;
+ const uchar* field_lens;
+ uint field_block_len;
+
+
+ const char* table_name;
+ const char* db;
+ const char* fname;
+ uint skip_lines;
+ sql_ex_info sql_ex;
+
+#if !defined(MYSQL_CLIENT)
+ THD* thd;
+ String field_lens_buf;
+ String fields_buf;
+ Load_log_event(THD* thd, sql_exchange* ex, const char* table_name,
+ List<Item>& fields, 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),
+ db(thd->db),
+ fname(ex->file_name),
+ thd(thd)
+ {
+ time_t end_time;
+ time(&end_time);
+ exec_time = end_time - thd->start_time;
+ valid_exec_time = 1;
+ db_len = (db) ? strlen(db) : 0;
+ table_name_len = (table_name) ? strlen(table_name) : 0;
+ fname_len = (fname) ? 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);
+ 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();
+ this->fields = fields_buf.ptr();
+ }
+ void set_fields(List<Item> &fields);
+#endif
+
+ Load_log_event(FILE* file, time_t when);
+ Load_log_event(const char* buf, int max_buf);
+ ~Load_log_event()
+ {
+ if (data_buf)
+ {
+ my_free((gptr)data_buf, MYF(0));
+ }
+ }
+ Log_event_type get_type_code() { return LOAD_EVENT; }
+ int write_data(FILE* file); // returns 0 on success, -1 on error
+ int get_data_size()
+ {
+ return table_name_len + 2 + db_len + 2 + fname_len
+ + sizeof(thread_id) // thread_id
+ + sizeof(exec_time) // exec_time
+ + sizeof(skip_lines)
+ + sizeof(field_block_len)
+ + sizeof(sql_ex) + field_block_len + num_fields*sizeof(uchar) ;
+ ;
+ }
+
+ void print(FILE* file, bool short_form = 0);
+};
+
+
+class Start_log_event: public Log_event
+{
+public:
+ Start_log_event() :Log_event(time(NULL))
+ {}
+ Start_log_event(FILE* file, time_t when_arg) :Log_event(when_arg)
+ {
+ my_fseek(file, 4L, MY_SEEK_CUR, MYF(MY_WME)); // skip the event length
+ }
+ Start_log_event(const char* buf) :Log_event(buf)
+ {
+ }
+ ~Start_log_event() {}
+ Log_event_type get_type_code() { return START_EVENT;}
+ void print(FILE* file, bool short_form = 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)
+ {}
+ Intvar_log_event(FILE* file, time_t when);
+ Intvar_log_event(const char* buf);
+ ~Intvar_log_event() {}
+ Log_event_type get_type_code() { return INTVAR_EVENT;}
+ int get_data_size() { return sizeof(type) + sizeof(val);}
+ int write_data(FILE* file);
+
+
+ void print(FILE* file, bool short_form = 0);
+};
+
+class Stop_log_event: public Log_event
+{
+public:
+ Stop_log_event() :Log_event(time(NULL))
+ {}
+ Stop_log_event(FILE* file, time_t when_arg): Log_event(when_arg)
+ {
+ my_fseek(file, 4L, MY_SEEK_CUR, MYF(MY_WME)); // 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);
+};
+
+class Rotate_log_event: public Log_event
+{
+public:
+ const char* new_log_ident;
+ uchar 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 : strlen(new_log_ident_arg)),
+ alloced(0)
+ {}
+
+ Rotate_log_event(FILE* file, time_t when) ;
+ Rotate_log_event(const char* buf, int max_buf);
+ ~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 write_data(FILE* file);
+
+ void print(FILE* file, bool short_form = 0);
+};
+
+#endif
diff --git a/sql/matherr.c b/sql/matherr.c
new file mode 100644
index 00000000000..8523a78ce94
--- /dev/null
+++ b/sql/matherr.c
@@ -0,0 +1,43 @@
+/* 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 <errno.h>
+
+ /* Fix that we gets POSTFIX_ERROR when error in math */
+
+#if defined(HAVE_MATHERR)
+int matherr(struct exception *x)
+{
+ if (x->type != PLOSS)
+ x->retval=POSTFIX_ERROR;
+ switch (x->type) {
+ case DOMAIN:
+ case SING:
+ my_errno=EDOM;
+ break;
+ case OVERFLOW:
+ case UNDERFLOW:
+ my_errno=ERANGE;
+ break;
+ default:
+ break;
+ }
+ return(1); /* Take no other action */
+}
+#endif
diff --git a/sql/md5.c b/sql/md5.c
new file mode 100644
index 00000000000..0775ba3bd1a
--- /dev/null
+++ b/sql/md5.c
@@ -0,0 +1,351 @@
+/* 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 (context, input, inputLen)
+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 (digest, context)
+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 (state, block)
+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 (output, input, len)
+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 (output, input, len)
+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
new file mode 100644
index 00000000000..862129391f1
--- /dev/null
+++ b/sql/md5.h
@@ -0,0 +1,80 @@
+
+/* 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
new file mode 100644
index 00000000000..e5ecc4bb428
--- /dev/null
+++ b/sql/mf_iocache.cc
@@ -0,0 +1,641 @@
+/* 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 */
+
+/*
+ Cashing of files with only does (sequential) read or writes of fixed-
+ length records. A read isn't allowed to go over file-length. A read is ok
+ if it ends at file-length and next read can try to read after file-length
+ (and get a EOF-error).
+ Possibly use of asyncronic io.
+ macros for read and writes for faster io.
+ Used instead of FILE when reading or writing whole files.
+ This will make mf_rec_cache obsolete.
+ One can change info->pos_in_file to a higher value to skip bytes in file if
+ also info->rc_pos is set to info->rc_end.
+ If called through open_cached_file(), then the temporary file will
+ only be created if a write exeeds the file buffer or if one calls
+ flush_io_cache().
+*/
+
+#define MAP_TO_USE_RAID
+#include "mysql_priv.h"
+#include <mysys_err.h>
+#ifdef HAVE_AIOWAIT
+#include <errno.h>
+static void my_aiowait(my_aio_result *result);
+#endif
+
+ /* if cachesize == 0 then use default cachesize (from s-file) */
+ /* returns 0 if we have enough memory */
+
+extern "C" {
+
+
+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 */
+ 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;
+ info->pos_in_file= seek_offset;
+ info->read_length=info->buffer_length=cachesize;
+ info->seek_not_done=test(file >= 0); /* Seek not done */
+ info->myflags=cache_myflags & ~(MY_NABP | MY_FNABP);
+ info->rc_request_pos=info->rc_pos=info->buffer;
+
+ if (type == READ_CACHE || type == READ_NET) /* the same logic */
+ {
+ 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));
+ }
+ info->end_of_file=(type == READ_NET) ? 0 : MY_FILEPOS_ERROR; /* May be changed by user */
+ 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 (!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 (info->type == READ_CACHE && type == WRITE_CACHE)
+ info->rc_end=info->buffer+info->buffer_length;
+ 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 (info->type == WRITE_CACHE && type == READ_CACHE)
+ info->end_of_file=my_b_tell(info);
+ if (flush_io_cache(info))
+ DBUG_RETURN(1);
+ info->pos_in_file=seek_offset;
+ info->rc_request_pos=info->rc_pos=info->buffer;
+ if (type == READ_CACHE || type == READ_NET)
+ {
+ 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) ? 0 : MY_FILEPOS_ERROR;
+ }
+ }
+ 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 */
+ /* Returns 0 if record read */
+
+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;
+ memcpy(Buffer,info->rc_pos,
+ (size_t) (left_length=(uint) (info->rc_end-info->rc_pos)));
+ Buffer+=left_length;
+ Count-=left_length;
+ 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->end_of_file - pos_in_file;
+ if (max_length > info->read_length-diff_length)
+ max_length=info->read_length-diff_length;
+ 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
+ */
+
+int _my_b_net_read(register IO_CACHE *info, byte *Buffer,
+ uint Count __attribute__((unused)))
+{
+ int read_length;
+ NET *net= &(current_thd)->net;
+
+ if (info->end_of_file)
+ 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)
+ {
+ info->error=(uint) -1;
+ return 1;
+ }
+ if (read_length == 0)
+ {
+ /* End of file from client */
+ info->end_of_file = 1; 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 (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;
+}
+
+ /* Flush write cache */
+
+int flush_io_cache(IO_CACHE *info)
+{
+ uint length;
+ DBUG_ENTER("flush_io_cache");
+
+ 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 */
+ VOID(my_seek(info->file,info->pos_in_file,MY_SEEK_SET,MYF(0)));
+ 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 */
+
+}
diff --git a/sql/mini_client.cc b/sql/mini_client.cc
new file mode 100644
index 00000000000..49433bdf96a
--- /dev/null
+++ b/sql/mini_client.cc
@@ -0,0 +1,801 @@
+/* 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 */
+
+/*
+ mini MySQL client to be included into the server to do server to server
+ commincation by Sasha Pachev
+
+ 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__) || defined(WIN32)
+#include <winsock.h>
+#include <odbcinst.h>
+#endif
+#include <global.h>
+#include <my_sys.h>
+#include <mysys_err.h>
+#include <m_string.h>
+#include <m_ctype.h>
+#include "mysql.h"
+#include "mini_client.h"
+#include "mysql_version.h"
+#include "mysqld_error.h"
+#include "errmsg.h"
+#include <violite.h>
+#include <sys/stat.h>
+#include <signal.h>
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+#if !defined(MSDOS) && !defined(__WIN__)
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#ifdef HAVE_SELECT_H
+# include <select.h>
+#endif
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#endif
+#ifdef HAVE_SYS_UN_H
+# include <sys/un.h>
+#endif
+#if defined(THREAD) && !defined(__WIN__)
+#include <my_pthread.h> /* because of signal() */
+#endif
+#ifndef INADDR_NONE
+#define INADDR_NONE -1
+#endif
+
+
+static 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);
+
+
+#define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | CLIENT_LOCAL_FILES)
+
+#if defined(MSDOS) || defined(__WIN__)
+#define ERRNO WSAGetLastError()
+#define perror(A)
+#else
+#include <sys/errno.h>
+#define ERRNO errno
+#define SOCKET_ERROR -1
+#define closesocket(A) close(A)
+#endif
+
+#ifdef __WIN__
+static my_bool is_NT(void)
+{
+ char *os=getenv("OS");
+ return (os && !strcmp(os, "Windows_NT")) ? 1 : 0;
+}
+#endif
+
+/*
+** Create a named pipe connection
+*/
+
+#ifdef __WIN__
+
+HANDLE create_named_pipe(NET *net, uint connect_timeout, char **arg_host,
+ char **arg_unix_socket)
+{
+ HANDLE hPipe=INVALID_HANDLE_VALUE;
+ char szPipeName [ 257 ];
+ DWORD dwMode;
+ int i;
+ my_bool testing_named_pipes=0;
+ char *host= *arg_host, *unix_socket= *arg_unix_socket;
+
+ if (!host || !strcmp(host,LOCAL_HOST))
+ host=LOCAL_HOST_NAMEDPIPE;
+
+ sprintf( szPipeName, "\\\\%s\\pipe\\%s", host, unix_socket);
+ DBUG_PRINT("info",("Server name: '%s'. Named Pipe: %s",
+ host, unix_socket));
+
+ for (i=0 ; i < 100 ; i++) /* Don't retry forever */
+ {
+ if ((hPipe = CreateFile(szPipeName,
+ GENERIC_READ | GENERIC_WRITE,
+ 0,
+ NULL,
+ OPEN_EXISTING,
+ 0,
+ NULL )) != INVALID_HANDLE_VALUE)
+ break;
+ if (GetLastError() != ERROR_PIPE_BUSY)
+ {
+ net->last_errno=CR_NAMEDPIPEOPEN_ERROR;
+ sprintf(net->last_error,ER(net->last_errno),host, unix_socket,
+ (ulong) GetLastError());
+ return INVALID_HANDLE_VALUE;
+ }
+ /* wait for for an other instance */
+ if (! WaitNamedPipe(szPipeName, connect_timeout*1000) )
+ {
+ net->last_errno=CR_NAMEDPIPEWAIT_ERROR;
+ sprintf(net->last_error,ER(net->last_errno),host, unix_socket,
+ (ulong) GetLastError());
+ return INVALID_HANDLE_VALUE;
+ }
+ }
+ if (hPipe == INVALID_HANDLE_VALUE)
+ {
+ net->last_errno=CR_NAMEDPIPEOPEN_ERROR;
+ sprintf(net->last_error,ER(net->last_errno),host, unix_socket,
+ (ulong) GetLastError());
+ return INVALID_HANDLE_VALUE;
+ }
+ dwMode = PIPE_READMODE_BYTE | PIPE_WAIT;
+ if ( !SetNamedPipeHandleState(hPipe, &dwMode, NULL, NULL) )
+ {
+ CloseHandle( hPipe );
+ net->last_errno=CR_NAMEDPIPESETSTATE_ERROR;
+ sprintf(net->last_error,ER(net->last_errno),host, unix_socket,
+ (ulong) GetLastError());
+ return INVALID_HANDLE_VALUE;
+ }
+ *arg_host=host ; *arg_unix_socket=unix_socket; /* connect arg */
+ return (hPipe);
+}
+#endif
+
+
+/****************************************************************************
+** Init MySQL structure or allocate one
+****************************************************************************/
+
+MYSQL * STDCALL
+mc_mysql_init(MYSQL *mysql)
+{
+ init_client_errs();
+ if (!mysql)
+ {
+ if (!(mysql=(MYSQL*) my_malloc(sizeof(*mysql),MYF(MY_WME | MY_ZEROFILL))))
+ return 0;
+ mysql->free_me=1;
+ mysql->net.vio = 0;
+ }
+ else
+ bzero((char*) (mysql),sizeof(*(mysql)));
+#ifdef __WIN__
+ mysql->options.connect_timeout=20;
+#endif
+ return mysql;
+}
+
+/**************************************************************************
+** Shut down connection
+**************************************************************************/
+
+static void
+mc_end_server(MYSQL *mysql)
+{
+ DBUG_ENTER("mc_end_server");
+ if (mysql->net.vio != 0)
+ {
+ DBUG_PRINT("info",("Net: %s", vio_description(mysql->net.vio)));
+ vio_delete(mysql->net.vio);
+ mysql->net.vio= 0; /* Marker */
+ }
+ net_end(&mysql->net);
+ mc_free_old_query(mysql);
+ DBUG_VOID_RETURN;
+}
+
+static void mc_free_old_query(MYSQL *mysql)
+{
+ DBUG_ENTER("mc_free_old_query");
+ if (mysql->fields)
+ free_root(&mysql->field_alloc);
+ init_alloc_root(&mysql->field_alloc,8192); /* Assume rowlength < 8192 */
+ mysql->fields=0;
+ mysql->field_count=0; /* For API */
+ DBUG_VOID_RETURN;
+}
+
+
+/****************************************************************************
+* A modified version of connect(). mc_sock_connect() allows you to specify
+* a timeout value, in seconds, that we should wait until we
+* derermine we can't connect to a particular host. If timeout is 0,
+* mc_sock_connect() will behave exactly like connect().
+*
+* Base version coded by Steve Bernacki, Jr. <steve@navinet.net>
+*****************************************************************************/
+
+static int mc_sock_connect(File s, const struct sockaddr *name, uint namelen, uint to)
+{
+#if defined(__WIN__)
+ return connect(s, (struct sockaddr*) name, namelen);
+#else
+ int flags, res, s_err;
+ size_socket s_err_size = sizeof(uint);
+ fd_set sfds;
+ struct timeval tv;
+
+ /* If they passed us a timeout of zero, we should behave
+ * exactly like the normal connect() call does.
+ */
+
+ if (to == 0)
+ return connect(s, (struct sockaddr*) name, namelen);
+
+ flags = fcntl(s, F_GETFL, 0); /* Set socket to not block */
+#ifdef O_NONBLOCK
+ fcntl(s, F_SETFL, flags | O_NONBLOCK); /* and save the flags.. */
+#endif
+
+ res = connect(s, (struct sockaddr*) name, namelen);
+ s_err = errno; /* Save the error... */
+ fcntl(s, F_SETFL, flags);
+ if ((res != 0) && (s_err != EINPROGRESS))
+ {
+ errno = s_err; /* Restore it */
+ return(-1);
+ }
+ if (res == 0) /* Connected quickly! */
+ return(0);
+
+ /* Otherwise, our connection is "in progress." We can use
+ * the select() call to wait up to a specified period of time
+ * for the connection to suceed. If select() returns 0
+ * (after waiting howevermany seconds), our socket never became
+ * writable (host is probably unreachable.) Otherwise, if
+ * select() returns 1, then one of two conditions exist:
+ *
+ * 1. An error occured. We use getsockopt() to check for this.
+ * 2. The connection was set up sucessfully: getsockopt() will
+ * return 0 as an error.
+ *
+ * Thanks goes to Andrew Gierth <andrew@erlenstar.demon.co.uk>
+ * who posted this method of timing out a connect() in
+ * comp.unix.programmer on August 15th, 1997.
+ */
+
+ FD_ZERO(&sfds);
+ FD_SET(s, &sfds);
+ tv.tv_sec = (long) to;
+ tv.tv_usec = 0;
+#ifdef HPUX
+ res = select(s+1, NULL, (int*) &sfds, NULL, &tv);
+#else
+ res = select(s+1, NULL, &sfds, NULL, &tv);
+#endif
+ if (res <= 0) /* Never became writable */
+ return(-1);
+
+ /* select() returned something more interesting than zero, let's
+ * see if we have any errors. If the next two statements pass,
+ * we've got an open socket!
+ */
+
+ s_err=0;
+ if (getsockopt(s, SOL_SOCKET, SO_ERROR, (char*) &s_err, &s_err_size) != 0)
+ return(-1);
+
+ if (s_err)
+ { /* getsockopt() could suceed */
+ errno = s_err;
+ return(-1); /* but return an error... */
+ }
+ return(0); /* It's all good! */
+#endif
+}
+
+/*****************************************************************************
+** read a packet from server. Give error message if socket was down
+** or packet is an error message
+*****************************************************************************/
+
+uint STDCALL
+mc_net_safe_read(MYSQL *mysql)
+{
+ NET *net= &mysql->net;
+ uint len=0;
+
+ if (net->vio != 0)
+ len=my_net_read(net);
+
+ if (len == packet_error || len == 0)
+ {
+ DBUG_PRINT("error",("Wrong connection or packet. fd: %s len: %d",
+ vio_description(net->vio),len));
+ if(errno != EINTR)
+ {
+ mc_end_server(mysql);
+ net->last_errno=CR_SERVER_LOST;
+ strmov(net->last_error,ER(net->last_errno));
+ }
+ return(packet_error);
+ }
+ if (net->read_pos[0] == 255)
+ {
+ if (len > 3)
+ {
+ char *pos=(char*) net->read_pos+1;
+ if (mysql->protocol_version > 9)
+ { /* New client protocol */
+ net->last_errno=uint2korr(pos);
+ pos+=2;
+ len-=2;
+ if(!net->last_errno)
+ net->last_errno = CR_UNKNOWN_ERROR;
+ }
+ else
+ {
+ net->last_errno=CR_UNKNOWN_ERROR;
+ len--;
+ }
+ (void) strmake(net->last_error,(char*) pos,
+ min(len,sizeof(net->last_error)-1));
+ }
+ else
+ {
+ net->last_errno=CR_UNKNOWN_ERROR;
+ (void) strmov(net->last_error,ER(net->last_errno));
+ }
+ DBUG_PRINT("error",("Got error: %d (%s)", net->last_errno,
+ net->last_error));
+ return(packet_error);
+ }
+ return len;
+}
+
+
+char * STDCALL mc_mysql_error(MYSQL *mysql)
+{
+ return (mysql)->net.last_error;
+}
+
+my_bool STDCALL mc_mysql_reconnect(MYSQL *mysql)
+{
+ MYSQL tmp_mysql;
+ DBUG_ENTER("mc_mysql_reconnect");
+
+ 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))
+ DBUG_RETURN(1);
+ tmp_mysql.free_me=mysql->free_me;
+ mysql->free_me=0;
+ bzero((char*) &mysql->options,sizeof(&mysql->options));
+ mc_mysql_close(mysql);
+ *mysql=tmp_mysql;
+ net_clear(&mysql->net);
+ mysql->affected_rows= ~(my_ulonglong) 0;
+ DBUG_RETURN(0);
+}
+
+
+
+int STDCALL
+mc_simple_command(MYSQL *mysql,enum enum_server_command command, const char *arg,
+ uint length, my_bool skipp_check)
+{
+ NET *net= &mysql->net;
+ int result= -1;
+
+ 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)
+ {
+ strmov(net->last_error,ER(mysql->net.last_errno=CR_COMMANDS_OUT_OF_SYNC));
+ goto end;
+ }
+
+ mysql->net.last_error[0]=0;
+ mysql->net.last_errno=0;
+ mysql->info=0;
+ mysql->affected_rows= ~(my_ulonglong) 0;
+ net_clear(net); /* Clear receive buffer */
+ if (!arg)
+ arg="";
+
+ if (net_write_command(net,(uchar) command,arg,
+ length ? length :strlen(arg)))
+ {
+ DBUG_PRINT("error",("Can't send command to server. Error: %d",errno));
+ mc_end_server(mysql);
+ if (mc_mysql_reconnect(mysql) ||
+ net_write_command(net,(uchar) command,arg,
+ length ? length :strlen(arg)))
+ {
+ net->last_errno=CR_SERVER_GONE_ERROR;
+ strmov(net->last_error,ER(net->last_errno));
+ goto end;
+ }
+ }
+ result=0;
+ if (!skipp_check)
+ result= ((mysql->packet_length=mc_net_safe_read(mysql)) == packet_error ?
+ -1 : 0);
+ end:
+ return result;
+}
+
+
+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)
+{
+ char buff[100],*end,*host_info;
+ int sock;
+ ulong ip_addr;
+ struct sockaddr_in sock_addr;
+ uint pkt_length;
+ NET *net= &mysql->net;
+#ifdef __WIN__
+ HANDLE hPipe=INVALID_HANDLE_VALUE;
+#endif
+#ifdef HAVE_SYS_UN_H
+ struct sockaddr_un UNIXaddr;
+#endif
+ DBUG_ENTER("mysql_real_connect");
+
+ DBUG_PRINT("enter",("host: %s db: %s user: %s",
+ host ? host : "(Null)",
+ db ? db : "(Null)",
+ user ? user : "(Null)"));
+
+ bzero((char*) &mysql->options,sizeof(mysql->options));
+ net->vio = 0; /* If something goes wrong */
+ mysql->charset=default_charset_info; /* Set character set */
+ if (!port)
+ port = MYSQL_PORT; /* Should always be set by mysqld */
+ if (!unix_socket)
+ unix_socket=MYSQL_UNIX_ADDR;
+
+ mysql->reconnect=1; /* Reconnect as default */
+
+ /*
+ ** Grab a socket and connect it to the server
+ */
+
+#if defined(HAVE_SYS_UN_H)
+ if (!host || !strcmp(host,LOCAL_HOST))
+ {
+ host=LOCAL_HOST;
+ host_info=(char*) ER(CR_LOCALHOST_CONNECTION);
+ DBUG_PRINT("info",("Using UNIX sock '%s'",unix_socket));
+ if ((sock = socket(AF_UNIX,SOCK_STREAM,0)) == SOCKET_ERROR)
+ {
+ net->last_errno=CR_SOCKET_CREATE_ERROR;
+ sprintf(net->last_error,ER(net->last_errno),ERRNO);
+ goto error;
+ }
+ net->vio = vio_new(sock, VIO_TYPE_SOCKET, TRUE);
+ 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),
+ mysql->options.connect_timeout) <0)
+ {
+ DBUG_PRINT("error",("Got error %d on connect to local server",ERRNO));
+ net->last_errno=CR_CONNECTION_ERROR;
+ sprintf(net->last_error,ER(net->last_errno),unix_socket,ERRNO);
+ goto error;
+ }
+ }
+ else
+#elif defined(__WIN__)
+ {
+ if ((unix_socket ||
+ !host && is_NT() ||
+ host && !strcmp(host,LOCAL_HOST_NAMEDPIPE) ||
+ mysql->options.named_pipe || !have_tcpip))
+ {
+ sock=0;
+ if ((hPipe=create_named_pipe(net, mysql->options.connect_timeout,
+ (char**) &host, (char**) &unix_socket)) ==
+ INVALID_HANDLE_VALUE)
+ {
+ DBUG_PRINT("error",
+ ("host: '%s' socket: '%s' named_pipe: %d have_tcpip: %d",
+ host ? host : "<null>",
+ unix_socket ? unix_socket : "<null>",
+ (int) mysql->options.named_pipe,
+ (int) have_tcpip));
+ if (mysql->options.named_pipe ||
+ (host && !strcmp(host,LOCAL_HOST_NAMEDPIPE)) ||
+ (unix_socket && !strcmp(unix_socket,MYSQL_NAMEDPIPE)))
+ goto error; /* User only requested named pipes */
+ /* Try also with TCP/IP */
+ }
+ else
+ {
+ net->vio=vio_new_win32pipe(hPipe);
+ sprintf(host_info=buff, ER(CR_NAMEDPIPE_CONNECTION), host,
+ unix_socket);
+ }
+ }
+ }
+ if (hPipe == INVALID_HANDLE_VALUE)
+#endif
+ {
+ unix_socket=0; /* This is not used */
+ if (!host)
+ 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)
+ {
+ net->last_errno=CR_IPSOCK_ERROR;
+ sprintf(net->last_error,ER(net->last_errno),ERRNO);
+ goto error;
+ }
+ net->vio = vio_new(sock,VIO_TYPE_TCPIP,FALSE);
+ bzero((char*) &sock_addr,sizeof(sock_addr));
+ sock_addr.sin_family = AF_INET;
+
+ /*
+ ** The server name may be a host name or IP address
+ */
+
+ if ((int) (ip_addr = inet_addr(host)) != (int) INADDR_NONE)
+ {
+ memcpy_fixed(&sock_addr.sin_addr,&ip_addr,sizeof(ip_addr));
+ }
+ else
+#if defined(HAVE_GETHOSTBYNAME_R) && defined(_REENTRANT) && defined(THREAD)
+ {
+ int tmp_errno;
+ struct hostent tmp_hostent,*hp;
+ char buff2[GETHOSTBYNAME_BUFF_SIZE];
+ hp = my_gethostbyname_r(host,&tmp_hostent,buff2,sizeof(buff2),
+ &tmp_errno);
+ if (!hp)
+ {
+ net->last_errno=CR_UNKNOWN_HOST;
+ sprintf(net->last_error, ER(CR_UNKNOWN_HOST), host, tmp_errno);
+ goto error;
+ }
+ memcpy(&sock_addr.sin_addr,hp->h_addr, (size_t) hp->h_length);
+ }
+#else
+ {
+ struct hostent *hp;
+ if (!(hp=gethostbyname(host)))
+ {
+ net->last_errno=CR_UNKNOWN_HOST;
+ sprintf(net->last_error, ER(CR_UNKNOWN_HOST), host, errno);
+ goto error;
+ }
+ memcpy(&sock_addr.sin_addr,hp->h_addr, (size_t) hp->h_length);
+ }
+#endif
+ sock_addr.sin_port = (ushort) htons((ushort) port);
+ if (mc_sock_connect(sock,(struct sockaddr *) &sock_addr, sizeof(sock_addr),
+ mysql->options.connect_timeout) <0)
+ {
+ DBUG_PRINT("error",("Got error %d on connect to '%s'",ERRNO,host));
+ net->last_errno= CR_CONN_HOST_ERROR;
+ sprintf(net->last_error ,ER(CR_CONN_HOST_ERROR), host, ERRNO);
+ goto error;
+ }
+ }
+
+ if (!net->vio || my_net_init(net, net->vio))
+ {
+ vio_delete(net->vio);
+ net->last_errno=CR_OUT_OF_MEMORY;
+ strmov(net->last_error,ER(net->last_errno));
+ goto error;
+ }
+ vio_keepalive(net->vio,TRUE);
+
+ /* Get version info */
+ mysql->protocol_version= PROTOCOL_VERSION; /* Assume this */
+ if ((pkt_length=mc_net_safe_read(mysql)) == packet_error)
+ goto error;
+
+ /* Check if version of protocoll matches current one */
+
+ mysql->protocol_version= net->read_pos[0];
+ DBUG_DUMP("packet",(char*) net->read_pos,10);
+ DBUG_PRINT("info",("mysql protocol version %d, server=%d",
+ PROTOCOL_VERSION, mysql->protocol_version));
+ if (mysql->protocol_version != PROTOCOL_VERSION &&
+ mysql->protocol_version != PROTOCOL_VERSION-1)
+ {
+ net->last_errno= CR_VERSION_ERROR;
+ sprintf(net->last_error, ER(CR_VERSION_ERROR), mysql->protocol_version,
+ PROTOCOL_VERSION);
+ goto error;
+ }
+ end=strend((char*) net->read_pos+1);
+ mysql->thread_id=uint4korr(end+1);
+ end+=5;
+ strmake(mysql->scramble_buff,end,8);
+ if (pkt_length > (uint) (end+9 - (char*) net->read_pos))
+ mysql->server_capabilities=uint2korr(end+9);
+
+ /* Save connection information */
+ if (!user) user="";
+ if (!passwd) passwd="";
+ if (!my_multi_malloc(MYF(0),
+ &mysql->host_info, (uint) strlen(host_info)+1,
+ &mysql->host, (uint) strlen(host)+1,
+ &mysql->unix_socket,
+ unix_socket ? (uint) strlen(unix_socket)+1 : (uint) 1,
+ &mysql->server_version,
+ (uint) (end - (char*) net->read_pos),
+ NullS) ||
+ !(mysql->user=my_strdup(user,MYF(0))) ||
+ !(mysql->passwd=my_strdup(passwd,MYF(0))))
+ {
+ strmov(net->last_error, ER(net->last_errno=CR_OUT_OF_MEMORY));
+ goto error;
+ }
+ strmov(mysql->host_info,host_info);
+ strmov(mysql->host,host);
+ if (unix_socket)
+ strmov(mysql->unix_socket,unix_socket);
+ else
+ mysql->unix_socket=0;
+ strmov(mysql->server_version,(char*) net->read_pos+1);
+ mysql->port=port;
+ mysql->client_flag=client_flag | mysql->options.client_flag;
+ DBUG_PRINT("info",("Server version = '%s' capabilites: %ld",
+ mysql->server_version,mysql->server_capabilities));
+
+ /* Send client information for access check */
+ client_flag|=CLIENT_CAPABILITIES;
+
+#ifdef HAVE_OPENSSL
+ if (mysql->options.use_ssl)
+ client_flag|=CLIENT_SSL;
+#endif /* HAVE_OPENSSL */
+
+ if (db)
+ client_flag|=CLIENT_CONNECT_WITH_DB;
+#ifdef HAVE_COMPRESS
+ if (mysql->server_capabilities & CLIENT_COMPRESS &&
+ (mysql->options.compress || client_flag & CLIENT_COMPRESS))
+ client_flag|=CLIENT_COMPRESS; /* We will use compression */
+ else
+#endif
+ client_flag&= ~CLIENT_COMPRESS;
+
+#ifdef HAVE_OPENSSL
+ if ((mysql->server_capabilities & CLIENT_SSL) &&
+ (mysql->options.use_ssl || (client_flag & CLIENT_SSL)))
+ {
+ DBUG_PRINT("info", ("Changing IO layer to SSL"));
+ client_flag |= CLIENT_SSL;
+ }
+ else
+ {
+ if (client_flag & CLIENT_SSL)
+ {
+ DBUG_PRINT("info", ("Leaving IO layer intact because server doesn't support SSL"));
+ }
+ client_flag &= ~CLIENT_SSL;
+ }
+#endif /* HAVE_OPENSSL */
+
+ int2store(buff,client_flag);
+ mysql->client_flag=client_flag;
+
+#ifdef HAVE_OPENSSL
+ /* 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))
+ 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);
+ }
+#endif /* HAVE_OPENSSL */
+
+ 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 );
+ }
+
+ DBUG_PRINT("info",("user: %s",buff+5));
+ end=scramble(strend(buff+5)+1, mysql->scramble_buff, passwd,
+ (my_bool) (mysql->protocol_version == 9));
+ if (db)
+ {
+ end=strmov(end+1,db);
+ mysql->db=my_strdup(db,MYF(MY_WME));
+ }
+ if (my_net_write(net,buff,(uint) (end-buff)) || net_flush(net) ||
+ mc_net_safe_read(mysql) == packet_error)
+ goto error;
+ if (client_flag & CLIENT_COMPRESS) /* We will use compression */
+ net->compress=1;
+ DBUG_PRINT("exit",("Mysql handler: %lx",mysql));
+ DBUG_RETURN(mysql);
+
+error:
+ DBUG_PRINT("error",("message: %u (%s)",net->last_errno,net->last_error));
+ {
+ /* Free alloced memory */
+ my_bool free_me=mysql->free_me;
+ mc_end_server(mysql);
+ mysql->free_me=0;
+ mc_mysql_close(mysql);
+ mysql->free_me=free_me;
+ }
+ DBUG_RETURN(0);
+}
+
+/*************************************************************************
+** Send a QUIT to the server and close the connection
+** If handle is alloced by mysql connect free it.
+*************************************************************************/
+
+void STDCALL
+mc_mysql_close(MYSQL *mysql)
+{
+ DBUG_ENTER("mysql_close");
+ if (mysql) /* Some simple safety */
+ {
+ if (mysql->net.vio != 0)
+ {
+ mc_free_old_query(mysql);
+ mysql->status=MYSQL_STATUS_READY; /* Force command */
+ mc_simple_command(mysql,COM_QUIT,NullS,0,1);
+ mc_end_server(mysql);
+ }
+ my_free((gptr) mysql->host_info,MYF(MY_ALLOW_ZERO_PTR));
+ my_free(mysql->user,MYF(MY_ALLOW_ZERO_PTR));
+ my_free(mysql->passwd,MYF(MY_ALLOW_ZERO_PTR));
+ my_free(mysql->db,MYF(MY_ALLOW_ZERO_PTR));
+ /* Clear pointers for better safety */
+ mysql->host_info=mysql->user=mysql->passwd=mysql->db=0;
+ bzero((char*) &mysql->options,sizeof(mysql->options));
+ mysql->net.vio = 0;
+#ifdef HAVE_OPENSSL
+ ((VioConnectorFd*)(mysql->connector_fd))->delete();
+ mysql->connector_fd = 0;
+#endif /* HAVE_OPENSSL */
+ if (mysql->free_me)
+ my_free((gptr) mysql,MYF(0));
+ }
+ DBUG_VOID_RETURN;
+}
diff --git a/sql/mini_client.h b/sql/mini_client.h
new file mode 100644
index 00000000000..1103bd379fe
--- /dev/null
+++ b/sql/mini_client.h
@@ -0,0 +1,45 @@
+/* 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 _MINI_CLIENT_H
+#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);
+my_bool STDCALL mc_mysql_reconnect(MYSQL* mysql);
+
+
+#endif
diff --git a/sql/my_lock.c b/sql/my_lock.c
new file mode 100644
index 00000000000..6c35634203f
--- /dev/null
+++ b/sql/my_lock.c
@@ -0,0 +1,82 @@
+/* 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__
+#include "../mysys/my_lock.c"
+#else
+
+#undef MAP_TO_USE_RAID /* Avoid RAID mappings */
+#include <global.h>
+#include <my_sys.h>
+#include <mysys_err.h>
+#include <my_pthread.h>
+#include <thr_alarm.h>
+#include <errno.h>
+
+#ifdef HAVE_FCNTL
+static struct flock lock; /* Must be static for sun-sparc */
+#endif
+
+ /* Lock a part of a file */
+
+int my_lock(File fd,int locktype,my_off_t start,my_off_t length,myf MyFlags)
+{
+ thr_alarm_t alarmed;
+ ALARM alarm_buff;
+ uint wait_for_alarm;
+ DBUG_ENTER("my_lock");
+ DBUG_PRINT("my",("Fd: %d Op: %d start: %ld Length: %ld MyFlags: %d",
+ fd,locktype,(ulong) start,(ulong) length,MyFlags));
+ if (my_disable_locking)
+ DBUG_RETURN(0); /* purecov: inspected */
+ lock.l_type=(short) locktype;
+ lock.l_whence=0L;
+ lock.l_start=(long) start;
+ lock.l_len=(long) length;
+ wait_for_alarm=(MyFlags & MY_DONT_WAIT ? MY_HOW_OFTEN_TO_ALARM :
+ (uint) 12*60*60);
+ if (fcntl(fd,F_SETLK,&lock) != -1) /* Check if we can lock */
+ DBUG_RETURN(0); /* Ok, file locked */
+ DBUG_PRINT("info",("Was locked, trying with alarm"));
+ if (!thr_alarm(&alarmed,wait_for_alarm,&alarm_buff))
+ {
+ int value;
+ while ((value=fcntl(fd,F_SETLKW,&lock)) && !thr_got_alarm(alarmed) &&
+ errno == EINTR) ;
+ thr_end_alarm(&alarmed);
+ if (value != -1)
+ DBUG_RETURN(0);
+ }
+ else
+ {
+ errno=EINTR;
+ }
+ if (errno == EINTR || errno == EACCES)
+ my_errno=EAGAIN; /* Easier to check for this */
+ else
+ my_errno=errno;
+
+ if (MyFlags & MY_WME)
+ {
+ if (locktype == F_UNLCK)
+ my_error(EE_CANTUNLOCK,MYF(ME_BELL+ME_WAITTANG),errno);
+ else
+ my_error(EE_CANTLOCK,MYF(ME_BELL+ME_WAITTANG),errno);
+ }
+ DBUG_PRINT("error",("errno: %d",errno));
+ DBUG_RETURN(-1);
+}
+#endif
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
new file mode 100644
index 00000000000..0963b4dd03a
--- /dev/null
+++ b/sql/mysql_priv.h
@@ -0,0 +1,594 @@
+/* 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 _MYSQL_PRIV_H
+#define _MYSQL_PRIV_H
+
+#include <global.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 <violite.h>
+
+typedef ulong table_map; /* Used for table bits in join */
+typedef ulong key_map; /* Used for finding keys */
+typedef ulong key_part_map; /* Used for finding key parts */
+
+#include "mysql_com.h"
+#include "unireg.h"
+
+void init_sql_alloc(MEM_ROOT *root,uint block_size);
+gptr sql_alloc(unsigned size);
+gptr sql_calloc(unsigned size);
+char *sql_strdup(const char *str);
+char *sql_strmake(const char *str,uint len);
+gptr sql_memdup(const void * ptr,unsigned size);
+void sql_element_free(void *ptr);
+
+#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
+****************************************************************************/
+
+#define ACL_CACHE_SIZE 256
+#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.
+#ifndef MYSQLD_NET_RETRY_COUNT
+#define MYSQLD_NET_RETRY_COUNT 10 // Abort read after this many int.
+#endif
+/* The following parameters is to decide when to use an extra cache to
+ 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
+
+// The following is used to decide if MySQL should use table scanning
+// instead of reading with keys. The number says how many evaluation of the
+// WHERE clause is comparable to reading one extra row from a table.
+#define TIME_FOR_COMPARE 5 // 5 compares == one read
+
+/* Don't pack string keys shorter than this (if PACK_KEYS=1 isn't used) */
+#define KEY_DEFAULT_PACK_LENGTH 8
+
+/* Characters shown for the command in 'show processlist' */
+#define PROCESS_LIST_WIDTH 100
+
+/* Time handling defaults */
+#define TIMESTAMP_MAX_YEAR 2038
+#define YY_PART_YEAR 70
+#define PRECISION_FOR_DOUBLE 53
+#define PRECISION_FOR_FLOAT 24
+
+/* The following can also be changed from the command line */
+#define CONNECT_TIMEOUT 5 // Do not wait long for connect
+#define DEFAULT_CONCURRENCY 10
+#define DELAYED_LIMIT 100 /* pause after xxx inserts */
+#define DELAYED_QUEUE_SIZE 1000
+#define DELAYED_WAIT_TIMEOUT 5*60 /* Wait for delayed insert */
+#define FLUSH_TIME 0 /* Don't flush tables */
+#define MAX_CONNECT_ERRORS 10 // errors before disabling host
+
+#ifdef __WIN__
+#define IF_WIN(A,B) (A)
+#undef FLUSH_TIME
+#define FLUSH_TIME 1800 /* Flush every half hour */
+
+#define INTERRUPT_PRIOR -2
+#define CONNECT_PRIOR -1
+#define WAIT_PRIOR 0
+#define QUERY_PRIOR 2
+#else
+#define IF_WIN(A,B) (B)
+#define INTERRUPT_PRIOR 10
+#define CONNECT_PRIOR 9
+#define WAIT_PRIOR 8
+#define QUERY_PRIOR 6
+#endif /* __WIN92__ */
+
+ /* Bits fro testflag */
+#define TEST_PRINT_CACHED_TABLES 1
+#define TEST_NO_KEY_GROUP 2
+#define TEST_MIT_THREAD 4
+#define TEST_BLOCKING 8
+#define TEST_KEEP_TMP_TABLES 16
+#define TEST_NO_THREADS 32 /* For debugging under Linux */
+#define TEST_READCHECK 64 /* Force use of readcheck */
+#define TEST_NO_EXTRA 128
+#define TEST_KILL_ON_DEBUG 256 /* Kill server */
+
+/* options for select set by the yacc parser */
+#define SELECT_DISTINCT 1
+#define SELECT_STRAIGHT_JOIN 2
+#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_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 OPTION_WARNINGS 16384
+#define OPTION_AUTO_IS_NULL 32768
+#define OPTION_ANSI_MODE 65536L
+#define OPTION_SAFE_UPDATES OPTION_ANSI_MODE*2
+#define OPTION_BUFFER_RESULT OPTION_SAFE_UPDATES*2
+#define OPTION_BIN_LOG OPTION_BUFFER_RESULT*2
+#define OPTION_AUTO_COMMIT OPTION_BIN_LOG*2
+#define OPTION_BEGIN OPTION_AUTO_COMMIT*2
+
+#define RAID_BLOCK_SIZE 1024
+
+/* BINLOG_DUMP options */
+
+#define BINLOG_DUMP_NON_BLOCK 1
+
+/* Some portable defines */
+
+#define portable_sizeof_char_ptr 8
+
+#define tmp_file_prefix "#sql" /* Prefix for tmp tables */
+#define tmp_file_prefix_length 4
+
+struct st_table;
+class THD;
+
+/* Struct to handle simple linked lists */
+
+typedef struct st_sql_list {
+ uint elements;
+ byte *first;
+ byte **next;
+} SQL_LIST;
+
+
+uint nr_of_decimals(const char *str); /* Neaded by sql_string.h */
+
+extern pthread_key(THD*, THR_THD);
+inline THD *_current_thd(void)
+{
+ return my_pthread_getspecific_ptr(THD*,THR_THD);
+}
+#define current_thd _current_thd()
+
+#include "sql_string.h"
+#include "sql_list.h"
+#include "sql_map.h"
+#include "handler.h"
+#include "table.h"
+#include "field.h" /* Field definitions */
+#include "sql_udf.h"
+#include "item.h"
+#include "sql_class.h"
+#include "opt_range.h"
+
+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 show_binlog_info(THD* thd);
+
+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
+
+
+void mysql_create_db(THD *thd, char *db, uint create_info);
+void mysql_binlog_send(THD* thd, char* log_ident, ulong pos, ushort flags);
+int mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists);
+int quick_rm_table(enum db_type base,const char *db,
+ const char *table_name);
+bool mysql_change_db(THD *thd,const char *name);
+void mysql_parse(THD *thd,char *inBuf,uint length);
+pthread_handler_decl(handle_one_connection,arg);
+int handle_bootstrap(THD *thd,FILE *file);
+sig_handler end_thread_signal(int sig);
+void end_thread(THD *thd,bool put_in_cache);
+void flush_thread_cache();
+void mysql_execute_command(void);
+bool do_command(THD *thd);
+bool check_stack_overrun(THD *thd,char *dummy);
+bool reload_acl_and_cache(uint options);
+void mysql_rm_db(THD *thd,char *db,bool if_exists);
+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);
+
+int mysql_check_table(THD* thd, TABLE_LIST* table_list,
+ HA_CHECK_OPT* check_opt);
+int mysql_repair_table(THD* thd, TABLE_LIST* table_list,
+ HA_CHECK_OPT* check_opt);
+int mysql_analyze_table(THD* thd, TABLE_LIST* table_list);
+int mysql_optimize_table(THD* thd, TABLE_LIST* table_list);
+
+
+/* net_pkg.c */
+void send_error(NET *net,uint sql_errno=0, 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,
+ const char *info=0);
+void send_eof(NET *net,bool no_flush=0);
+char *net_store_length(char *packet,ulonglong length);
+char *net_store_length(char *packet,uint length);
+char *net_store_data(char *to,const char *from);
+char *net_store_data(char *to,int32 from);
+char *net_store_data(char *to,longlong from);
+bool net_store_null(String *packet);
+bool net_store_data(String *packet,uint32 from);
+bool net_store_data(String *packet,longlong from);
+bool net_store_data(String *packet,const char *from);
+bool net_store_data(String *packet,const char *from,uint length);
+bool net_store_data(String *packet,struct tm *tmp);
+bool net_store_data(String* packet, I_List<i_string>* str_list);
+
+int mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &list,COND *conds,
+ List<Item_func_match> &ftfuncs,
+ 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,
+ 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,
+ List<Key> *keys,
+ List<Item> *items,
+ MYSQL_LOCK **lock);
+int mysql_alter_table(THD *thd, char *new_db, char *new_name,
+ HA_CREATE_INFO *create_info,
+ TABLE_LIST *table_list,
+ List<create_field> &fields,
+ List<Key> &keys,List<Alter_drop> &drop_list,
+ List<Alter_column> &alter_list,
+ bool drop_primary,
+ enum enum_duplicates handle_duplicates);
+bool close_cached_table(THD *thd,TABLE *table);
+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);
+int mysql_insert(THD *thd,TABLE_LIST *table,List<Item> &fields,
+ List<List_item> &values, enum_duplicates flag,
+ thr_lock_type lock_type);
+void kill_delayed_threads(void);
+int mysql_delete(THD *thd,TABLE_LIST *table,COND *conds,ha_rows rows,
+ thr_lock_type lock_type);
+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);
+TABLE *find_locked_table(THD *thd, const char *db,const char *table_name);
+bool reopen_table(TABLE *table,bool locked=0);
+bool reopen_tables(THD *thd,bool get_locks,bool in_refresh);
+void close_old_data_files(THD *thd, TABLE *table, bool abort_locks);
+bool close_data_tables(THD *thd,const char *db, const char *table_name);
+bool wait_for_tables(THD *thd);
+bool drop_locked_tables(THD *thd,const char *db, const char *table_name);
+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);
+
+/* sql_list.c */
+int mysqld_show_dbs(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);
+int mysqld_show_keys(THD *thd, TABLE_LIST *table);
+void mysqld_list_fields(THD *thd,TABLE_LIST *table, const char *wild);
+int mysqld_dump_create_info(THD *thd, TABLE *table, int fd = -1);
+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);
+
+/* sql_base.cc */
+void set_item_name(Item *item,char *pos,uint length);
+bool add_field_to_list(char *field_name, enum enum_field_types type,
+ char *length, char *decimal,
+ uint type_modifier, Item *default_value,char *change,
+ TYPELIB *interval);
+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,
+ thr_lock_type flags=TL_UNLOCK,
+ List<String> *use_index=0,
+ List<String> *ignore_index=0);
+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);
+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);
+int setup_fields(THD *thd,TABLE_LIST *tables,List<Item> &item,
+ bool set_query_id,List<Item> *sum_func_list);
+int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds);
+int setup_ftfuncs(THD *thd,TABLE_LIST *tables, List<Item_func_match> &ftfuncs);
+void wait_for_refresh(THD *thd);
+int open_tables(THD *thd,TABLE_LIST *tables);
+int open_and_lock_tables(THD *thd,TABLE_LIST *tables);
+int lock_tables(THD *thd,TABLE_LIST *tables);
+TABLE *open_temporary_table(THD *thd, const char *path, const char *db,
+ const char *table_name, bool link_in_list);
+bool rm_temporary_table(enum db_type base, char *path);
+bool send_fields(THD *thd,List<Item> &item,uint send_field_count);
+void free_io_cache(TABLE *entry);
+void intern_close_table(TABLE *entry);
+void close_thread_tables(THD *thd,bool locked=0);
+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);
+void close_temporary(TABLE *table, bool delete_table=1);
+bool rename_temporary_table(TABLE *table, const char *new_db,
+ const char *table_name);
+void remove_db_from_cache(const my_string db);
+void flush_tables();
+bool remove_table_from_cache(THD *thd, const char *db, const char *table);
+bool close_cached_tables(bool wait_for_refresh);
+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);
+
+/* sql_calc.cc */
+bool eval_const_cond(COND *cond);
+
+/* sql_load.cc */
+int mysql_load(THD *thd,sql_exchange *ex, TABLE_LIST *table_list,
+ List<Item> &fields, enum enum_duplicates handle_duplicates,
+ bool local_file,thr_lock_type lock_type);
+int write_record(TABLE *table,COPY_INFO *info);
+/* 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);
+#endif
+void mysql_print_status(THD *thd);
+/* key.cc */
+int find_ref_key(TABLE *form,Field *field, uint *offset);
+void key_copy(byte *key,TABLE *form,uint index,uint key_length);
+void key_restore(TABLE *form,byte *key,uint index,uint key_length);
+int key_cmp(TABLE *form,const byte *key,uint index,uint key_length);
+void key_unpack(String *to,TABLE *form,uint index);
+bool check_if_key_used(TABLE *table, uint idx, List<Item> &fields);
+void init_errmessage(void);
+
+void sql_perror(const char *message);
+void sql_print_error(const char *format,...)
+ __attribute__ ((format (printf, 1, 2)));
+
+extern char mysql_data_home[2],server_version[50],max_sort_char,
+ mysql_real_data_home[];
+extern my_string mysql_unix_port,mysql_tmpdir;
+extern const char *first_keyword;
+extern ulong refresh_version,flush_version, thread_id,query_id,opened_tables,
+ created_tmp_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 uint test_flags,select_errors,mysql_port,ha_open_options;
+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 MYSQL_LOG mysql_log,mysql_update_log,mysql_slow_log,mysql_bin_log;
+extern pthread_key(MEM_ROOT*,THR_MALLOC);
+extern pthread_key(NET*, THR_NET);
+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;
+extern pthread_cond_t COND_refresh,COND_thread_count, COND_binlog_update,
+ COND_slave_stopped;
+extern pthread_attr_t connection_attrib;
+extern bool opt_endinfo,using_udf_functions;
+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 char f_fyllchar;
+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,
+ long_query_count,net_wait_timeout,net_interactive_timeout,
+ net_read_timeout,net_write_timeout,
+ what_to_log,flush_time,
+ max_tmp_tables,max_heap_table_size,query_buff_size,
+ lower_case_table_names,thread_stack,thread_stack_min;
+extern ulong specialflag;
+extern ulong current_pid;
+extern bool low_priority_updates;
+extern bool opt_sql_bin_update;
+extern char language[LIBLEN],reg_ext[FN_EXTLEN],blob_newline;
+extern const char **errmesg; /* Error messages */
+extern byte last_ref[MAX_REFLENGTH]; /* Index ref of keys */
+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;
+
+#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;
+
+MYSQL_LOCK *mysql_lock_tables(THD *thd,TABLE **table,uint count);
+void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock);
+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);
+MYSQL_LOCK *mysql_lock_merge(MYSQL_LOCK *a,MYSQL_LOCK *b);
+
+extern int flush_master_info(MASTER_INFO* mi);
+
+/* old unireg functions */
+
+void unireg_init(ulong options);
+void unireg_end(int signal);
+int rea_create_table(my_string file_name,HA_CREATE_INFO *create_info,
+ List<create_field> &create_field,
+ uint key_count,KEY *key_info);
+int format_number(uint inputflag,uint max_length,my_string pos,uint length,
+ my_string *errpos);
+int openfrm(const char *name,const char *alias,uint filestat,uint prgflag,
+ TABLE *outparam);
+int closefrm(TABLE *table);
+db_type get_table_type(const char *name);
+int read_string(File file, gptr *to, uint length);
+void free_blobs(TABLE *table);
+int set_zone(int nr,int min_zone,int max_zone);
+ulong convert_period_to_month(ulong period);
+ulong convert_month_to_period(ulong month);
+long calc_daynr(uint year,uint month,uint day);
+uint calc_days_in_year(uint year);
+void get_date_from_daynr(long daynr,uint *year, uint *month,
+ uint *day);
+void init_time(void);
+long my_gmt_sec(TIME *);
+time_t str_to_timestamp(const char *str,uint length);
+bool str_to_time(const char *str,uint length,TIME *l_time);
+longlong str_to_datetime(const char *str,uint length,bool fuzzy_date);
+timestamp_type str_to_TIME(const char *str, uint length, TIME *l_time,
+ bool fuzzy_date);
+
+int test_if_number(char *str,int *res,bool allow_wildcards);
+void change_byte(byte *,uint,char,char);
+void unireg_abort(int exit_code);
+void init_read_record(READ_RECORD *info, THD *thd, TABLE *reg_form,
+ SQL_SELECT *select,
+ bool 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,
+ SQL_SELECT *select, ha_rows special,ha_rows max_rows);
+void change_double_for_sort(double nr,byte *to);
+int get_quick_record(SQL_SELECT *select);
+int calc_weekday(long daynr,bool sunday_first_day_of_week);
+uint calc_week(TIME *ltime, bool with_year, bool sunday_first_day_of_week,
+ uint *year);
+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(void);
+ulong get_form_pos(File file, uchar *head, TYPELIB *save_names);
+ulong make_new_entry(File file,uchar *fileinfo,TYPELIB *formnames,
+ const char *newname);
+ulong next_io_size(ulong pos);
+void append_unescaped(String *res,const char *pos);
+int create_frm(char *name,uint reclength,uchar *fileinfo,
+ HA_CREATE_INFO *create_info, uint keys);
+void update_create_info_from_table(HA_CREATE_INFO *info, TABLE *form);
+int rename_file_ext(const char * from,const char * to,const char * ext);
+bool check_db_name(const char *db);
+bool check_column_name(const char *name);
+bool check_table_name(const char *name, uint length);
+char *get_field(MEM_ROOT *mem,TABLE *table,uint fieldnr);
+int wild_case_compare(const char *str,const char *wildstr);
+/* from hostname.cc */
+struct in_addr;
+my_string ip_to_hostname(struct in_addr *in,uint *errors);
+void inc_host_errors(struct in_addr *in);
+void reset_host_errors(struct in_addr *in);
+bool hostname_cache_init();
+void hostname_cache_free();
+void hostname_cache_refresh(void);
+bool get_interval_info(const char *str,uint length,uint count,
+ long *values);
+/* sql_cache */
+
+extern bool sql_cache_init();
+extern void sql_cache_free();
+extern int sql_cache_hit(THD *thd, char *inBuf, uint length);
+
+/* Some inline functions for more speed */
+
+inline bool add_item_to_list(Item *item)
+{
+ return current_lex->item_list.push_back(item);
+}
+inline bool add_value_to_list(Item *value)
+{
+ return current_lex->value_list.push_back(value);
+}
+inline bool add_order_to_list(Item *item,bool asc)
+{
+ return add_to_list(current_lex->order_list,item,asc);
+}
+inline bool add_group_to_list(Item *item,bool asc)
+{
+ return add_to_list(current_lex->group_list,item,asc);
+}
+inline void mark_as_null_row(TABLE *table)
+{
+ table->null_row=1;
+ bfill(table->null_flags,table->null_bytes,255);
+}
+
+#endif
diff --git a/sql/mysqlbinlog.cc b/sql/mysqlbinlog.cc
new file mode 100644
index 00000000000..c088dc13fd3
--- /dev/null
+++ b/sql/mysqlbinlog.cc
@@ -0,0 +1,378 @@
+/* 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 MYSQL_CLIENT
+#undef MYSQL_SERVER
+#include <global.h>
+#include <m_string.h>
+#include <my_sys.h>
+#include <getopt.h>
+#include <thr_alarm.h>
+#include "log_event.h"
+#define MYSQL_SERVER // We want the C++ version of net
+#include <mysql.h>
+#include "mini_client.h"
+
+#define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | CLIENT_LOCAL_FILES)
+
+
+// needed by net_serv.c
+ulong bytes_sent = 0L, bytes_received = 0L;
+ulong mysqld_net_retry_count = 10L;
+ulong net_read_timeout= NET_READ_TIMEOUT;
+ulong net_write_timeout= NET_WRITE_TIMEOUT;
+uint test_flags = 0;
+
+#ifndef DBUG_OFF
+static const char* default_dbug_option = "d:t:o,/tmp/mysqlbinlog.trace";
+#endif
+
+static struct option long_options[] =
+{
+ {"short-form", no_argument, 0, 's'},
+ {"table", required_argument, 0, 't'},
+ {"offset", required_argument,0, 'o'},
+ {"help", no_argument, 0, '?'},
+ {"host", required_argument,0, 'h'},
+ {"port", required_argument,0, 'P'},
+ {"user", required_argument,0, 'u'},
+ {"password", required_argument,0, 'p'},
+ {"position", required_argument,0, 'j'},
+#ifndef DBUG_OFF
+ {"debug", required_argument, 0, '#'}
+#endif
+};
+
+void sql_print_error(const char *format,...);
+
+static bool short_form = 0;
+static int offset = 0;
+static const char* host = "localhost";
+static int port = MYSQL_PORT;
+static const char* user = "test";
+static const char* pass = "";
+static long position = 0;
+static bool use_remote = 0;
+static short binlog_flags = 0;
+static MYSQL* mysql = NULL;
+static const char* table = 0;
+
+static void dump_local_log_entries(const char* logname);
+static void dump_remote_log_entries(const char* logname);
+static void dump_log_entries(const char* logname);
+static void dump_remote_file(NET* net, const char* fname);
+static void dump_remote_table(NET* net, const char* db, const char* table);
+static void die(const char* fmt, ...);
+static MYSQL* safe_connect();
+
+ void sql_print_error(const char *format,...)
+ {
+ va_list args;
+ va_start(args, format);
+ fprintf(stderr, "ERROR: ");
+ vfprintf(stderr, format, args);
+ fprintf(stderr, "\n");
+ va_end(args);
+ }
+
+static void die(const char* fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ fprintf(stderr, "ERROR: ");
+ vfprintf(stderr, fmt, args);
+ fprintf(stderr, "\n");
+ va_end(args);
+ exit(1);
+}
+
+static void usage()
+{
+ printf("Usage: %s [options] log-files\n",my_progname);
+ printf("Options:\n\
+-s,--short-form just show the queries, no extra info\n\
+-o,--offset=N skip the first N entries\n\
+-h,--host=server get the binlog from server\n\
+-P,--port=port use port to connect to the remove server\n\
+-u,--user=username connect to the remove server as username\n\
+-p,--password=password use this password to connect to remote server\n\
+-j,--position=N start reading the binlog at postion N\n\
+-t,--table=name get raw table dump using COM_TABLE_DUMB \n\
+-?,--help this message\n");
+}
+
+static void dump_remote_file(NET* net, const char* fname)
+{
+ char buf[FN_REFLEN+1];
+ uint len = strlen(fname);
+ buf[0] = 0;
+ memcpy(buf + 1, fname, len + 1);
+ if(my_net_write(net, buf, len +2) || net_flush(net))
+ die("Failed requesting the remote dump of %s", fname);
+ for(;;)
+ {
+ uint packet_len = my_net_read(net);
+ if(packet_len == 0)
+ {
+ if(my_net_write(net, "", 0) || net_flush(net))
+ die("Failed sending the ack packet");
+
+ // we just need to send something, as the server will read but
+ // not examine the packet - this is because mysql_load() sends an OK when it is done
+ break;
+ }
+ else if(packet_len == packet_error)
+ die("Failed reading a packet during the dump of %s ", fname);
+
+ if(!short_form)
+ (void)my_fwrite(stdout, (byte*) net->read_pos, packet_len, MYF(0));
+ }
+
+ fflush(stdout);
+}
+
+static int parse_args(int *argc, char*** argv)
+{
+ int c, opt_index = 0;
+
+ while((c = getopt_long(*argc, *argv, "so:#:h:j:u:p:P:t:?", long_options,
+ &opt_index)) != EOF)
+ {
+ switch(c)
+ {
+#ifndef DBUG_OFF
+ case '#':
+ DBUG_PUSH(optarg ? optarg : default_dbug_option);
+ break;
+#endif
+ case 's':
+ short_form = 1;
+ break;
+
+ case 'o':
+ offset = atoi(optarg);
+ break;
+
+ case 'j':
+ position = atoi(optarg);
+ break;
+
+ case 'h':
+ use_remote = 1;
+ host = my_strdup(optarg, MYF(0));
+ break;
+
+ case 'P':
+ use_remote = 1;
+ port = atoi(optarg);
+ break;
+
+ case 'p':
+ use_remote = 1;
+ pass = my_strdup(optarg, MYF(0));
+ break;
+
+ case 'u':
+ use_remote = 1;
+ user = my_strdup(optarg, MYF(0));
+ break;
+
+ case 't':
+ table = my_strdup(optarg, MYF(0));
+ break;
+
+ case '?':
+ default:
+ usage();
+ exit(0);
+
+ }
+ }
+
+ (*argc)-=optind;
+ (*argv)+=optind;
+
+
+ return 0;
+}
+
+static MYSQL* safe_connect()
+{
+ MYSQL *local_mysql = mc_mysql_init(NULL);
+ if(!local_mysql)
+ die("Failed on mc_mysql_init");
+
+ if(!mc_mysql_connect(local_mysql, host, user, pass, 0, port, 0, 0))
+ die("failed on connect: %s", mc_mysql_error(local_mysql));
+
+ return local_mysql;
+}
+
+static void dump_log_entries(const char* logname)
+{
+ if(use_remote)
+ dump_remote_log_entries(logname);
+ else
+ dump_local_log_entries(logname);
+}
+
+static void dump_remote_table(NET* net, const char* db, const char* table)
+{
+ char buf[1024];
+ char * p = buf;
+ uint table_len = strlen(table);
+ uint db_len = strlen(db);
+ if(table_len + db_len > sizeof(buf) - 2)
+ die("Buffer overrun");
+
+ *p++ = db_len;
+ memcpy(p, db, db_len);
+ p += db_len;
+ *p++ = table_len;
+ memcpy(p, table, table_len);
+
+ if(mc_simple_command(mysql, COM_TABLE_DUMP, buf, p - buf + table_len, 1))
+ die("Error sending the table dump command");
+
+ for(;;)
+ {
+ uint packet_len = my_net_read(net);
+ if(packet_len == 0) break; // end of file
+ if(packet_len == packet_error)
+ die("Error reading packet in table dump");
+ my_fwrite(stdout, (byte*)net->read_pos, packet_len, MYF(MY_WME));
+ fflush(stdout);
+ }
+}
+
+
+static void dump_remote_log_entries(const char* logname)
+{
+ char buf[128];
+ uint len;
+ NET* net = &mysql->net;
+ int4store(buf, position);
+ int2store(buf + 4, binlog_flags);
+ len = strlen(logname);
+ memcpy(buf + 6, logname,len);
+ if(mc_simple_command(mysql, COM_BINLOG_DUMP, buf, len + 6, 1))
+ die("Error sending the log dump command");
+
+ for(;;)
+ {
+ len = mc_net_safe_read(mysql);
+ if (len == packet_error)
+ die("Error reading packet from server: %s", mc_mysql_error(mysql));
+ if(len == 1 && net->read_pos[0] == 254)
+ break; // end of data
+ DBUG_PRINT("info",( "len= %u, net->read_pos[5] = %d\n",
+ len, net->read_pos[5]));
+ Log_event * ev = Log_event::read_log_event((const char*) net->read_pos + 1 , len);
+ if(ev)
+ {
+ ev->print(stdout, short_form);
+ if(ev->get_type_code() == LOAD_EVENT)
+ dump_remote_file(net, ((Load_log_event*)ev)->fname);
+ delete ev;
+ }
+ else
+ die("Could not construct log event object");
+ }
+}
+
+static void dump_local_log_entries(const char* logname)
+{
+ FILE* file;
+ int rec_count = 0;
+
+ if(logname && logname[0] != '-')
+ file = my_fopen(logname, O_RDONLY, MYF(MY_WME));
+ else
+ file = stdin;
+
+ if(!file)
+ die("Could not open log file %s", logname);
+
+ if(my_fseek(file, position, MY_SEEK_SET, MYF(MY_WME)))
+ die("failed on my_fseek()");
+
+ while(1)
+ {
+ Log_event* ev = Log_event::read_log_event(file);
+ if(!ev)
+ if(!feof(file))
+ die("Could not read entry at offset %ld : Error in log format or \
+read error",
+ my_ftell(file, MYF(MY_WME)));
+ else
+ break;
+
+ if(rec_count >= offset)
+ ev->print(stdout, short_form);
+ rec_count++;
+ delete ev;
+ }
+
+ my_fclose(file, MYF(MY_WME));
+}
+
+int main(int argc, char** argv)
+{
+ MY_INIT(argv[0]);
+ parse_args(&argc, (char***)&argv);
+
+ if(!argc && !table)
+ {
+ usage();
+ return -1;
+ }
+
+ if(use_remote)
+ {
+ init_thr_alarm(10); // need to do this manually
+ mysql = safe_connect();
+ }
+
+ if(table)
+ {
+ if(!use_remote)
+ die("You must specify connection parameter to get table dump");
+ char* db = (char*)table;
+ char* tbl = (char*) strchr(table, '.');
+ if(!tbl)
+ die("You must use database.table syntax to specify the table");
+ *tbl++ = 0;
+ dump_remote_table(&mysql->net, db, tbl);
+ }
+ else
+ while(--argc >= 0)
+ {
+ dump_log_entries(*(argv++));
+ }
+
+ if(use_remote)
+ mc_mysql_close(mysql);
+
+ return 0;
+}
+
+/*
+ We must include this here as it's compiled with different options for
+ the server
+*/
+
+#include "log_event.cc"
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
new file mode 100644
index 00000000000..179c7ecd9dc
--- /dev/null
+++ b/sql/mysqld.cc
@@ -0,0 +1,3433 @@
+/* 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 <mysql.h>
+#include <m_ctype.h>
+#include "sql_acl.h"
+#ifdef HAVE_BERKELEY_DB
+#include "ha_berkeley.h"
+#endif
+#include "ha_myisam.h"
+#include <nisam.h>
+#include <thr_alarm.h>
+#include <ft_global.h>
+
+#ifdef __cplusplus
+extern "C" { // Because of SCO 3.2V4.2
+#endif
+#include <errno.h>
+#include <sys/stat.h>
+#ifndef __GNU_LIBRARY__
+#define __GNU_LIBRARY__ // Skipp warnings in getopt.h
+#endif
+#include <getopt.h>
+#ifdef HAVE_SYSENT_H
+#include <sysent.h>
+#endif
+#ifdef HAVE_PWD_H
+#include <pwd.h> // For getpwent
+#endif
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+
+#ifndef __WIN__
+#include <sys/resource.h>
+#ifdef HAVE_SYS_UN_H
+# include <sys/un.h>
+#endif
+#include <netdb.h>
+#ifdef HAVE_SELECT_H
+# include <select.h>
+#endif
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#include <sys/utsname.h>
+#else
+#include <windows.h>
+#endif // __WIN__
+
+#ifdef HAVE_LIBWRAP
+#include <tcpd.h>
+#include <syslog.h>
+#ifdef NEED_SYS_SYSLOG_H
+#include <sys/syslog.h>
+#endif /* NEED_SYS_SYSLOG_H */
+int allow_severity = LOG_INFO;
+int deny_severity = LOG_WARNING;
+#endif /* HAVE_LIBWRAP */
+
+#if defined(__FreeBSD__) && defined(HAVE_IEEEFP_H)
+#include <ieeefp.h>
+#ifdef HAVE_FP_EXCEPT // Fix type conflict
+typedef fp_except fp_except_t;
+#endif
+
+ /* We can't handle floating point expections with threads, so disable
+ this on freebsd
+ */
+
+inline void reset_floating_point_exceptions()
+{
+ /* Don't fall for overflow, underflow,divide-by-zero or loss of precision */
+ fpsetmask(~(FP_X_INV | FP_X_DNML | FP_X_OFL | FP_X_UFL |
+ FP_X_DZ | FP_X_IMP));
+}
+#else
+#define reset_floating_point_exceptions()
+#endif /* __FreeBSD__ && HAVE_IEEEFP_H */
+
+#ifdef __cplusplus
+}
+#endif
+
+#if defined(HAVE_LINUXTHREADS)
+#define THR_KILL_SIGNAL SIGINT
+#else
+#define THR_KILL_SIGNAL SIGUSR2 // Can't use this with LinuxThreads
+#endif
+
+#ifdef HAVE_GLIBC2_STYLE_GETHOSTBYNAME_R
+#include <sys/types.h>
+#else
+#include <my_pthread.h> // For thr_setconcurency()
+#endif
+#if defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE) && !defined(__linux__) && !defined(HAVE_mit_thread)
+#define SET_RLIMIT_NOFILE
+#endif
+
+#ifdef SOLARIS
+extern "C" int gethostname(char *name, int namelen);
+#endif
+
+#define MYSQL_KILL_SIGNAL SIGTERM
+
+#ifndef DBUG_OFF
+static const char* default_dbug_option=IF_WIN("d:t:i:O,\\mysqld.trace",
+ "d:t:i:o,/tmp/mysqld.trace");
+#endif
+
+#ifdef __NT__
+static char szPipeName [ 257 ];
+static SECURITY_ATTRIBUTES saPipeSecurity;
+static SECURITY_DESCRIPTOR sdPipeDescriptor;
+static HANDLE hPipe = INVALID_HANDLE_VALUE;
+static pthread_cond_t COND_handler_count;
+static uint handler_count;
+#endif
+#ifdef __WIN__
+static bool opt_console=0;
+#endif
+
+static ulong opt_specialflag=SPECIAL_ENGLISH;
+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;
+my_string opt_bin_logname = 0; // this one needs to be seen in sql_parse.cc
+static char mysql_home[FN_REFLEN],pidfile_name[FN_REFLEN];
+static pthread_t select_thread;
+static pthread_t flush_thread; // Used when debugging
+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_ansi_mode;
+bool opt_sql_bin_update = 0, opt_log_slave_updates = 0;
+
+// 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;
+static struct rand_struct sql_rand;
+static int cleanup_done;
+static char **defaults_argv,time_zone[30];
+static const char *default_table_type_name;
+
+#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;
+#endif /* HAVE_OPENSSL */
+
+extern bool slave_running;
+
+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;
+
+uint mysql_port;
+uint test_flags, 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;
+ulong thd_startup_options=(OPTION_UPDATE_LOG | OPTION_AUTO_IS_NULL |
+ OPTION_BIN_LOG | OPTION_AUTO_COMMIT);
+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;
+ulong thread_cache_size=0;
+volatile ulong cached_thread_count=0;
+
+// replication parameters, if master_host is not NULL, we are slaving off the master
+my_string master_user = (char*) "test", master_password = 0, master_host=0,
+ master_info_file = (char*) "master.info";
+uint master_port = MYSQL_PORT, master_connect_retry = 60;
+
+ulong max_tmp_tables,max_heap_table_size;
+ulong bytes_sent = 0L, bytes_received = 0L;
+
+bool opt_endinfo,using_udf_functions,low_priority_updates;
+bool volatile abort_loop,select_thread_in_use,flush_thread_in_use,grant_option;
+bool volatile ready_to_exit,shutdown_in_progress;
+ulong refresh_version=1L,flush_version=1L; /* Increments on each reload */
+ulong query_id=1L,long_query_count,long_query_time,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;
+ulong specialflag=0,opened_tables=0,created_tmp_tables=0;
+ulong max_connections,max_insert_delayed_threads,max_used_connections,
+ max_connect_errors;
+ulong thread_id=1L,current_pid;
+ulong slow_launch_threads = 0;
+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,
+ blob_newline,f_fyllchar,max_sort_char,*mysqld_user,*mysqld_chroot,
+ *opt_init_file;
+char server_version[50]=MYSQL_SERVER_VERSION;
+const char *first_keyword="first";
+const char **errmesg; /* Error messages */
+byte last_ref[MAX_REFLENGTH]; /* Index ref of keys */
+my_string mysql_unix_port=NULL,mysql_tmpdir=NULL;
+ulong my_bind_addr; /* the address we bind to */
+DATE_FORMAT dayord;
+double log_10[32]; /* 10 potences */
+I_List<THD> threads,thread_cache;
+time_t start_time;
+
+pthread_key(MEM_ROOT*,THR_MALLOC);
+pthread_key(THD*, THR_THD);
+pthread_key(NET*, THR_NET);
+pthread_mutex_t LOCK_mysql_create_db, LOCK_Acl, LOCK_open, LOCK_thread_count,
+ LOCK_mapped_file, LOCK_status, LOCK_grant,
+ LOCK_error_log,
+ LOCK_delayed_insert, LOCK_delayed_status, LOCK_delayed_create,
+ LOCK_flush, LOCK_crypt, LOCK_bytes_sent, LOCK_bytes_received,
+ LOCK_binlog_update, LOCK_slave;
+
+pthread_cond_t COND_refresh,COND_thread_count,COND_flush, COND_binlog_update,
+ COND_slave_stopped;
+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>
+HANDLE hEventShutdown;
+#include "nt_servc.h"
+static NTService Service; // Service object for WinNT
+#endif
+
+static void *signal_hand(void *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 int bootstrap(FILE *file);
+static bool read_init_file(char *file_name);
+#ifdef __NT__
+static pthread_handler_decl(handle_connections_namedpipes,arg);
+#endif
+#ifdef __WIN__
+static int get_service_parameters();
+#endif
+static pthread_handler_decl(handle_flush,arg);
+extern pthread_handler_decl(handle_slave,arg);
+#ifdef SET_RLIMIT_NOFILE
+static uint set_maximum_open_files(uint max_file_limit);
+#endif
+
+
+/****************************************************************************
+** Code to end mysqld
+****************************************************************************/
+
+static void close_connections(void)
+{
+#ifdef EXTRA_DEBUG
+ int count=0;
+#endif
+ NET net;
+ DBUG_ENTER("close_connections");
+
+ /* Clear thread cache */
+ kill_cached_threads++;
+ flush_thread_cache();
+
+ /* kill flush thread */
+ (void) pthread_mutex_lock(&LOCK_flush);
+ if (flush_thread_in_use)
+ {
+ DBUG_PRINT("quit",("killing flush thread: %lx",flush_thread));
+ (void) pthread_cond_signal(&COND_flush);
+ }
+ (void) pthread_mutex_unlock(&LOCK_flush);
+
+ /* kill connection thread */
+#if !defined(__WIN__) && !defined(__EMX__)
+ DBUG_PRINT("quit",("waiting for select thread: %lx",select_thread));
+ (void) pthread_mutex_lock(&LOCK_thread_count);
+
+ while (select_thread_in_use)
+ {
+ struct timespec abstime;
+ int error;
+ LINT_INIT(error);
+#ifndef DONT_USE_THR_ALARM
+ 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
+ for (uint tmp=0 ; tmp < 10 ; tmp++)
+ {
+ error=pthread_cond_timedwait(&COND_thread_count,&LOCK_thread_count,
+ &abstime);
+ if (error != EINTR)
+ break;
+ }
+#ifdef EXTRA_DEBUG
+ if (error != 0 && !count++)
+ sql_print_error("Got error %d from pthread_cond_timedwait",error);
+#endif
+#if defined(AIX_3_2) || defined(HAVE_DEC_3_2_THREADS)
+ if (ip_sock != INVALID_SOCKET)
+ {
+ VOID(shutdown(ip_sock,2));
+ VOID(closesocket(ip_sock));
+ VOID(shutdown(unix_sock,2));
+ VOID(closesocket(unix_sock));
+ VOID(unlink(mysql_unix_port));
+ ip_sock=unix_sock= INVALID_SOCKET;
+ }
+#endif
+ }
+ (void) pthread_mutex_unlock(&LOCK_thread_count);
+#endif /* __WIN__ */
+
+
+ /* Abort listening to new connections */
+ DBUG_PRINT("quit",("Closing sockets"));
+ if ( !opt_disable_networking )
+ {
+ if (ip_sock != INVALID_SOCKET)
+ {
+ (void) shutdown(ip_sock,2);
+ (void) closesocket(ip_sock);
+ ip_sock= INVALID_SOCKET;
+ }
+ }
+#ifdef __NT__
+ if ( hPipe != INVALID_HANDLE_VALUE )
+ {
+ HANDLE hTempPipe = hPipe;
+ DBUG_PRINT( "quit", ("Closing named pipes") );
+ hPipe = INVALID_HANDLE_VALUE;
+ CancelIo( hTempPipe );
+ DisconnectNamedPipe( hTempPipe );
+ CloseHandle( hTempPipe );
+ }
+#endif
+#ifdef HAVE_SYS_UN_H
+ if (unix_sock != INVALID_SOCKET)
+ {
+ (void) shutdown(unix_sock,2);
+ (void) closesocket(unix_sock);
+ (void) unlink(mysql_unix_port);
+ unix_sock= INVALID_SOCKET;
+ }
+#endif
+ end_thr_alarm(); // Don't allow alarms
+
+ /* First signal all threads that it's time to die */
+
+ THD *tmp;
+ (void) pthread_mutex_lock(&LOCK_thread_count); // For unlink from list
+
+ I_List_iterator<THD> it(threads);
+ while ((tmp=it++))
+ {
+ DBUG_PRINT("quit",("Informing thread %ld that it's time to die",
+ tmp->thread_id));
+ tmp->killed=1;
+ if (tmp->mysys_var)
+ {
+ tmp->mysys_var->abort=1;
+ if (tmp->mysys_var->current_mutex)
+ {
+ pthread_mutex_lock(tmp->mysys_var->current_mutex);
+ pthread_cond_broadcast(tmp->mysys_var->current_cond);
+ pthread_mutex_unlock(tmp->mysys_var->current_mutex);
+ }
+ }
+ }
+ (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);
+ for (;;)
+ {
+ DBUG_PRINT("quit",("Locking LOCK_thread_count"));
+ (void) pthread_mutex_lock(&LOCK_thread_count); // For unlink from list
+ if (!(tmp=threads.get()))
+ {
+ DBUG_PRINT("quit",("Unlocking LOCK_thread_count"));
+ (void) pthread_mutex_unlock(&LOCK_thread_count);
+ break;
+ }
+#ifndef __bsdi__ // Bug in BSDI kernel
+ if ((net.vio=tmp->net.vio) != 0)
+ {
+ sql_print_error(ER(ER_FORCING_CLOSE),my_progname,
+ tmp->thread_id,tmp->user ? tmp->user : "");
+ close_connection(&net,0,0);
+ }
+#endif
+ DBUG_PRINT("quit",("Unlocking LOCK_thread_count"));
+ (void) pthread_mutex_unlock(&LOCK_thread_count);
+ }
+ net_end(&net);
+ /* All threads has now been aborted */
+ DBUG_PRINT("quit",("Waiting for threads to die (count=%u)",thread_count));
+ (void) pthread_mutex_lock(&LOCK_thread_count);
+ while (thread_count)
+ {
+ (void) pthread_cond_wait(&COND_thread_count,&LOCK_thread_count);
+ DBUG_PRINT("quit",("One thread died (count=%u)",thread_count));
+ }
+ (void) pthread_mutex_unlock(&LOCK_thread_count);
+
+ mysql_log.close();
+ mysql_update_log.close();
+ mysql_bin_log.close();
+ my_free(charsets_list, MYF(0));
+ DBUG_PRINT("quit",("close_connections thread"));
+ DBUG_VOID_RETURN;
+}
+
+void kill_mysql(void)
+{
+ DBUG_ENTER("kill_mysql");
+
+#if defined(__WIN__)
+ {
+ if (!SetEvent(hEventShutdown))
+ {
+ DBUG_PRINT("error",("Got error: %ld from SetEvent",GetLastError()));
+ }
+ // or:
+ // HANDLE hEvent=OpenEvent(0, FALSE, "MySqlShutdown");
+ // SetEvent(hEventShutdown);
+ // CloseHandle(hEvent);
+ }
+#elif defined(HAVE_PTHREAD_KILL)
+ if (pthread_kill(signal_thread,SIGTERM)) /* End everything nicely */
+ {
+ DBUG_PRINT("error",("Got error %d from pthread_kill",errno)); /* purecov: inspected */
+ }
+#else
+ kill(current_pid,SIGTERM);
+#endif
+ DBUG_PRINT("quit",("After pthread_kill"));
+ shutdown_in_progress=1; // Safety if kill didn't work
+ DBUG_VOID_RETURN;
+}
+
+
+ /* Force server down. kill all connections and threads and exit */
+
+#ifndef __WIN__
+static void *kill_server(void *sig_ptr)
+#define RETURN_FROM_KILL_SERVER return 0
+#else
+static void __cdecl kill_server(int sig_ptr)
+#define RETURN_FROM_KILL_SERVER 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 (kill_in_progress) // Safety
+ RETURN_FROM_KILL_SERVER;
+ kill_in_progress=TRUE;
+ abort_loop=1; // This should be set
+ signal(sig,SIG_IGN);
+ if (sig == MYSQL_KILL_SIGNAL || sig == 0)
+ sql_print_error(ER(ER_NORMAL_SHUTDOWN),my_progname);
+ else
+ sql_print_error(ER(ER_GOT_SIGNAL),my_progname,sig); /* purecov: inspected */
+
+#if defined(USE_ONE_SIGNAL_HAND) && !defined(__WIN__)
+ my_thread_init(); // If this is a new thread
+#endif
+ close_connections();
+ sql_print_error(ER(ER_SHUTDOWN_COMPLETE),my_progname);
+ if (sig != MYSQL_KILL_SIGNAL && sig != 0)
+ unireg_abort(1); /* purecov: inspected */
+ else
+ unireg_end(0);
+ pthread_exit(0); /* purecov: deadcode */
+ RETURN_FROM_KILL_SERVER;
+}
+
+
+#ifdef USE_ONE_SIGNAL_HAND
+pthread_handler_decl(kill_server_thread,arg __attribute__((unused)))
+{
+ my_thread_init(); // Initialize new thread
+ kill_server(0);
+ my_thread_end(); // Normally never reached
+ return 0;
+}
+#endif
+
+static sig_handler print_signal_warning(int sig)
+{
+ 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
+#ifndef __WIN__
+ if (sig == SIGALRM)
+ alarm(2); /* reschedule alarm */
+#endif
+}
+
+
+void unireg_end(int signal_number __attribute__((unused)))
+{
+ clean_up();
+ pthread_exit(0); // Exit is in main thread
+}
+
+
+void unireg_abort(int exit_code)
+{
+ if (exit_code)
+ sql_print_error("Aborting\n");
+ (void) my_delete(pidfile_name,MYF(0)); // This may not always exist
+ clean_up(); /* purecov: inspected */
+ exit(exit_code); /* purecov: inspected */
+}
+
+
+void clean_up(void)
+{
+ DBUG_PRINT("exit",("clean_up"));
+ if (cleanup_done++)
+ return; /* purecov: inspected */
+ acl_free(1);
+ grant_free();
+ sql_cache_free();
+ table_cache_free();
+ hostname_cache_free();
+ item_user_lock_free();
+ lex_free(); /* Free some memory */
+#ifdef HAVE_DLOPEN
+ if (!opt_noacl)
+ udf_free();
+#endif
+ end_key_cache(); /* This is usually freed automaticly */
+ (void) ha_panic(HA_PANIC_CLOSE); /* close all tables */
+#ifdef USE_RAID
+ end_raid();
+#endif
+ x_free((gptr) errmsg[ERRMAPP]); /* Free messages */
+ free_defaults(defaults_argv);
+ my_free(mysql_tmpdir,MYF(0));
+ my_end(opt_endinfo ? MY_CHECK_ERROR | MY_GIVE_INFO : 0);
+
+ /* Tell main we are ready */
+ (void) pthread_mutex_lock(&LOCK_thread_count);
+ ready_to_exit=1;
+ (void) pthread_cond_broadcast(&COND_thread_count);
+ (void) pthread_mutex_unlock(&LOCK_thread_count);
+} /* clean_up */
+
+
+
+/****************************************************************************
+** Init IP and UNIX socket
+****************************************************************************/
+
+static void set_ports()
+{
+ char *env;
+ if (!mysql_port)
+ { // Get port if not from commandline
+ struct servent *serv_ptr;
+ mysql_port = MYSQL_PORT;
+ if ((serv_ptr = getservbyname("mysql", "tcp")))
+ mysql_port = ntohs((u_short) serv_ptr->s_port); /* purecov: inspected */
+ if ((env = getenv("MYSQL_TCP_PORT")))
+ mysql_port = (uint) atoi(env); /* purecov: inspected */
+ }
+ if (!mysql_unix_port)
+ {
+#ifdef __WIN__
+ mysql_unix_port = (char*) MYSQL_NAMEDPIPE;
+#else
+ mysql_unix_port = (char*) MYSQL_UNIX_ADDR;
+#endif
+ if ((env = getenv("MYSQL_UNIX_PORT")))
+ mysql_unix_port = env; /* purecov: inspected */
+ }
+}
+
+/* Change to run as another user if started with --user */
+
+static void set_user(const char *user)
+{
+#ifndef __WIN__
+ struct passwd *ent;
+
+ // don't bother if we aren't superuser
+ if (geteuid())
+ {
+ if (user)
+ fprintf(stderr,
+ "Warning: One can only use the --user switch if running as root\n");
+ return;
+ }
+ else if (!user)
+ {
+ if (!opt_bootstrap)
+ {
+ fprintf(stderr,"Fatal error: Please read \"Security\" section of the manual to find out how to run mysqld as root!\n");
+ unireg_abort(1);
+ }
+ return;
+ }
+ if (!strcmp(user,"root"))
+ return; // Avoid problem with dynamic libraries
+
+ 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);
+ }
+#ifdef HAVE_INITGROUPS
+ initgroups(user,ent->pw_gid);
+#endif
+ if (setgid(ent->pw_gid) == -1)
+ {
+ sql_perror("setgid");
+ unireg_abort(1);
+ }
+ if (setuid(ent->pw_uid) == -1)
+ {
+ sql_perror("setuid");
+ unireg_abort(1);
+ }
+#endif
+}
+
+/* Change root user if started with --chroot */
+
+static void set_root(const char *path)
+{
+#if !defined(__WIN__) && !defined(__EMX__)
+ if (chroot(path) == -1)
+ {
+ sql_perror("chroot");
+ unireg_abort(1);
+ }
+#endif
+}
+
+static void server_init(void)
+{
+ struct sockaddr_in IPaddr;
+#ifdef HAVE_SYS_UN_H
+ struct sockaddr_un UNIXaddr;
+#endif
+ int arg=1;
+ DBUG_ENTER("server_init");
+
+#ifdef __WIN__
+ if ( !opt_disable_networking )
+ {
+ WSADATA WsaData;
+ if (SOCKET_ERROR == WSAStartup (0x0101, &WsaData))
+ {
+ my_message(0,"WSAStartup Failed\n",MYF(0));
+ unireg_abort(1);
+ }
+ }
+#endif /* __WIN__ */
+
+ set_ports();
+
+ if (mysql_port != 0 && !opt_disable_networking && !opt_bootstrap)
+ {
+ DBUG_PRINT("general",("IP Socket is %d",mysql_port));
+ ip_sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (ip_sock == INVALID_SOCKET)
+ {
+ DBUG_PRINT("error",("Got error: %d from socket()",socket_errno));
+ sql_perror(ER(ER_IPSOCK_ERROR)); /* purecov: tested */
+ unireg_abort(1); /* purecov: tested */
+ }
+ bzero((char*) &IPaddr, sizeof(IPaddr));
+ IPaddr.sin_family = AF_INET;
+ IPaddr.sin_addr.s_addr = my_bind_addr;
+ IPaddr.sin_port = (unsigned short) htons((unsigned short) mysql_port);
+ (void) setsockopt(ip_sock,SOL_SOCKET,SO_REUSEADDR,(char*)&arg,sizeof(arg));
+ for(;;)
+ {
+ if (bind(ip_sock, my_reinterpret_cast(struct sockaddr *) (&IPaddr),
+ sizeof(IPaddr)) >= 0)
+ break;
+ DBUG_PRINT("error",("Got error: %d from bind",socket_errno));
+ sql_perror("Can't start server: Bind on TCP/IP port");/* Had a loop here */
+ sql_print_error("Do you already have another mysqld server running on port: %d ?",mysql_port);
+ unireg_abort(1);
+ }
+ (void) listen(ip_sock,(int) back_log);
+ }
+
+ if (mysqld_chroot)
+ set_root(mysqld_chroot);
+
+ set_user(mysqld_user); // set_user now takes care of mysqld_user==NULL
+
+#ifdef __NT__
+ /* create named pipe */
+ if (Service.IsNT() && mysql_unix_port[0] && !opt_bootstrap)
+ {
+ sprintf( szPipeName, "\\\\.\\pipe\\%s", mysql_unix_port );
+ ZeroMemory( &saPipeSecurity, sizeof(saPipeSecurity) );
+ ZeroMemory( &sdPipeDescriptor, sizeof(sdPipeDescriptor) );
+ if ( !InitializeSecurityDescriptor(&sdPipeDescriptor,
+ SECURITY_DESCRIPTOR_REVISION) )
+ {
+ sql_perror("Can't start server : Initialize security descriptor");
+ unireg_abort(1);
+ }
+ if (!SetSecurityDescriptorDacl(&sdPipeDescriptor, TRUE, NULL, FALSE))
+ {
+ sql_perror("Can't start server : Set security descriptor");
+ unireg_abort(1);
+ }
+ saPipeSecurity.nLength = sizeof( SECURITY_ATTRIBUTES );
+ saPipeSecurity.lpSecurityDescriptor = &sdPipeDescriptor;
+ saPipeSecurity.bInheritHandle = FALSE;
+ if ((hPipe = CreateNamedPipe(szPipeName,
+ PIPE_ACCESS_DUPLEX,
+ PIPE_TYPE_BYTE |
+ PIPE_READMODE_BYTE |
+ PIPE_WAIT,
+ PIPE_UNLIMITED_INSTANCES,
+ (int) net_buffer_length,
+ (int) net_buffer_length,
+ NMPWAIT_USE_DEFAULT_WAIT,
+ &saPipeSecurity )) == INVALID_HANDLE_VALUE)
+ {
+ LPVOID lpMsgBuf;
+ int error=GetLastError();
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) &lpMsgBuf, 0, NULL );
+ MessageBox( NULL, (LPTSTR) lpMsgBuf, "Error from CreateNamedPipe",
+ MB_OK|MB_ICONINFORMATION );
+ LocalFree( lpMsgBuf );
+ unireg_abort(1);
+ }
+ }
+#endif
+
+#if defined(HAVE_SYS_UN_H) && !defined(HAVE_mit_thread)
+ /*
+ ** Create the UNIX socket
+ */
+ if (mysql_unix_port[0] && !opt_bootstrap)
+ {
+ DBUG_PRINT("general",("UNIX Socket is %s",mysql_unix_port));
+
+ if ((unix_sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
+ {
+ sql_perror("Can't start server : UNIX Socket "); /* purecov: inspected */
+ unireg_abort(1); /* purecov: inspected */
+ }
+ bzero((char*) &UNIXaddr, sizeof(UNIXaddr));
+ UNIXaddr.sun_family = AF_UNIX;
+ strmov(UNIXaddr.sun_path, mysql_unix_port);
+ (void) unlink(mysql_unix_port);
+ (void) setsockopt(unix_sock,SOL_SOCKET,SO_REUSEADDR,(char*)&arg,
+ sizeof(arg));
+ umask(0);
+ if (bind(unix_sock, my_reinterpret_cast(struct sockaddr *) (&UNIXaddr),
+ sizeof(UNIXaddr)) < 0)
+ {
+ sql_perror("Can't start server : Bind on unix socket"); /* purecov: tested */
+ sql_print_error("Do you already have another mysqld server running on socket: %s ?",mysql_unix_port);
+ unireg_abort(1); /* purecov: tested */
+ }
+ umask(((~my_umask) & 0666));
+#if defined(S_IFSOCK) && defined(SECURE_SOCKETS)
+ (void) chmod(mysql_unix_port,S_IFSOCK); /* Fix solaris 2.6 bug */
+#endif
+ (void) listen(unix_sock,(int) back_log);
+ }
+#endif
+ DBUG_PRINT("info",("server started"));
+ DBUG_VOID_RETURN;
+}
+
+
+void yyerror(const char *s)
+{
+ NET *net=my_pthread_getspecific_ptr(NET*,THR_NET);
+ char *yytext=(char*) current_lex->tok_start;
+ if (!strcmp(s,"parse error"))
+ s=ER(ER_SYNTAX_ERROR);
+ net_printf(net,ER_PARSE_ERROR, s, yytext ? (char*) yytext : "",
+ current_lex->yylineno);
+}
+
+
+void close_connection(NET *net,uint errcode,bool lock)
+{
+ Vio* vio;
+ DBUG_ENTER("close_connection");
+ DBUG_PRINT("enter",("fd: %s error: '%s'",
+ net->vio? vio_description(net->vio):"(not connected)",
+ errcode ? ER(errcode) : ""));
+ if (lock)
+ (void) pthread_mutex_lock(&LOCK_thread_count);
+ if ((vio=net->vio) != 0)
+ {
+ if (errcode)
+ send_error(net,errcode,ER(errcode)); /* purecov: inspected */
+ vio_close(vio); /* vio is freed in delete thd */
+ }
+ if (lock)
+ (void) pthread_mutex_unlock(&LOCK_thread_count);
+ DBUG_VOID_RETURN;
+}
+
+ /* Called when a thread is aborted */
+ /* ARGSUSED */
+
+sig_handler end_thread_signal(int sig __attribute__((unused)))
+{
+ THD *thd=current_thd;
+ DBUG_ENTER("end_thread_signal");
+ if (thd)
+ end_thread(thd,0);
+ DBUG_VOID_RETURN; /* purecov: deadcode */
+}
+
+
+void end_thread(THD *thd, bool put_in_cache)
+{
+ DBUG_ENTER("end_thread");
+ (void) pthread_mutex_lock(&LOCK_thread_count);
+ thread_count--;
+ delete thd;
+
+ if (cached_thread_count < thread_cache_size && ! abort_loop &&
+ !kill_cached_threads)
+ {
+ /* Don't kill the thread, just put it in cache for reuse */
+ DBUG_PRINT("info", ("Adding thread to cache"))
+ cached_thread_count++;
+ while (!abort_loop && ! wake_thread && ! kill_cached_threads)
+ (void) pthread_cond_wait(&COND_thread_cache, &LOCK_thread_count);
+ cached_thread_count--;
+ if (kill_cached_threads)
+ pthread_cond_signal(&COND_flush_thread_cache);
+ if (wake_thread)
+ {
+ wake_thread--;
+ thd=thread_cache.get();
+ threads.append(thd);
+ (void) thd->store_globals();
+ pthread_mutex_unlock(&LOCK_thread_count);
+ DBUG_VOID_RETURN;
+ }
+ }
+
+ DBUG_PRINT("info", ("sending a broadcast"))
+
+ /* Tell main we are ready */
+ (void) pthread_cond_broadcast(&COND_thread_count);
+ (void) pthread_mutex_unlock(&LOCK_thread_count);
+ DBUG_PRINT("info", ("unlocked thread_count mutex"))
+#ifndef DBUG_OFF
+ if (!(test_flags & TEST_NO_THREADS)) // For debugging under Linux
+#endif
+ {
+ my_thread_end();
+ pthread_exit(0);
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+/* Start a cached thread. LOCK_thread_count is locked on entry */
+
+static void start_cached_thread(THD *thd)
+{
+ thread_cache.append(thd);
+ wake_thread++;
+ thread_count++;
+ pthread_cond_signal(&COND_thread_cache);
+}
+
+
+void flush_thread_cache()
+{
+ (void) pthread_mutex_lock(&LOCK_thread_count);
+ kill_cached_threads++;
+ while (cached_thread_count)
+ {
+ pthread_cond_broadcast(&COND_thread_cache);
+ pthread_cond_wait(&COND_flush_thread_cache,&LOCK_thread_count);
+ }
+ kill_cached_threads--;
+ (void) pthread_mutex_unlock(&LOCK_thread_count);
+}
+
+
+ /*
+ ** 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)))
+{
+ THD *thd=current_thd;
+ DBUG_ENTER("abort_thread");
+ if (thd)
+ thd->killed=1;
+ DBUG_VOID_RETURN;
+}
+#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
+******************************************************************************/
+
+#ifdef __WIN__
+static void init_signals(void)
+{
+ int signals[] = {SIGINT,SIGILL,SIGFPE,SIGSEGV,SIGTERM,SIGABRT } ;
+ for (uint i=0 ; i < sizeof(signals)/sizeof(int) ; i++)
+ signal( signals[i], kill_server) ;
+ signal(SIGBREAK,SIG_IGN); //ignore SIGBREAK for NT
+}
+
+#elif defined(__EMX__)
+static void sig_reload(int signo)
+{
+ reload_acl_and_cache(~0); // Flush everything
+ signal(signo, SIG_ACK);
+}
+
+static void sig_kill(int signo)
+{
+ if (!abort_loop)
+ {
+ abort_loop=1; // mark abort for threads
+ kill_server((void*) signo);
+ }
+ signal(signo, SIG_ACK);
+}
+
+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();
+}
+#else
+
+static void init_signals(void)
+{
+ sigset_t set;
+ pthread_attr_t thr_attr;
+ int error;
+ DBUG_ENTER("init_signals");
+
+ sigset(THR_KILL_SIGNAL,end_thread_signal);
+ sigset(THR_SERVER_ALARM,print_signal_warning); // Should never be called!
+ (void) sigemptyset(&set);
+#ifdef THREAD_SPECIFIC_SIGPIPE
+ sigset(SIGPIPE,abort_thread);
+ sigaddset(&set,SIGPIPE);
+#else
+ (void) signal(SIGPIPE,SIG_IGN); // Can't know which thread
+ sigaddset(&set,SIGPIPE);
+#endif
+ sigaddset(&set,SIGINT);
+ sigaddset(&set,SIGQUIT);
+ sigaddset(&set,SIGTERM);
+ sigaddset(&set,SIGHUP);
+ signal(SIGTERM,SIG_DFL); // If it's blocked by parent
+#ifdef SIGTSTP
+ sigaddset(&set,SIGTSTP);
+#endif
+ sigaddset(&set,THR_SERVER_ALARM);
+ sigdelset(&set,THR_KILL_SIGNAL); // May be SIGINT
+ sigdelset(&set,THR_CLIENT_ALARM); // For alarms
+ (void) pthread_sigmask(SIG_SETMASK,&set,NULL);
+
+ (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);
+ if (!(opt_specialflag & SPECIAL_NO_PRIOR))
+ my_pthread_attr_setprio(&thr_attr,INTERRUPT_PRIOR);
+ pthread_attr_setstacksize(&thr_attr,32768);
+#endif
+
+ (void) pthread_mutex_lock(&LOCK_thread_count);
+ if ((error=pthread_create(&signal_thread,&thr_attr,signal_hand,0)))
+ {
+ sql_print_error("Can't create interrupt-thread (error %d, errno: %d)",
+ error,errno);
+ exit(1);
+ }
+ (void) pthread_cond_wait(&COND_thread_count,&LOCK_thread_count);
+ pthread_mutex_unlock(&LOCK_thread_count);
+
+ (void) pthread_attr_destroy(&thr_attr);
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+** This threads handles all signals and alarms
+*/
+
+/* ARGSUSED */
+static void *signal_hand(void *arg __attribute__((unused)))
+{
+ sigset_t set;
+ int sig;
+ my_thread_init(); // Init new thread
+ DBUG_ENTER("signal_hand");
+
+ /* Setup alarm handler */
+ init_thr_alarm(max_connections+max_insert_delayed_threads);
+#if SIGINT != THR_KILL_SIGNAL
+ (void) sigemptyset(&set); // Setup up SIGINT for debug
+ (void) sigaddset(&set,SIGINT); // For debugging
+ (void) pthread_sigmask(SIG_UNBLOCK,&set,NULL);
+#endif
+ (void) sigemptyset(&set); // Setup up SIGINT for debug
+#ifdef USE_ONE_SIGNAL_HAND
+ (void) sigaddset(&set,THR_SERVER_ALARM); // For alarms
+#endif
+ (void) sigaddset(&set,SIGQUIT);
+ (void) sigaddset(&set,SIGTERM);
+#if THR_CLIENT_ALARM != SIGHUP
+ (void) sigaddset(&set,SIGHUP);
+#endif
+ (void) sigaddset(&set,SIGTSTP);
+
+ /* Save pid to this process (or thread on Linux) */
+ {
+ FILE *pidFile;
+ if ((pidFile = my_fopen(pidfile_name,O_WRONLY,MYF(MY_WME))))
+ {
+ fprintf(pidFile,"%lu",(ulong) getpid());
+ (void) my_fclose(pidFile,MYF(0));
+ (void) chmod(pidfile_name,0644);
+ }
+ }
+
+ (void) pthread_mutex_lock(&LOCK_thread_count);
+ (void) pthread_cond_signal(&COND_thread_count); /* continue init_signals */
+ (void) pthread_mutex_unlock(&LOCK_thread_count);
+
+ for (;;)
+ {
+ int error; // Used when debugging
+ if (shutdown_in_progress && !abort_loop)
+ {
+ sig=SIGTERM;
+ error=0;
+ }
+ else
+ while ((error=my_sigwait(&set,&sig)) == EINTR) ;
+ if (cleanup_done)
+ pthread_exit(0); // Safety
+ switch (sig) {
+ case SIGTERM:
+ case SIGQUIT:
+ case SIGKILL:
+#ifdef EXTRA_DEBUG
+ sql_print_error("Got signal %d to shutdown mysqld",sig);
+#endif
+ DBUG_PRINT("info",("Got signal: %d abort_loop: %d",sig,abort_loop));
+ if (!abort_loop)
+ {
+ abort_loop=1; // mark abort for threads
+#ifdef USE_ONE_SIGNAL_HAND
+ pthread_t tmp;
+ if (!(opt_specialflag & SPECIAL_NO_PRIOR))
+ my_pthread_attr_setprio(&connection_attrib,INTERRUPT_PRIOR);
+ if (pthread_create(&tmp,&connection_attrib, kill_server_thread,
+ (void*) sig))
+ sql_print_error("Error: Can't create thread to kill server");
+#else
+ kill_server((void*) sig); // MIT THREAD has a alarm thread
+#endif
+ }
+ break;
+ case SIGHUP:
+ reload_acl_and_cache(~0); // Flush everything
+ mysql_print_status((THD*) 0); // Send debug some info
+ break;
+#ifdef USE_ONE_SIGNAL_HAND
+ case THR_SERVER_ALARM:
+ process_alarm(sig); // Trigger alarms.
+ break;
+#endif
+ default:
+#ifdef EXTRA_DEBUG
+ sql_print_error("Warning: Got signal: %d, error: %d",sig,error); /* purecov: tested */
+#endif
+ break; /* purecov: tested */
+ }
+ }
+ return(0); /* purecov: deadcode */
+}
+
+#endif /* __WIN__*/
+
+
+/*
+** 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)))
+{
+ NET *net;
+ DBUG_ENTER("my_message_sql");
+ DBUG_PRINT("error",("Message: '%s'",str));
+ if ((net=my_pthread_getspecific_ptr(NET*,THR_NET)))
+ {
+ if (!net->last_error[0]) // Return only first message
+ {
+ strmake(net->last_error,str,sizeof(net->last_error)-1);
+ net->last_errno=error ? error : ER_UNKNOWN_ERROR;
+ }
+ }
+ else
+ sql_print_error("%s: %s",my_progname,str); /* purecov: inspected */
+ DBUG_RETURN(0);
+}
+
+#ifdef __WIN__
+#undef errno
+#undef EINTR
+#define errno WSAGetLastError()
+#define EINTR WSAEINTR
+
+struct utsname
+{
+ char nodename[FN_REFLEN];
+};
+
+int uname(struct utsname *a)
+{
+ return -1;
+}
+#endif
+
+
+#ifdef __WIN__
+pthread_handler_decl(handle_shutdown,arg)
+{
+ MSG msg;
+ my_thread_init();
+
+ /* this call should create the message queue for this thread */
+ PeekMessage(&msg, NULL, 1, 65534,PM_NOREMOVE);
+
+ if (WaitForSingleObject(hEventShutdown,INFINITE)==WAIT_OBJECT_0)
+ kill_server(MYSQL_KILL_SIGNAL);
+ return 0;
+}
+
+int __stdcall handle_kill(ulong ctrl_type)
+{
+ if (ctrl_type == CTRL_CLOSE_EVENT ||
+ ctrl_type == CTRL_SHUTDOWN_EVENT)
+ {
+ kill_server(MYSQL_KILL_SIGNAL);
+ return TRUE;
+ }
+ return FALSE;
+}
+#endif
+
+const char *load_default_groups[]= { "mysqld","server",0 };
+
+#ifdef HAVE_LIBWRAP
+char *libwrapName=NULL;
+#endif
+
+static void open_log(MYSQL_LOG *log, const char *hostname,
+ const char *opt_name, const char *extension,
+ enum_log_type type)
+{
+ char tmp[FN_REFLEN];
+ if (!opt_name || !opt_name[0])
+ {
+ strnmov(tmp,hostname,FN_REFLEN-5);
+ strmov(strcend(tmp,'.'),extension);
+ opt_name=tmp;
+ }
+ log->open(opt_name,type);
+}
+
+
+
+#ifdef __WIN__
+int win_main(int argc, char **argv)
+#else
+int main(int argc, char **argv)
+#endif
+{
+ DEBUGGER_OFF;
+ char hostname[FN_REFLEN];
+
+ my_umask=0660; // Default umask for new files
+ my_umask_dir=0700; // Default umask for new directories
+ MY_INIT(argv[0]); // init my_sys library & pthreads
+ tzset(); // Set tzname
+
+ start_time=time((time_t*) 0);
+#ifdef HAVE_TZNAME
+#if defined(HAVE_LOCALTIME_R) && defined(_REENTRANT)
+ {
+ struct tm tm_tmp;
+ localtime_r(&start_time,&tm_tmp);
+ strmov(time_zone,tzname[tm_tmp.tm_isdst == 1 ? 1 : 0]);
+ }
+#else
+ {
+ struct tm *start_tm;
+ start_tm=localtime(&start_time);
+ strmov(time_zone=tzname[start_tm->tm_isdst == 1 ? 1 : 0]);
+ }
+#endif
+#endif
+
+ if (gethostname(hostname,sizeof(hostname)-4) < 0)
+ strmov(hostname,"mysql");
+ strmov(pidfile_name,hostname);
+ strmov(strcend(pidfile_name,'.'),".pid"); // Add extension
+#ifdef DEMO_VERSION
+ strcat(server_version,"-demo");
+#endif
+#ifdef SHAREWARE_VERSION
+ strcat(server_version,"-shareware");
+#endif
+#ifndef DBUG_OFF
+ strcat(server_version,"-debug");
+#endif
+#ifdef _CUSTOMSTARTUPCONFIG_
+ if (_cust_check_startup())
+ {
+ /* _cust_check_startup will report startup failure error */
+ exit( 1 );
+ }
+#endif
+ load_defaults("my",load_default_groups,&argc,&argv);
+ defaults_argv=argv;
+ mysql_tmpdir=getenv("TMPDIR"); /* Use this if possible */
+#ifdef __WIN__
+ if (!mysql_tmpdir)
+ mysql_tmpdir=getenv("TEMP");
+ if (!mysql_tmpdir)
+ mysql_tmpdir=getenv("TMP");
+#endif
+ if (!mysql_tmpdir || !mysql_tmpdir[0])
+ mysql_tmpdir=(char*) P_tmpdir; /* purecov: inspected */
+
+ set_options();
+#ifdef __WIN__
+ /* service parameters can be overwritten by options */
+ if (get_service_parameters())
+ {
+ my_message( 0, "Can't read MySQL service parameters", MYF(0) );
+ exit( 1 );
+ }
+#endif
+ get_options(argc,argv);
+ if (opt_log || opt_update_log || opt_slow_log || opt_bin_log)
+ strcat(server_version,"-log");
+ DBUG_PRINT("info",("%s Ver %s for %s on %s\n",my_progname,
+ server_version, SYSTEM_TYPE,MACHINE_TYPE));
+
+ /* These must be set early */
+
+ (void) pthread_cond_init(&COND_thread_count,NULL);
+ (void) pthread_mutex_init(&LOCK_mysql_create_db,NULL);
+ (void) pthread_mutex_init(&LOCK_Acl,NULL);
+ (void) pthread_mutex_init(&LOCK_grant,NULL);
+ (void) pthread_mutex_init(&LOCK_open,NULL);
+ (void) pthread_mutex_init(&LOCK_thread_count,NULL);
+ (void) pthread_mutex_init(&LOCK_mapped_file,NULL);
+ (void) pthread_mutex_init(&LOCK_status,NULL);
+ (void) pthread_mutex_init(&LOCK_error_log,NULL);
+ (void) pthread_mutex_init(&LOCK_delayed_insert,NULL);
+ (void) pthread_mutex_init(&LOCK_delayed_status,NULL);
+ (void) pthread_mutex_init(&LOCK_delayed_create,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_flush,NULL);
+ (void) pthread_mutex_init(&LOCK_flush,NULL);
+ (void) pthread_mutex_init(&LOCK_crypt,NULL);
+ (void) pthread_mutex_init(&LOCK_bytes_sent,NULL);
+ (void) pthread_mutex_init(&LOCK_bytes_received,NULL);
+ (void) pthread_mutex_init(&LOCK_timezone,NULL);
+ (void) pthread_mutex_init(&LOCK_binlog_update, NULL);
+ (void) pthread_mutex_init(&LOCK_slave, NULL);
+ (void) pthread_cond_init(&COND_binlog_update, NULL);
+ (void) pthread_cond_init(&COND_slave_stopped, NULL);
+
+ if (set_default_charset_by_name(default_charset, MYF(MY_WME)))
+ unireg_abort(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);
+ if (!ssl_acceptor_fd)
+ opt_use_ssl=0;
+ /* having ssl_acceptor_fd!=0 signals the use of SSL */
+ }
+#endif /* HAVE_OPENSSL */
+
+#ifdef HAVE_LIBWRAP
+ libwrapName= my_progname+dirname_length(my_progname);
+ openlog(libwrapName, LOG_PID, LOG_AUTH);
+#endif
+
+ if (!(opt_specialflag & SPECIAL_NO_PRIOR))
+ my_pthread_setprio(pthread_self(),CONNECT_PRIOR);
+ /* Parameter for threads created for connections */
+ (void) pthread_attr_init(&connection_attrib);
+ (void) pthread_attr_setdetachstate(&connection_attrib,
+ PTHREAD_CREATE_DETACHED);
+ pthread_attr_setstacksize(&connection_attrib,thread_stack);
+
+ if (!(opt_specialflag & SPECIAL_NO_PRIOR))
+ my_pthread_attr_setprio(&connection_attrib,WAIT_PRIOR);
+ pthread_attr_setscope(&connection_attrib, PTHREAD_SCOPE_SYSTEM);
+
+#ifdef SET_RLIMIT_NOFILE
+ /* connections and databases neads lots of files */
+ {
+ uint wanted_files=10+(uint) max(max_connections*5,
+ max_connections+table_cache_size*2);
+ uint files=set_maximum_open_files(wanted_files);
+ if (files && files < wanted_files) // Some systems return 0
+ {
+ max_connections= (ulong) (files-10)/5;
+ table_cache_size= (ulong) (files-10-max_connections)/2;
+ DBUG_PRINT("warning",
+ ("Changed limits: max_connections: %ld table_cache: %ld",
+ max_connections,table_cache_size));
+ sql_print_error("Warning: Changed limits: max_connections: %ld table_cache: %ld",max_connections,table_cache_size);
+ }
+ }
+#endif
+ unireg_init(opt_specialflag); /* Set up extern variabels */
+ init_errmessage(); /* Read error messages from file */
+ lex_init();
+ item_init();
+ mysys_uses_curses=0;
+#ifdef USE_REGEX
+ regex_init();
+#endif
+ select_thread=pthread_self();
+ select_thread_in_use=1;
+
+ /*
+ ** We have enough space for fiddling with the argv, continue
+ */
+ umask(((~my_umask) & 0666));
+ if (my_setwd(mysql_real_data_home,MYF(MY_WME)))
+ {
+ unireg_abort(1); /* purecov: inspected */
+ }
+ 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();
+ randominit(&sql_rand,(ulong) start_time,(ulong) start_time/2);
+ reset_floating_point_exceptions();
+ init_thr_lock();
+
+ /* Setup log files */
+ if (opt_log)
+ open_log(&mysql_log, hostname, opt_logname, ".log", LOG_NORMAL);
+ if (opt_update_log)
+ open_log(&mysql_update_log, hostname, opt_update_logname, "",
+ LOG_NEW);
+ if (opt_bin_log)
+ {
+ mysql_bin_log.set_index_file_name(opt_binlog_index_name);
+ open_log(&mysql_bin_log, hostname, opt_bin_logname, "-bin",
+ LOG_BIN);
+ }
+ if (opt_slow_log)
+ open_log(&mysql_slow_log, hostname, opt_slow_logname, "-slow.log",
+ LOG_NORMAL);
+ if (ha_init())
+ {
+ sql_print_error("Can't init databases");
+ exit(1);
+ }
+ ft_init_stopwords(NULL); /* SerG */
+
+#ifdef __WIN__
+#define MYSQL_ERR_FILE "mysql.err"
+ if (!opt_console)
+ {
+ freopen(MYSQL_ERR_FILE,"a+",stdout);
+ freopen(MYSQL_ERR_FILE,"a+",stderr);
+ FreeConsole(); // Remove window
+ }
+#endif
+
+ /*
+ init signals & alarm
+ After this we can't quit by a simple unireg_abort
+ */
+ error_handler_hook = my_message_sql;
+ if (pthread_key_create(&THR_THD,NULL) || pthread_key_create(&THR_NET,NULL) ||
+ pthread_key_create(&THR_MALLOC,NULL))
+ {
+ sql_print_error("Can't create thread-keys");
+ exit(1);
+ }
+ init_signals(); // Creates pidfile
+ if (acl_init(opt_noacl))
+ {
+ select_thread_in_use=0;
+ (void) pthread_kill(signal_thread,MYSQL_KILL_SIGNAL);
+ (void) my_delete(pidfile_name,MYF(MY_WME)); // Not neaded anymore
+ exit(1);
+ }
+ if (!opt_noacl)
+ (void) grant_init();
+
+#ifdef HAVE_DLOPEN
+ if (!opt_noacl)
+ udf_init();
+#endif
+
+ if (opt_bootstrap)
+ {
+ int error=bootstrap(stdin);
+ end_thr_alarm(); // Don't allow alarms
+ unireg_abort(error ? 1 : 0);
+ }
+ if (opt_init_file)
+ {
+ if (read_init_file(opt_init_file))
+ {
+ end_thr_alarm(); // Don't allow alarms
+ unireg_abort(1);
+ }
+ }
+ (void) thr_setconcurrency(concurrency); // 10 by default
+#ifdef __WIN__ //IRENA
+ {
+ hEventShutdown=CreateEvent(0, FALSE, FALSE, "MySqlShutdown");
+ pthread_t hThread;
+ if (pthread_create(&hThread,&connection_attrib,handle_shutdown,0))
+ sql_print_error("Warning: Can't create thread to handle shutdown requests");
+
+ // On "Stop Service" we have to do regular shutdown
+ Service.SetShutdownEvent(hEventShutdown);
+ }
+#endif
+
+ if (flush_time && flush_time != ~(ulong) 0L)
+ {
+ pthread_t hThread;
+ if (pthread_create(&hThread,&connection_attrib,handle_flush,0))
+ sql_print_error("Warning: Can't create thread to handle flush");
+ }
+
+ // slave thread
+ if(master_host)
+ {
+ pthread_t hThread;
+ if(pthread_create(&hThread, &connection_attrib, handle_slave, 0))
+ sql_print_error("Warning: Can't create thread to handle slave");
+
+ }
+
+ printf(ER(ER_READY),my_progname,server_version,"");
+ fflush(stdout);
+
+#ifdef __NT__
+ if (hPipe == INVALID_HANDLE_VALUE && !have_tcpip)
+ {
+ sql_print_error("TCP/IP must be installed on Win98 platforms");
+ }
+ else
+ {
+ pthread_mutex_lock(&LOCK_thread_count);
+ (void) pthread_cond_init(&COND_handler_count,NULL);
+ {
+ pthread_t hThread;
+ handler_count=0;
+ if ( hPipe != INVALID_HANDLE_VALUE )
+ {
+ handler_count++;
+ if (pthread_create(&hThread,&connection_attrib,
+ handle_connections_namedpipes, 0))
+ {
+ sql_print_error("Warning: Can't create thread to handle named pipes");
+ handler_count--;
+ }
+ }
+ if (have_tcpip)
+ {
+ handler_count++;
+ if (pthread_create(&hThread,&connection_attrib,
+ handle_connections_sockets, 0))
+ {
+ sql_print_error("Warning: Can't create thread to handle named pipes");
+ handler_count--;
+ }
+ }
+ 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
+ sql_print_error("Exiting main thread");
+#endif
+#endif /* __NT__ */
+
+ /* (void) pthread_attr_destroy(&connection_attrib); */
+
+ DBUG_PRINT("quit",("Exiting main thread"));
+
+#ifndef __WIN__
+#ifdef EXTRA_DEBUG
+ sql_print_error("Before Lock_thread_count");
+#endif
+ (void) pthread_mutex_lock(&LOCK_thread_count);
+ 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
+ sql_print_error("After lock_thread_count");
+#endif
+#else
+ // remove the event, because it will not be valid anymore
+ Service.SetShutdownEvent(0);
+ if(hEventShutdown) CloseHandle(hEventShutdown);
+ // if it was started as service on NT try to stop the service
+ if(Service.IsNT())
+ Service.Stop();
+#endif
+
+ /* 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);
+ (void) my_delete(pidfile_name,MYF(MY_WME)); // Not neaded anymore
+ my_thread_end();
+ exit(0);
+ return(0); /* purecov: deadcode */
+}
+
+
+#ifdef __WIN__
+/* ------------------------------------------------------------------------
+ main and thread entry function for Win32
+ (all this is needed only to run mysqld as a service on WinNT)
+ -------------------------------------------------------------------------- */
+int mysql_service(void *p)
+{
+ win_main(Service.my_argc, Service.my_argv);
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ // check environment variable OS
+ if (Service.GetOS()) // "OS" defined; Should be NT
+ {
+ if (argc == 2)
+ {
+ if (!strcmp(argv[1],"-install") || !strcmp(argv[1],"--install"))
+ {
+ char path[FN_REFLEN];
+ my_path(path, argv[0], ""); // Find name in path
+ fn_format(path,argv[0],path,"",1+4+16); // Force use of full path
+ if (!Service.Install(MYSQL_SERVICENAME,MYSQL_SERVICENAME,path))
+ MessageBox(NULL,"Failed to install Service",MYSQL_SERVICENAME,
+ MB_OK|MB_ICONSTOP);
+ return 0;
+ }
+ else if (!strcmp(argv[1],"-remove") || !strcmp(argv[1],"--remove"))
+ {
+ Service.Remove(MYSQL_SERVICENAME);
+ return 0;
+ }
+ }
+ else if (argc == 1) // No arguments; start as a service
+ {
+ // init service
+ long tmp=Service.Init(MYSQL_SERVICENAME,mysql_service);
+ return 0;
+ }
+ }
+
+ // This is a WIN95 machine or a start of mysqld as a standalone program
+ // we have to pass the arguments, in case of NT-service this will be done
+ // by ServiceMain()
+
+ Service.my_argc=argc;
+ Service.my_argv=argv;
+ mysql_service(NULL);
+ return 0;
+}
+/* ------------------------------------------------------------------------ */
+#endif
+
+
+static int bootstrap(FILE *file)
+{
+ THD *thd= new THD;
+ int error;
+ thd->bootstrap=1;
+ thd->client_capabilities=0;
+ my_net_init(&thd->net,(Vio*) 0);
+ thd->max_packet_length=thd->net.max_packet;
+ thd->master_access= ~0;
+ thread_count++;
+ thd->real_id=pthread_self();
+ error=handle_bootstrap(thd,file);
+ net_end(&thd->net);
+ delete thd;
+ return error;
+}
+
+static bool read_init_file(char *file_name)
+{
+ FILE *file;
+ DBUG_ENTER("read_init_file");
+ DBUG_PRINT("enter",("name: %s",file_name));
+ if (!(file=my_fopen(file_name,O_RDONLY,MYF(MY_WME))))
+ return(1);
+ bootstrap(file); /* Ignore errors from this */
+ (void) my_fclose(file,MYF(MY_WME));
+ return 0;
+}
+
+
+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
+ if (protocol_version > 9)
+ net->return_errno=1;
+
+ /* don't allow too many connections */
+ if (thread_count - delayed_insert_threads >= max_connections+1 || abort_loop)
+ {
+ DBUG_PRINT("error",("too many connections"));
+ close_connection(net,ER_CON_COUNT_ERROR);
+ 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;
+ }
+ if (thread_count-delayed_insert_threads > max_used_connections)
+ max_used_connections=thread_count-delayed_insert_threads;
+ thd->thread_id=thread_id++;
+ for (uint i=0; i < 8 ; i++) // Generate password teststring
+ thd->scramble[i]= (char) (rnd(&sql_rand)*94+33);
+ thd->scramble[8]=0;
+ thd->rand=sql_rand;
+ thd->real_id=pthread_self(); // Keep purify happy
+
+ /* Start a new thread to handle connection */
+#ifndef DBUG_OFF
+ if (test_flags & TEST_NO_THREADS) // For debugging under Linux
+ {
+ thread_cache_size=0; // Safety
+ thread_count++;
+ threads.append(thd);
+ thd->real_id=pthread_self();
+ (void) pthread_mutex_unlock(&LOCK_thread_count);
+ handle_one_connection((void*) thd);
+ }
+ else
+#endif
+ {
+ if (cached_thread_count > wake_thread)
+ {
+ start_cached_thread(thd);
+ (void) pthread_mutex_unlock(&LOCK_thread_count);
+ }
+ else
+ {
+ int error;
+ thread_count++;
+ threads.append(thd);
+ DBUG_PRINT("info",(("creating thread %d"), thd->thread_id));
+ thd->connect_time = time(NULL);
+ (void) pthread_mutex_unlock(&LOCK_thread_count);
+ if ((error=pthread_create(&thd->real_id,&connection_attrib,
+ handle_one_connection,
+ (void*) thd)))
+ {
+ DBUG_PRINT("error",
+ ("Can't create thread to handle request (error %d)",
+ error));
+ (void) pthread_mutex_lock(&LOCK_thread_count);
+ thread_count--;
+ thd->killed=1; // Safety
+ (void) pthread_mutex_unlock(&LOCK_thread_count);
+ net_printf(net,ER_CANT_CREATE_THREAD,error);
+ (void) pthread_mutex_lock(&LOCK_thread_count);
+ close_connection(net,0,0);
+ delete thd;
+ (void) pthread_mutex_unlock(&LOCK_thread_count);
+ DBUG_VOID_RETURN;
+ }
+ }
+ }
+ DBUG_PRINT("info",(("Thread %d created"), thd->thread_id));
+ DBUG_VOID_RETURN;
+}
+
+
+ /* Handle new connections and spawn new process to handle them */
+
+pthread_handler_decl(handle_connections_sockets,arg __attribute__((unused)))
+{
+ my_socket sock,new_sock;
+ uint error_count=0;
+ uint max_used_connection= (uint) (max(ip_sock,unix_sock)+1);
+ fd_set readFDs,clientFDs;
+ THD *thd;
+ struct sockaddr_in cAddr;
+ int ip_flags=0,socket_flags=0,flags;
+ Vio *vio_tmp;
+#ifdef __WIN__
+ my_thread_init();
+#endif
+ DBUG_ENTER("handle_connections_sockets");
+
+ LINT_INIT(new_sock);
+
+ (void) my_pthread_getprio(pthread_self()); // For debugging
+
+ FD_ZERO(&clientFDs);
+ if (ip_sock != INVALID_SOCKET)
+ {
+ FD_SET(ip_sock,&clientFDs);
+#ifdef HAVE_FCNTL
+ ip_flags = fcntl(ip_sock, F_GETFL, 0);
+#endif
+ }
+#ifdef HAVE_SYS_UN_H
+ FD_SET(unix_sock,&clientFDs);
+ socket_flags=fcntl(unix_sock, F_GETFL, 0);
+#endif
+
+ DBUG_PRINT("general",("Waiting for connections."));
+ while (!abort_loop)
+ {
+ readFDs=clientFDs;
+#ifdef HPUX
+ if (select(max_used_connection,(int*) &readFDs,0,0,0) < 0)
+ continue;
+#else
+ if (select((int) max_used_connection,&readFDs,0,0,0) < 0)
+ {
+ if (errno != EINTR)
+ {
+ if (!select_errors++ && !abort_loop) /* purecov: inspected */
+ sql_print_error("mysqld: Got error %d from select",errno); /* purecov: inspected */
+ }
+ continue;
+ }
+#endif /* HPUX */
+ if (abort_loop)
+ break;
+
+ /*
+ ** Is this a new connection request
+ */
+
+#ifdef HAVE_SYS_UN_H
+ if (FD_ISSET(unix_sock,&readFDs))
+ {
+ sock = unix_sock;
+ flags= socket_flags;
+ }
+ else
+#endif
+ {
+ sock = ip_sock;
+ flags= ip_flags;
+ }
+
+#if !defined(NO_FCNTL_NONBLOCK)
+ if (!(test_flags & TEST_BLOCKING))
+ {
+#if defined(O_NONBLOCK)
+ fcntl(sock, F_SETFL, flags | O_NONBLOCK);
+#elif defined(O_NDELAY)
+ fcntl(sock, F_SETFL, flags | O_NDELAY);
+#endif
+ }
+#endif /* NO_FCNTL_NONBLOCK */
+ for (uint retry=0; retry < MAX_ACCEPT_RETRY; retry++)
+ {
+ size_socket length=sizeof(struct sockaddr_in);
+ new_sock = accept(sock, my_reinterpret_cast(struct sockaddr *) (&cAddr),
+ &length);
+ if (new_sock != INVALID_SOCKET || (errno != EINTR && errno != EAGAIN))
+ break;
+#if !defined(NO_FCNTL_NONBLOCK)
+ if (!(test_flags & TEST_BLOCKING))
+ {
+ if (retry == MAX_ACCEPT_RETRY - 1)
+ fcntl(sock, F_SETFL, flags); // Try without O_NONBLOCK
+ }
+#endif
+ }
+#if !defined(NO_FCNTL_NONBLOCK)
+ if (!(test_flags & TEST_BLOCKING))
+ fcntl(sock, F_SETFL, flags);
+#endif
+ if (new_sock < 0)
+ {
+ if ((error_count++ & 255) == 0) // This can happen often
+ sql_perror("Error in accept");
+ if (errno == ENFILE || errno == EMFILE)
+ sleep(1); // Give other threads some time
+ continue;
+ }
+
+#ifdef HAVE_LIBWRAP
+ {
+ if (sock == ip_sock)
+ {
+ struct request_info req;
+ signal(SIGCHLD, SIG_DFL);
+ request_init(&req, RQ_DAEMON, libwrapName, RQ_FILE, new_sock, NULL);
+ fromhost(&req);
+ if (!hosts_access(&req))
+ {
+ // This may be stupid but refuse() includes an exit(0)
+ // which we surely don't want...
+ // clean_exit() - same stupid thing ...
+ syslog(deny_severity, "refused connect from %s", eval_client(&req));
+ if (req.sink)
+ ((void (*)(int))req.sink)(req.fd);
+
+ // C++ sucks (the gibberish in front just translates the supplied
+ // sink function pointer in the req structure from a void (*sink)();
+ // to a void(*sink)(int) if you omit the cast, the C++ compiler
+ // will cry...
+
+ (void) shutdown(new_sock,2); // This looks fine to me...
+ (void) closesocket(new_sock);
+ continue;
+ }
+ }
+ }
+#endif /* HAVE_LIBWRAP */
+
+ {
+ size_socket dummyLen;
+ struct sockaddr dummy;
+ dummyLen = sizeof(struct sockaddr);
+ if (getsockname(new_sock,&dummy, &dummyLen) < 0)
+ {
+ sql_perror("Error on new connection socket");
+ (void) shutdown(new_sock,2);
+ (void) closesocket(new_sock);
+ continue;
+ }
+ }
+
+ /*
+ ** Don't allow too many connections
+ */
+
+ if (!(thd= new THD))
+ {
+ (void) shutdown(new_sock,2); VOID(closesocket(new_sock));
+ continue;
+ }
+ if (!(vio_tmp=vio_new(new_sock,
+ new_sock == unix_sock ? VIO_TYPE_SOCKET :
+ VIO_TYPE_TCPIP,
+ new_sock == unix_sock)) ||
+ my_net_init(&thd->net,vio_tmp))
+ {
+ if (vio_tmp)
+ vio_delete(vio_tmp);
+ else
+ {
+ (void) shutdown(new_sock,2);
+ (void) closesocket(new_sock);
+ }
+ delete thd;
+ continue;
+ }
+ if (sock == unix_sock)
+ {
+ if (!(thd->host=my_strdup(LOCAL_HOST,MYF(0))))
+ {
+ close_connection(&thd->net,ER_OUT_OF_RESOURCES);
+ delete thd;
+ continue;
+ }
+ }
+ create_new_thread(thd);
+ }
+
+#ifdef __NT__
+ pthread_mutex_lock(&LOCK_thread_count);
+ handler_count--;
+ pthread_cond_signal(&COND_handler_count);
+ pthread_mutex_unlock(&LOCK_thread_count);
+#endif
+ DBUG_RETURN(0);
+}
+
+
+#ifdef __NT__
+pthread_handler_decl(handle_connections_namedpipes,arg)
+{
+ HANDLE hConnectedPipe;
+ BOOL fConnected;
+ THD *thd;
+ my_thread_init();
+ DBUG_ENTER("handle_connections_namedpipes");
+ (void) my_pthread_getprio(pthread_self()); // For debugging
+
+ DBUG_PRINT("general",("Waiting for named pipe connections."));
+ while (!abort_loop)
+ {
+ /* wait for named pipe connection */
+ fConnected = ConnectNamedPipe( hPipe, NULL );
+ if (abort_loop)
+ break;
+ if ( !fConnected )
+ fConnected = GetLastError() == ERROR_PIPE_CONNECTED;
+ if ( !fConnected )
+ {
+ CloseHandle( hPipe );
+ if ((hPipe = CreateNamedPipe(szPipeName,
+ PIPE_ACCESS_DUPLEX,
+ PIPE_TYPE_BYTE |
+ PIPE_READMODE_BYTE |
+ PIPE_WAIT,
+ PIPE_UNLIMITED_INSTANCES,
+ (int) net_buffer_length,
+ (int) net_buffer_length,
+ NMPWAIT_USE_DEFAULT_WAIT,
+ &saPipeSecurity )) ==
+ INVALID_HANDLE_VALUE )
+ {
+ sql_perror("Can't create new named pipe!");
+ break; // Abort
+ }
+ }
+ hConnectedPipe = hPipe;
+ /* create new pipe for new connection */
+ if ((hPipe = CreateNamedPipe(szPipeName,
+ PIPE_ACCESS_DUPLEX,
+ PIPE_TYPE_BYTE |
+ PIPE_READMODE_BYTE |
+ PIPE_WAIT,
+ PIPE_UNLIMITED_INSTANCES,
+ (int) net_buffer_length,
+ (int) net_buffer_length,
+ NMPWAIT_USE_DEFAULT_WAIT,
+ &saPipeSecurity)) ==
+ INVALID_HANDLE_VALUE)
+ {
+ sql_perror("Can't create new named pipe!");
+ hPipe=hConnectedPipe;
+ continue; // We have to try again
+ }
+
+ if ( !(thd = new THD))
+ {
+ DisconnectNamedPipe( hConnectedPipe );
+ CloseHandle( hConnectedPipe );
+ continue;
+ }
+ if (!(thd->net.vio = vio_new_win32pipe(hConnectedPipe)) ||
+ my_net_init(&thd->net, thd->net.vio))
+ {
+ close_connection(&thd->net,ER_OUT_OF_RESOURCES);
+ delete thd;
+ continue;
+ }
+ /* host name is unknown */
+ thd->host = my_strdup("localhost",MYF(0)); /* Host is unknown */
+ create_new_thread(thd);
+ }
+
+ pthread_mutex_lock(&LOCK_thread_count);
+ handler_count--;
+ pthread_cond_signal(&COND_handler_count);
+ pthread_mutex_unlock(&LOCK_thread_count);
+ DBUG_RETURN(0);
+}
+#endif /* __NT__ */
+
+/****************************************************************************
+** Create thread that automaticly flush all tables after a given time
+****************************************************************************/
+
+pthread_handler_decl(handle_flush,arg __attribute__((unused)))
+{
+ my_thread_init();
+ DBUG_ENTER("handle_flush");
+
+ pthread_detach_this_thread();
+ flush_thread=pthread_self();
+ flush_thread_in_use=1;
+
+ pthread_mutex_lock(&LOCK_flush);
+ for (;;)
+ {
+ struct timespec abstime;
+#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
+ (void) pthread_cond_timedwait(&COND_flush,&LOCK_flush, &abstime);
+ if (abort_loop)
+ break;
+ flush_tables();
+ }
+ flush_thread_in_use=0;
+ pthread_mutex_unlock(&LOCK_flush);
+ my_thread_end();
+ DBUG_RETURN(0);
+}
+
+
+/******************************************************************************
+** handle start options
+******************************************************************************/
+
+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_RECOVER,
+ OPT_MASTER_HOST, OPT_MASTER_USER, OPT_MASTER_PASSWORD,
+ OPT_MASTER_PORT, OPT_MASTER_INFO_FILE, OPT_MASTER_CONNECT_RETRY,
+ 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};
+
+static struct option long_options[] =
+{
+ {"ansi", no_argument,0, 'a'},
+ {"basedir", required_argument, 0, 'b'},
+#ifdef HAVE_BERKELEY_DB
+ {"bdb-home", required_argument, 0, OPT_BDB_HOME},
+ {"bdb-lock-detect", required_argument, 0, OPT_BDB_LOCK},
+ {"bdb-logdir", required_argument, 0, OPT_BDB_LOG},
+ {"bdb-recover", no_argument, 0, OPT_BDB_RECOVER},
+ {"bdb-no-sync", no_argument, 0, OPT_BDB_NOSYNC},
+ {"bdb-tmpdir", required_argument, 0, OPT_BDB_TMP},
+#endif
+ {"big-tables", no_argument,0,(int) OPT_BIG_TABLES},
+ {"binlog-do-db", required_argument, 0, OPT_BINLOG_DO_DB},
+ {"binlog-ignore-db", required_argument, 0, OPT_BINLOG_IGNORE_DB},
+ {"bind-address", required_argument, 0, OPT_BIND_ADDRESS},
+ {"bootstrap", no_argument,0,(int) OPT_BOOTSTRAP},
+#ifdef __WIN__
+ {"console", no_argument, 0, OPT_CONSOLE},
+#endif
+ {"chroot", required_argument,0, 'r'},
+ {"character-sets-dir",required_argument,0, OPT_CHARSETS_DIR},
+ {"datadir", required_argument, 0, 'h'},
+#ifndef DBUG_OFF
+ {"debug", optional_argument, 0, '#'},
+#endif
+ {"default-character-set",required_argument,0,'C'},
+ {"default-table-type",required_argument,0,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},
+ {"exit-info", optional_argument, 0, 'T'},
+ {"flush", no_argument, 0, OPT_FLUSH},
+ {"help", no_argument, 0, '?'},
+ {"init-file", required_argument, 0, (int) OPT_INIT_FILE},
+ {"log", optional_argument, 0, 'l'},
+ {"language", required_argument, 0, 'L'},
+ {"log-bin", optional_argument, 0, (int) OPT_BIN_LOG},
+ {"log-bin-index", optional_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-info-file", required_argument, 0, (int) OPT_MASTER_INFO_FILE},
+ {"new", no_argument, 0, 'n'},
+ {"old-protocol", no_argument, 0, 'o'},
+ {"one-thread", no_argument, 0, OPT_ONE_THREAD},
+ {"pid-file", required_argument, 0, (int) OPT_PID_FILE},
+ {"port", required_argument, 0, 'P'},
+ {"replicate-do-db", required_argument, 0, OPT_REPLICATE_DO_DB},
+ {"replicate-ignore-db", required_argument, 0, OPT_REPLICATE_IGNORE_DB},
+ {"safe-mode", no_argument, 0, (int) OPT_SAFE},
+ {"socket", required_argument, 0, (int) OPT_SOCKET},
+ {"set-variable", required_argument, 0, 'O'},
+#ifdef HAVE_BERKELEY_DB
+ {"skip-bdb", no_argument, 0, OPT_BDB_SKIP},
+#endif
+ {"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-new", no_argument,0, (int) OPT_SKIP_NEW},
+ {"skip-show-database",no_argument,0, (int) OPT_SKIP_SHOW_DB},
+ {"skip-networking", no_argument,0, (int) OPT_SKIP_NETWORKING},
+ {"skip-thread-priority", no_argument,0,(int) OPT_SKIP_PRIOR},
+ {"sql-bin-update-same", no_argument, 0, (int)OPT_SQL_BIN_UPDATE_SAME},
+#include "sslopt-longopts.h"
+#ifdef __WIN__
+ {"standalone", no_argument,0, (int) OPT_STANDALONE},
+#endif
+ {"tmpdir", required_argument, 0, 't'},
+ {"use-locking", no_argument, 0,(int) OPT_USE_LOCKING},
+#ifdef USE_SYMDIR
+ {"use-symbolic-links",no_argument, 0, 's'},
+#endif
+ {"user", required_argument, 0, 'u'},
+ {"version", no_argument, 0, 'V'},
+ {0, 0, 0, 0}
+};
+
+CHANGEABLE_VAR changeable_vars[] = {
+ { "back_log", (long*) &back_log,50,1,65535,0,1},
+#ifdef HAVE_BERKELEY_DB
+ { "bdb_cache_size", (long*) &berkeley_cache_size, KEY_CACHE_SIZE,
+ 20*1024, (long) ~0, 0, IO_SIZE},
+#endif
+ { "connect_timeout", (long*) &connect_timeout,CONNECT_TIMEOUT,1,65535,0,1},
+ { "delayed_insert_timeout",(long*) &delayed_insert_timeout,
+ DELAYED_WAIT_TIMEOUT,1,~0L,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,~0L,0,1},
+ { "interactive_timeout", (long*) &net_interactive_timeout,
+ NET_WAIT_TIMEOUT,1,31*24*60*60,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,~0L,0,1},
+ { "lower_case_table_names", (long*) &lower_case_table_names,IF_WIN(1,0),
+ 0,1,0,1},
+ { "max_allowed_packet",(long*) &max_allowed_packet,1024*1024L,80,
+ 17*1024*1024L, MALLOC_OVERHEAD,1024},
+ { "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_write_lock_count",(long*) &max_write_lock_count,~0L,1,~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,65535,0,1},
+ { "net_write_timeout", (long*) &net_write_timeout,NET_WRITE_TIMEOUT,1,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 },
+ { "slow_launch_time", (long*) &slow_launch_time, 2L,
+ 0L,~0L,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,1,16384,0,1},
+ { "tmp_table_size", (long*) &tmp_table_size,1024*1024L,1024,~0L,
+ MALLOC_OVERHEAD,1},
+ { "thread_stack", (long*) &thread_stack,1024*64,1024*32,~0L,0,1024},
+ { "wait_timeout", (long*) &net_wait_timeout,NET_WAIT_TIMEOUT,1,~0L,0,1},
+ { NullS,(long*) 0,0,0,0,0,0,} };
+
+
+struct show_var_st init_vars[]= {
+ {"ansi_mode",(char*) &opt_ansi_mode ,SHOW_BOOL},
+ {"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_home", (char*) &berkeley_home, SHOW_CHAR_PTR},
+ {"bdb_logdir", (char*) &berkeley_logdir, SHOW_CHAR_PTR},
+ {"bdb_tmpdir", (char*) &berkeley_tmpdir, SHOW_CHAR_PTR},
+#endif
+ {"character_set", default_charset, SHOW_CHAR},
+ {"character_sets", (char*) &charsets_list, SHOW_CHAR_PTR},
+ {"connect_timeout", (char*) &connect_timeout, SHOW_LONG},
+ {"concurrent_insert",(char*) &myisam_concurrent_insert, SHOW_MY_BOOL},
+ {"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},
+ {"join_buffer_size", (char*) &join_buff_size, SHOW_LONG},
+ {"flush", (char*) &myisam_flush, SHOW_MY_BOOL},
+ {"flush_time",(char*) &flush_time, SHOW_LONG},
+ {"init_file", (char*) &opt_init_file, SHOW_CHAR_PTR},
+ {"interactive_timeout", (char*) &net_interactive_timeout, SHOW_LONG},
+ {"key_buffer_size", (char*) &keybuff_size, SHOW_LONG},
+ {"language", language, SHOW_CHAR},
+ {"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},
+ {"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_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_tmp_tables", (char*) &max_tmp_tables,SHOW_LONG},
+ {"max_write_lock_count",(char*) &max_write_lock_count,SHOW_LONG},
+ {"myisam_sort_buffer_size", (char*) &myisam_sort_buffer_size,SHOW_LONG},
+ {"net_buffer_length", (char*) &net_buffer_length, SHOW_LONG},
+ {"net_retry_count", (char*) &mysqld_net_retry_count, 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},
+ {"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},
+ {"table_cache", (char*) &table_cache_size,SHOW_LONG},
+ {"table_type", (char*) (&default_table_type_name), SHOW_CHAR_PTR},
+#ifdef HAVE_THR_SETCONCURRENCY
+ {"thread_concurrency", (char*) &concurrency, SHOW_LONG},
+#endif
+ {"thread_stack", (char*) &thread_stack,SHOW_LONG},
+ {"thread_cache_size", (char*) &thread_cache_size,SHOW_LONG},
+#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},
+ {"Bytes_received", (char*) &bytes_received, SHOW_LONG},
+ {"Bytes_sent", (char*) &bytes_sent, SHOW_LONG},
+ {"Connections", (char*) &thread_id, SHOW_LONG_CONST},
+ {"Created_tmp_tables",(char*) &created_tmp_tables, SHOW_LONG},
+ {"Delayed_insert_threads", (char*) &delayed_insert_threads, SHOW_LONG},
+ {"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_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},
+ {"Handler_read_next", (char*) &ha_read_next_count, SHOW_LONG},
+ {"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_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},
+ {"Key_read_requests", (char*) &_my_cache_r_requests, SHOW_LONG},
+ {"Key_reads", (char*) &_my_cache_read, SHOW_LONG},
+ {"Key_write_requests",(char*) &_my_cache_w_requests, SHOW_LONG},
+ {"Key_writes", (char*) &_my_cache_write, SHOW_LONG},
+ {"Max_used_connections", (char*) &max_used_connections, SHOW_LONG},
+ {"Not_flushed_key_blocks", (char*) &_my_blocks_changed, SHOW_LONG_CONST},
+ {"Not_flushed_delayed_rows", (char*) &delayed_rows_in_use, SHOW_LONG_CONST},
+ {"Open_tables", (char*) 0, SHOW_OPENTABLES},
+ {"Open_files", (char*) &my_file_opened, SHOW_INT_CONST},
+ {"Open_streams", (char*) &my_stream_opened, SHOW_INT_CONST},
+ {"Opened_tables", (char*) &opened_tables, SHOW_LONG},
+ {"Questions", (char*) 0, SHOW_QUESTION},
+ {"Slow_launch_threads", (char*) &slow_launch_threads, SHOW_LONG},
+ {"Slow_queries", (char*) &long_query_count, SHOW_LONG},
+ {"Slave_running", (char*) &slave_running, SHOW_BOOL},
+ {"Threads_cached", (char*) &cached_thread_count, SHOW_LONG_CONST},
+ {"Threads_connected", (char*) &thread_count, SHOW_INT_CONST},
+ {"Threads_running", (char*) &thread_running, SHOW_INT_CONST},
+ {"Uptime", (char*) 0, SHOW_STARTTIME},
+ {NullS,NullS,SHOW_LONG}
+};
+
+static void print_version(void)
+{
+ printf("%s Ver %s for %s on %s\n",my_progname,
+ server_version,SYSTEM_TYPE,MACHINE_TYPE);
+}
+
+static void usage(void)
+{
+ print_version();
+ puts("Copyright (C) 2000 MySQL AB & MySQL Finland AB, by Monty and others");
+ 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("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\
+ -h, --datadir=path Path to the database root");
+#ifndef DBUG_OFF
+ printf("\
+ -#, --debug[=...] Debug log. Default is '%s'\n",default_dbug_option);
+#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 Print some debug info at exit\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\
+ -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 isam 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\
+ --pid-file=path Pid file used by safe_mysqld\n\
+ -P, --port=... Port number to use for connection\n\
+ -n, --new Use very new possible 'unsafe' functions\n\
+ -o, --old-protocol Use the old (3.20) protocol\n\
+ --one-thread Only use one thread (for debugging under Linux)\n\
+ -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\
+ --skip-delay-key-write\n\
+ Ignore the delay_key_write option for all tables\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\
+ --skip-host-cache Don't cache host names\n");
+ /* We have to break the string here because of VC++ limits */
+ puts("\
+ --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\
+ -u, --user=user_name Run mysqld daemon as user\n\
+ -V, --version output version information and exit");
+#ifdef __WIN__
+ puts("NT and Win32 specific options:\n\
+ --console Don't remove the console window\n\
+ --install Install mysqld as a service (NT)\n\
+ --remove Remove mysqld from the service list (NT)\n\
+ --standalone Dummy option to start as a standalone program (NT)\n\
+");
+#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-nosync Don't synchronously flush logs\n\
+ --bdb-recover Start Berkeley DB in recover mode\n\
+ --bdb-tmpdir=directory Berkeley DB tempfile name\n\
+ --skip-bdb Don't use berkeley db (will save memory)\n\
+");
+#endif
+ print_defaults("my",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);
+ printf("pid file: %s\n",pidfile_name);
+ if (opt_logname)
+ printf("logfile: %s\n",opt_logname);
+ if (opt_update_logname)
+ printf("update log: %s\n",opt_update_logname);
+ if (opt_bin_logname)
+ {
+ printf("binary log: %s\n",opt_bin_logname);
+ printf("binary log index: %s\n",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) && !defined(HAVE_mit_thread)
+ 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);
+}
+
+
+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) strmov( default_charset, MYSQL_CHARSET);
+ (void) strmov( language, LANGUAGE);
+ (void) strmov( mysql_real_data_home, get_relative_path(DATADIR));
+#ifdef __WIN__
+ /* Allow Win32 users to move MySQL anywhere */
+ {
+ char prg_dev[LIBLEN];
+ my_path(prg_dev,my_progname,"mysql/bin");
+ strcat(prg_dev,"/../"); // Remove 'bin' to get base dir
+ cleanup_dirname(mysql_home,prg_dev);
+ }
+#else
+ const char *tmpenv;
+ if ( !(tmpenv = getenv("MY_BASEDIR_VERSION")))
+ tmpenv = DEFAULT_MYSQL_HOME;
+ (void) strmov( mysql_home, tmpenv );
+#endif
+
+#if defined( HAVE_mit_thread ) || defined( __WIN__ ) || defined( HAVE_LINUXTHREADS )
+ my_disable_locking = 1;
+#endif
+ 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:noVvI?",
+ long_options, &option_index)) != EOF)
+ {
+ switch(c) {
+#ifndef DBUG_OFF
+ case '#':
+ DBUG_PUSH(optarg ? optarg : default_dbug_option);
+ opt_endinfo=1; /* unireg: memory allocation */
+ break;
+#endif
+ case 'a':
+ opt_ansi_mode=1;
+ thd_startup_options|=OPTION_ANSI_MODE;
+ break;
+ case 'b':
+ strmov(mysql_home,optarg);
+ break;
+ case 'l':
+ opt_log=1;
+ opt_logname=optarg; // Use hostname.log if null
+ break;
+ case 'h':
+ strmov(mysql_real_data_home,optarg);
+ break;
+ case 'L':
+ strmov(language,optarg);
+ 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))
+ {
+ usage();
+ exit(1);
+ }
+ break;
+ case 'P':
+ mysql_port= (unsigned int) atoi(optarg);
+ 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 '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) : (uint) ~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
+ {
+ usage();
+ exit(1);
+ }
+ break;
+ case (int) OPT_BIG_TABLES:
+ thd_startup_options|=OPTION_BIG_TABLES;
+ break;
+ case (int) OPT_ISAM_LOG:
+ if (optarg)
+ nisam_log_filename=optarg;
+ (void) nisam_log(1);
+ 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;
+ opt_bin_logname=optarg;
+ break;
+ case (int) OPT_LOG_SLAVE_UPDATES:
+ opt_log_slave_updates = 1;
+ break;
+
+ 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_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_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_NEW:
+ opt_specialflag|= SPECIAL_NO_NEW_FUNC;
+ default_table_type=DB_TYPE_ISAM;
+ myisam_delay_key_write=0;
+ myisam_concurrent_insert=0;
+ break;
+ case (int) OPT_SAFE:
+ opt_specialflag|= SPECIAL_SAFE_MODE;
+ myisam_delay_key_write=0;
+ 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;
+ mysql_port=0;
+ break;
+ case (int) OPT_ONE_THREAD:
+ test_flags |= TEST_NO_THREADS;
+ break;
+ case (int) OPT_BIND_ADDRESS:
+ if (optarg && isdigit(optarg[0]))
+ {
+ my_bind_addr = (ulong) inet_addr(optarg);
+ }
+ 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)
+ {
+ sql_perror("Can't start server: cannot resolve hostname!");
+ exit(1);
+ }
+ my_bind_addr = (ulong) ((in_addr*)ent->h_addr_list[0])->s_addr;
+ }
+ break;
+ case (int) OPT_PID_FILE:
+ strmov(pidfile_name,optarg);
+ break;
+ case (int) OPT_INIT_FILE:
+ opt_init_file=optarg;
+ 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;
+ 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)
+ {
+ fprintf(stderr,"Unknown table type: %s\n",optarg);
+ exit(1);
+ }
+ default_table_type= (enum db_type) type;
+ 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':
+ strmov(default_charset,optarg);
+ break;
+ case OPT_CHARSETS_DIR:
+ strmov(mysql_charsets_dir, optarg);
+ charsets_dir = mysql_charsets_dir;
+ break;
+#include "sslopt-case.h"
+#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_init_flags|=DB_TXN_NOSYNC;
+ break;
+ case OPT_BDB_RECOVER:
+ berkeley_init_flags|=DB_RECOVER;
+ break;
+ case OPT_BDB_TMP:
+ berkeley_tmpdir=optarg;
+ break;
+ case OPT_BDB_LOCK:
+ {
+ int type;
+ if ((type=find_type(optarg, &berkeley_lock_typelib, 2)) > 0)
+ berkeley_lock_type=berkeley_lock_types[type-1];
+ else
+ {
+ if (test_if_int(optarg,strlen(optarg)))
+ berkeley_lock_scan_time=atoi(optarg);
+ else
+ {
+ fprintf(stderr,"Unknown lock type: %s\n",optarg);
+ exit(1);
+ }
+ }
+ break;
+ }
+ case OPT_BDB_SKIP:
+ berkeley_skip=1;
+ break;
+#endif
+ 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;
+
+ default:
+ fprintf(stderr,"%s: Unrecognized option: %c\n",my_progname,c);
+ usage();
+ exit(1);
+ }
+ }
+ // Skipp empty arguments (from shell)
+ while (argc != optind && !argv[optind][0])
+ optind++;
+ if (argc != optind)
+ {
+ fprintf(stderr,"%s: Too many parameters\n",my_progname);
+ usage();
+ exit(1);
+ }
+ fix_paths();
+ default_table_type_name=ha_table_typelib.type_names[default_table_type-1];
+}
+
+
+#ifdef __WIN__
+
+#ifndef KEY_SERVICE_PARAMETERS
+#define KEY_SERVICE_PARAMETERS "SYSTEM\\CurrentControlSet\\Services\\MySql\\Parameters"
+#endif
+
+#define COPY_KEY_VALUE(value) if (copy_key_value(hParametersKey,&(value),lpszValue)) return 1
+#define CHECK_KEY_TYPE(type,name) if ( type != dwKeyValueType ) { key_type_error(hParametersKey,name); return 1; }
+#define SET_CHANGEABLE_VARVAL(varname) if (set_varval(hParametersKey,varname,szKeyValueName,dwKeyValueType,lpdwValue)) return 1;
+
+static void key_type_error(HKEY hParametersKey,const char *szKeyValueName)
+{
+ TCHAR szErrorMsg[512];
+ RegCloseKey( hParametersKey );
+ strxmov(szErrorMsg,TEXT("Value \""),
+ szKeyValueName,
+ TEXT("\" of registry key \"" KEY_SERVICE_PARAMETERS "\" has wrong type\n"),NullS);
+ fprintf(stderr, szErrorMsg); /* not unicode compatible */
+}
+
+static bool copy_key_value(HKEY hParametersKey, char **var, const char *value)
+{
+ if (!(*var=my_strdup(value,MYF(MY_WME))))
+ {
+ RegCloseKey(hParametersKey);
+ fprintf(stderr, "Couldn't allocate memory for registry key value\n");
+ return 1;
+ }
+ return 0;
+}
+
+static bool set_varval(HKEY hParametersKey,const char *var,
+ const char *szKeyValueName, DWORD dwKeyValueType,
+ LPDWORD lpdwValue)
+{
+ CHECK_KEY_TYPE(dwKeyValueType, szKeyValueName );
+ if (set_changeable_varval(var, *lpdwValue, changeable_vars))
+ {
+ TCHAR szErrorMsg [ 512 ];
+ RegCloseKey( hParametersKey );
+ strxmov(szErrorMsg,
+ TEXT("Value \""),
+ szKeyValueName,
+ TEXT("\" of registry key \"" KEY_SERVICE_PARAMETERS "\" is invalid\n"),NullS);
+ fprintf( stderr, szErrorMsg ); /* not unicode compatible */
+ return 1;
+ }
+ return 0;
+}
+
+
+static int get_service_parameters()
+{
+ DWORD dwLastError;
+ HKEY hParametersKey;
+ DWORD dwIndex;
+ TCHAR szKeyValueName [ 256 ];
+ DWORD dwKeyValueName;
+ DWORD dwKeyValueType;
+ BYTE bKeyValueBuffer [ 512 ];
+ DWORD dwKeyValueBuffer;
+ LPDWORD lpdwValue = (LPDWORD) &bKeyValueBuffer[0];
+ LPCTSTR lpszValue = (LPCTSTR) &bKeyValueBuffer[0];
+
+ /* open parameters of service */
+ dwLastError = (DWORD) RegOpenKeyEx( HKEY_LOCAL_MACHINE,
+ TEXT(KEY_SERVICE_PARAMETERS), 0,
+ KEY_READ, &hParametersKey );
+ if ( dwLastError == ERROR_FILE_NOT_FOUND ) /* no parameters available */
+ return 0;
+ if ( dwLastError != ERROR_SUCCESS )
+ {
+ fprintf(stderr,"Can't open registry key \"" KEY_SERVICE_PARAMETERS "\" for reading\n" );
+ return 1;
+ }
+
+ /* enumerate all values of key */
+ dwIndex = 0;
+ dwKeyValueName = sizeof( szKeyValueName ) / sizeof( TCHAR );
+ dwKeyValueBuffer = sizeof( bKeyValueBuffer );
+ while ( (dwLastError = (DWORD) RegEnumValue(hParametersKey, dwIndex,
+ szKeyValueName, &dwKeyValueName,
+ NULL, &dwKeyValueType,
+ &bKeyValueBuffer[0],
+ &dwKeyValueBuffer))
+ != ERROR_NO_MORE_ITEMS )
+ {
+ /* check if error occured */
+ if ( dwLastError != ERROR_SUCCESS )
+ {
+ RegCloseKey( hParametersKey );
+ fprintf( stderr, "Can't enumerate values of registry key \"" KEY_SERVICE_PARAMETERS "\"\n" );
+ return 1;
+ }
+ if ( lstrcmp(szKeyValueName, TEXT("BaseDir")) == 0 )
+ {
+ CHECK_KEY_TYPE( REG_SZ, szKeyValueName);
+ strmov( mysql_home, lpszValue ); /* not unicode compatible */
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("BindAddress")) == 0 )
+ {
+ CHECK_KEY_TYPE( REG_SZ, szKeyValueName);
+
+ my_bind_addr = (ulong) inet_addr( lpszValue );
+ if ( my_bind_addr == (ulong) INADDR_NONE )
+ {
+ struct hostent* ent;
+
+ if ( !(*lpszValue) )
+ {
+ char szHostName [ 256 ];
+ if ( gethostname(szHostName, sizeof(szHostName)) == SOCKET_ERROR )
+ {
+ RegCloseKey( hParametersKey );
+ fprintf( stderr, "Can't get my own hostname\n" );
+ return 1;
+ }
+ ent = gethostbyname( szHostName );
+ }
+ else ent = gethostbyname( lpszValue );
+ if ( !ent )
+ {
+ RegCloseKey( hParametersKey );
+ fprintf( stderr, "Can't resolve hostname!\n" );
+ return 1;
+ }
+ my_bind_addr = (ulong) ((in_addr*)ent->h_addr_list[0])->s_addr;
+ }
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("BigTables")) == 0 )
+ {
+ CHECK_KEY_TYPE( REG_DWORD, szKeyValueName);
+ if ( *lpdwValue )
+ thd_startup_options |= OPTION_BIG_TABLES;
+ else
+ thd_startup_options &= ~((ulong)OPTION_BIG_TABLES);
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("DataDir")) == 0 )
+ {
+ CHECK_KEY_TYPE( REG_SZ, szKeyValueName );
+ strmov( mysql_real_data_home, lpszValue ); /* not unicode compatible */
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("Locking")) == 0 )
+ {
+ CHECK_KEY_TYPE( REG_DWORD, szKeyValueName );
+ my_disable_locking = !(*lpdwValue);
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("LogFile")) == 0 )
+ {
+ CHECK_KEY_TYPE( REG_SZ, szKeyValueName );
+ opt_log = 1;
+ COPY_KEY_VALUE( opt_logname );
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("UpdateLogFile")) == 0 )
+ {
+ CHECK_KEY_TYPE( REG_SZ, szKeyValueName );
+ opt_update_log = 1;
+ COPY_KEY_VALUE( opt_update_logname );
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("BinaryLogFile")) == 0 )
+ {
+ CHECK_KEY_TYPE( REG_SZ, szKeyValueName );
+ opt_bin_log = 1;
+ COPY_KEY_VALUE( opt_bin_logname );
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("BinaryLogIndexFile")) == 0 )
+ {
+ CHECK_KEY_TYPE( REG_SZ, szKeyValueName );
+ opt_bin_log = 1;
+ COPY_KEY_VALUE( opt_binlog_index_name );
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("ISAMLogFile")) == 0 )
+ {
+ CHECK_KEY_TYPE( REG_SZ, szKeyValueName );
+ COPY_KEY_VALUE( nisam_log_filename );
+ (void) nisam_log( 1 );
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("LongLogFormat")) == 0 )
+ {
+ CHECK_KEY_TYPE( REG_DWORD, szKeyValueName );
+ if ( *lpdwValue )
+ opt_specialflag |= SPECIAL_LONG_LOG_FORMAT;
+ else
+ opt_specialflag &= ~((ulong)SPECIAL_LONG_LOG_FORMAT);
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("LowPriorityUpdates")) == 0 )
+ {
+ CHECK_KEY_TYPE( REG_DWORD, szKeyValueName );
+ if ( *lpdwValue )
+ {
+ thd_startup_options |= OPTION_LOW_PRIORITY_UPDATES;
+ low_priority_updates = 1;
+ }
+ else
+ {
+ thd_startup_options &= ~((ulong)OPTION_LOW_PRIORITY_UPDATES);
+ low_priority_updates = 0;
+ }
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("Port")) == 0 )
+ {
+ CHECK_KEY_TYPE( REG_DWORD, szKeyValueName );
+ mysql_port = (unsigned int) *lpdwValue;
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("OldProtocol")) == 0 )
+ {
+ CHECK_KEY_TYPE( REG_DWORD, szKeyValueName );
+ protocol_version = *lpdwValue ? PROTOCOL_VERSION - 1 : PROTOCOL_VERSION;
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("HostnameResolving")) == 0 )
+ {
+ CHECK_KEY_TYPE( REG_DWORD, szKeyValueName );
+ if ( !*lpdwValue )
+ opt_specialflag |= SPECIAL_NO_RESOLVE;
+ else
+ opt_specialflag &= ~((ulong)SPECIAL_NO_RESOLVE);
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("Networking")) == 0 )
+ {
+ CHECK_KEY_TYPE( REG_DWORD, szKeyValueName );
+ opt_disable_networking = !(*lpdwValue);
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("ShowDatabase")) == 0 )
+ {
+ CHECK_KEY_TYPE( REG_DWORD, szKeyValueName );
+ opt_disable_networking = !(*lpdwValue);
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("HostnameCaching")) == 0 )
+ {
+ CHECK_KEY_TYPE( REG_DWORD, szKeyValueName );
+ if ( !*lpdwValue )
+ opt_specialflag |= SPECIAL_NO_HOST_CACHE;
+ else
+ opt_specialflag &= ~((ulong)SPECIAL_NO_HOST_CACHE);
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("ThreadPriority")) == 0 )
+ {
+ CHECK_KEY_TYPE( REG_DWORD, szKeyValueName );
+ if ( !(*lpdwValue) )
+ opt_specialflag |= SPECIAL_NO_PRIOR;
+ else
+ opt_specialflag &= ~((ulong)SPECIAL_NO_PRIOR);
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("NamedPipe")) == 0 )
+ {
+ CHECK_KEY_TYPE( REG_SZ, szKeyValueName );
+ COPY_KEY_VALUE( mysql_unix_port );
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("TempDir")) == 0 )
+ {
+ CHECK_KEY_TYPE( REG_SZ, szKeyValueName );
+ COPY_KEY_VALUE( mysql_tmpdir );
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("FlushTables")) == 0 )
+ {
+ CHECK_KEY_TYPE( REG_DWORD, szKeyValueName );
+ nisam_flush = myisam_flush= *lpdwValue ? 1 : 0;
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("BackLog")) == 0 )
+ {
+ SET_CHANGEABLE_VARVAL( "back_log" );
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("ConnectTimeout")) == 0 )
+ {
+ SET_CHANGEABLE_VARVAL( "connect_timeout" );
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("JoinBufferSize")) == 0 )
+ {
+ SET_CHANGEABLE_VARVAL( "join_buffer" );
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("KeyBufferSize")) == 0 )
+ {
+ SET_CHANGEABLE_VARVAL( "key_buffer" );
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("LongQueryTime")) == 0 )
+ {
+ SET_CHANGEABLE_VARVAL( "long_query_time" );
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("MaxAllowedPacket")) == 0 )
+ {
+ SET_CHANGEABLE_VARVAL( "max_allowed_packet" );
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("MaxConnections")) == 0 )
+ {
+ SET_CHANGEABLE_VARVAL( "max_connections" );
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("MaxConnectErrors")) == 0 )
+ {
+ SET_CHANGEABLE_VARVAL( "max_connect_errors" );
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("MaxInsertDelayedThreads")) == 0 )
+ {
+ SET_CHANGEABLE_VARVAL( "max_delayed_threads" );
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("MaxJoinSize")) == 0 )
+ {
+ SET_CHANGEABLE_VARVAL( "max_join_size" );
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("MaxSortLength")) == 0 )
+ {
+ SET_CHANGEABLE_VARVAL( "max_sort_length" );
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("NetBufferLength")) == 0 )
+ {
+ SET_CHANGEABLE_VARVAL( "net_buffer_length" );
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("RecordBufferSize")) == 0 )
+ {
+ SET_CHANGEABLE_VARVAL( "record_buffer" );
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("SortBufferSize")) == 0 )
+ {
+ SET_CHANGEABLE_VARVAL( "sort_buffer" );
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("TableCacheSize")) == 0 )
+ {
+ SET_CHANGEABLE_VARVAL( "table_cache" );
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("TmpTableSize")) == 0 )
+ {
+ SET_CHANGEABLE_VARVAL( "tmp_table_size" );
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("ThreadStackSize")) == 0 )
+ {
+ SET_CHANGEABLE_VARVAL( "thread_stack" );
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("WaitTimeout")) == 0 )
+ {
+ SET_CHANGEABLE_VARVAL( "wait_timeout" );
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("DelayedInsertTimeout"))
+ == 0 )
+ {
+ SET_CHANGEABLE_VARVAL( "delayed_insert_timeout" );
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("DelayedInsertLimit")) ==
+ 0 )
+ {
+ SET_CHANGEABLE_VARVAL( "delayed_insert_limit" );
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("DelayedQueueSize")) == 0
+ )
+ {
+ SET_CHANGEABLE_VARVAL( "delayed_queue_size" );
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("FlushTime")) == 0 )
+ {
+ SET_CHANGEABLE_VARVAL( "flush_time" );
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("InteractiveTimeout")) ==
+ 0 )
+ {
+ SET_CHANGEABLE_VARVAL( "interactive_timeout" );
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("LowerCaseTableNames"))
+ == 0 )
+ {
+ SET_CHANGEABLE_VARVAL( "lower_case_table_names" );
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("MaxHeapTableSize")) == 0
+ )
+ {
+ SET_CHANGEABLE_VARVAL( "max_heap_table_size" );
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("MaxTmpTables")) == 0 )
+ {
+ SET_CHANGEABLE_VARVAL( "max_tmp_tables" );
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("MaxWriteLockCount")) ==
+ 0 )
+ {
+ SET_CHANGEABLE_VARVAL( "max_write_lock_count" );
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("NetRetryCount")) == 0 )
+ {
+ SET_CHANGEABLE_VARVAL( "net_retry_count" );
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("QueryBufferSize")) == 0
+ )
+ {
+ SET_CHANGEABLE_VARVAL( "query_buffer_size" );
+ }
+ else if ( lstrcmp(szKeyValueName, TEXT("ThreadConcurrency")) ==
+ 0 )
+ {
+ SET_CHANGEABLE_VARVAL( "thread_concurrency" );
+ }
+ else
+ {
+ TCHAR szErrorMsg [ 512 ];
+ RegCloseKey( hParametersKey );
+ lstrcpy( szErrorMsg, TEXT("Value \"") );
+ lstrcat( szErrorMsg, szKeyValueName );
+ lstrcat( szErrorMsg, TEXT("\" of registry key \"" KEY_SERVICE_PARAMETERS "\" is not defined by MySQL\n") );
+ fprintf( stderr, szErrorMsg ); /* not unicode compatible */
+ return 1;
+ }
+
+ dwIndex++;
+ dwKeyValueName = sizeof( szKeyValueName ) / sizeof( TCHAR );
+ dwKeyValueBuffer = sizeof( bKeyValueBuffer );
+ }
+ RegCloseKey( hParametersKey );
+
+ /* paths are fixed by method get_options() */
+ return 0;
+}
+#endif
+
+
+static char *get_relative_path(const char *path)
+{
+ if (test_if_hard_path(path) &&
+ is_prefix(path,DEFAULT_MYSQL_HOME) &&
+ strcmp(DEFAULT_MYSQL_HOME,FN_ROOTDIR))
+ {
+ path+=strlen(DEFAULT_MYSQL_HOME);
+ while (*path == FN_LIBCHAR)
+ path++;
+ }
+ return (char*) 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);
+ (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);
+ if (test_if_hard_path(sharedir))
+ strmov(buff,sharedir); /* purecov: tested */
+ else
+ strxmov(buff,mysql_home,sharedir,NullS);
+ convert_dirname(buff);
+ (void) my_load_path(language,language,buff);
+
+ /* If --character-sets-dir isn't given, use shared library dir */
+ if (charsets_dir != mysql_charsets_dir)
+ {
+ strmov(strmov(mysql_charsets_dir,buff),CHARSET_DIR);
+ charsets_dir=mysql_charsets_dir;
+ }
+
+ /* Add '/' to TMPDIR if needed */
+ char *tmp= (char*) my_malloc(FN_REFLEN,MYF(MY_FAE));
+ if (tmp)
+ {
+ strmov(tmp,mysql_tmpdir);
+ mysql_tmpdir=tmp;
+ convert_dirname(mysql_tmpdir);
+ mysql_tmpdir=(char*) my_realloc(mysql_tmpdir,strlen(mysql_tmpdir)+1,
+ MYF(MY_HOLD_ON_ERROR));
+ }
+}
+
+
+#ifdef SET_RLIMIT_NOFILE
+static uint set_maximum_open_files(uint max_file_limit)
+{
+ struct rlimit rlimit;
+ ulong old_cur;
+
+ if (!getrlimit(RLIMIT_NOFILE,&rlimit))
+ {
+ old_cur=rlimit.rlim_cur;
+ if (rlimit.rlim_cur >= max_file_limit) // Nothing to do
+ return rlimit.rlim_cur; /* purecov: inspected */
+ 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 */
+ 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 */
+ max_file_limit=rlimit.rlim_cur;
+ }
+ }
+ return max_file_limit;
+}
+#endif
+
+
+/*****************************************************************************
+** Instantiate templates
+*****************************************************************************/
+
+#ifdef __GNUC__
+/* Used templates */
+template class I_List<THD>;
+template class I_List_iterator<THD>;
+template class I_List<i_string>;
+#endif
diff --git a/sql/net_pkg.cc b/sql/net_pkg.cc
new file mode 100644
index 00000000000..27065ee776e
--- /dev/null
+++ b/sql/net_pkg.cc
@@ -0,0 +1,329 @@
+/* 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 <stdarg.h>
+
+ /* Send a error string to client */
+
+void send_error(NET *net, uint sql_errno, const char *err)
+{
+ uint length;
+ char buff[MYSQL_ERRMSG_SIZE+2];
+ THD *thd=current_thd;
+ DBUG_ENTER("send_error");
+ DBUG_PRINT("enter",("sql_errno: %d err: %s", sql_errno,
+ err ? err : net->last_error[0] ?
+ net->last_error : "NULL"));
+
+ 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)
+ {
+ if ((err=net->last_error)[0])
+ sql_errno=net->last_errno;
+ else
+ {
+ sql_errno=ER_UNKNOWN_ERROR;
+ err=ER(sql_errno); /* purecov: inspected */
+ }
+ }
+ }
+ if (net->vio == 0)
+ {
+ if (thd && thd->bootstrap)
+ {
+ fprintf(stderr,"ERROR: %d %s\n",sql_errno,err);
+ }
+ DBUG_VOID_RETURN;
+ }
+
+ if (net->return_errno)
+ { // new client code; Add errno before message
+ int2store(buff,sql_errno);
+ length= (uint) (strmake(buff+2,err,MYSQL_ERRMSG_SIZE-1) - buff);
+ err=buff;
+ }
+ else
+ {
+ length=strlen(err);
+ set_if_smaller(length,MYSQL_ERRMSG_SIZE);
+ }
+ VOID(net_write_command(net,(uchar) 255,(char*) err,length));
+ if (thd)
+ thd->fatal_error=0; // Error message is given
+ 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
+*/
+/* VARARGS3 */
+
+void
+net_printf(NET *net, uint errcode, ...)
+{
+ va_list args;
+ uint length,offset;
+ const char *format,*text_pos;
+ int head_length= NET_HEADER_SIZE;
+ THD *thd=current_thd;
+ DBUG_ENTER("net_printf");
+ DBUG_PRINT("enter",("message: %u",errcode));
+
+ if(thd) thd->query_error = 1;
+ // if we are here, something is wrong :-)
+
+ va_start(args,errcode);
+ format=ER(errcode);
+ 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);
+ length=strlen((char*) text_pos);
+ if (length >= sizeof(net->last_error))
+ length=sizeof(net->last_error)-1; /* purecov: inspected */
+ va_end(args);
+
+ if (net->vio == 0)
+ {
+ if (thd && thd->bootstrap)
+ {
+ fprintf(stderr,"ERROR: %d %s\n",errcode,text_pos);
+ thd->fatal_error=1;
+ }
+ DBUG_VOID_RETURN;
+ }
+
+ int3store(net->buff,length+1+offset);
+ net->buff[3]= (net->compress) ? 0 : (uchar) (net->pkt_nr++);
+ net->buff[head_length]=(uchar) 255; // Error package
+ if (offset)
+ int2store(text_pos-2, errcode);
+ VOID(net_real_write(net,(char*) net->buff,length+head_length+1+offset));
+ if (thd)
+ thd->fatal_error=0; // Error message is given
+ DBUG_VOID_RETURN;
+}
+
+
+void
+send_ok(NET *net,ha_rows affected_rows,ulonglong id,const char *message)
+{
+ if(net->no_send_ok)
+ return;
+
+ char buff[MYSQL_ERRMSG_SIZE+10],*pos;
+ DBUG_ENTER("send_ok");
+ buff[0]=0; // No fields
+ pos=net_store_length(buff+1,(ulonglong) affected_rows);
+ pos=net_store_length(pos, (ulonglong) id);
+ if (net->return_status)
+ {
+ int2store(pos,*net->return_status);
+ pos+=2;
+ }
+ if (message)
+ pos=net_store_data((char*) pos,message);
+ if (net->vio != 0)
+ {
+ VOID(my_net_write(net,buff,(uint) (pos-buff)));
+ VOID(net_flush(net));
+ }
+ DBUG_VOID_RETURN;
+}
+
+void
+send_eof(NET *net,bool no_flush)
+{
+ static char eof_buff[1]= { (char) 254 }; /* Marker for end of fields */
+ DBUG_ENTER("send_eof");
+ if (net->vio != 0)
+ {
+ VOID(my_net_write(net,eof_buff,1));
+ if (!no_flush)
+ VOID(net_flush(net));
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+/****************************************************************************
+** Store a field length in logical packet
+****************************************************************************/
+
+char *
+net_store_length(char *pkg, ulonglong length)
+{
+ uchar *packet=(uchar*) pkg;
+ if (length < LL(251))
+ {
+ *packet=(uchar) length;
+ return (char*) packet+1;
+ }
+ /* 251 is reserved for NULL */
+ if (length < LL(65536))
+ {
+ *packet++=252;
+ int2store(packet,(uint) length);
+ return (char*) packet+2;
+ }
+ if (length < LL(16777216))
+ {
+ *packet++=253;
+ int3store(packet,(ulong) length);
+ return (char*) packet+3;
+ }
+ *packet++=254;
+ int8store(packet,length);
+ return (char*) packet+9;
+}
+
+char *
+net_store_length(char *pkg, uint length)
+{
+ uchar *packet=(uchar*) pkg;
+ if (length < 251)
+ {
+ *packet=(uchar) length;
+ return (char*) packet+1;
+ }
+ *packet++=252;
+ int2store(packet,(uint) length);
+ return (char*) packet+2;
+}
+
+/* The following will only be used for short strings < 65K */
+char *
+net_store_data(char *to,const char *from)
+{
+ uint length=strlen(from);
+ to=net_store_length(to,length);
+ memcpy(to,from,length);
+ return to+length;
+}
+
+
+char *
+net_store_data(char *to,int32 from)
+{
+ char buff[20];
+ uint length=(uint) (int10_to_str(from,buff,10)-buff);
+ to=net_store_length(to,length);
+ memcpy(to,buff,length);
+ return to+length;
+}
+
+char *
+net_store_data(char *to,longlong from)
+{
+ char buff[22];
+ uint length=(uint) (longlong10_to_str(from,buff,10)-buff);
+ to=net_store_length(to,length);
+ memcpy(to,buff,length);
+ return to+length;
+}
+
+
+bool net_store_null(String *packet)
+{
+ return packet->append((char) 251);
+}
+
+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))
+ return 1;
+ char *to=(char*) net_store_length((char*) packet->ptr()+packet_length,
+ (ulonglong) length);
+ memcpy(to,from,length);
+ packet->length((uint) (to+length-packet->ptr()));
+ return 0;
+}
+
+/* The following is only used at short, null terminated data */
+
+bool
+net_store_data(String *packet,const char *from)
+{
+ uint length=strlen(from);
+ uint packet_length=packet->length();
+ if (packet_length+5+length > packet->alloced_length() &&
+ packet->realloc(packet_length+5+length))
+ return 1;
+ char *to=(char*) net_store_length((char*) packet->ptr()+packet_length,
+ length);
+ memcpy(to,from,length);
+ packet->length((uint) (to+length-packet->ptr()));
+ return 0;
+}
+
+
+bool
+net_store_data(String *packet,uint32 from)
+{
+ char buff[20];
+ return net_store_data(packet,(char*) buff,
+ (uint) (int10_to_str(from,buff,10)-buff));
+}
+
+bool
+net_store_data(String *packet, longlong from)
+{
+ char buff[22];
+ return net_store_data(packet,(char*) buff,
+ (uint) (longlong10_to_str(from,buff,10)-buff));
+}
+
+bool
+net_store_data(String *packet,struct tm *tmp)
+{
+ char buff[20];
+ sprintf(buff,"%04d-%02d-%02d %02d:%02d:%02d",
+ ((int) (tmp->tm_year+1900)) % 10000,
+ (int) tmp->tm_mon+1,
+ (int) tmp->tm_mday,
+ (int) tmp->tm_hour,
+ (int) tmp->tm_min,
+ (int) tmp->tm_sec);
+ return net_store_data(packet,(char*) buff,19);
+}
+
+bool net_store_data(String* packet, I_List<i_string>* str_list)
+{
+ char buf[256];
+ String tmp(buf, sizeof(buf));
+ tmp.length(0);
+ I_List_iterator<i_string> it(*str_list);
+ i_string* s;
+
+ while((s=it++))
+ {
+ if(tmp.length())
+ tmp.append(',');
+ tmp.append(s->ptr);
+ }
+
+ return net_store_data(packet, (char*)tmp.ptr(), tmp.length());
+}
diff --git a/sql/net_serv.cc b/sql/net_serv.cc
new file mode 100644
index 00000000000..643b5e031cf
--- /dev/null
+++ b/sql/net_serv.cc
@@ -0,0 +1,680 @@
+/* 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 */
+
+/* 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_sys.h>
+#include <m_string.h>
+#include "mysql.h"
+#include "mysqld_error.h"
+#include <signal.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <violite.h>
+
+#ifdef MYSQL_SERVER
+ulong max_allowed_packet=65536;
+extern ulong net_read_timeout,net_write_timeout;
+extern uint test_flags;
+#else
+ulong max_allowed_packet=16*1024*1024L;
+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>
+#endif
+#endif
+#include "mysqld_error.h"
+#ifdef MYSQL_SERVER
+#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
+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))
+static 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 pthread_mutex_t LOCK_bytes_sent , LOCK_bytes_received;
+#else
+#undef statistic_add
+#define statistic_add(A,B,C)
+#endif
+
+/*
+** 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.
+*/
+
+#define TEST_BLOCKING 8
+static int net_write_buff(NET *net,const char *packet,uint 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);
+ 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->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;
+
+ if (vio != 0) /* If real connection */
+ {
+ net->fd = vio_fd(vio); /* For perl DBI/DBD */
+#if defined(MYSQL_SERVER) && !defined(___WIN__) && !defined(__EMX__)
+ if (!(test_flags & TEST_BLOCKING))
+ vio_blocking(vio, FALSE);
+#endif
+ vio_fastsend(vio,TRUE);
+ }
+ return 0;
+}
+
+void net_end(NET *net)
+{
+ my_free((gptr) net->buff,MYF(MY_ALLOW_ZERO_PTR));
+ net->buff=0;
+}
+
+/* Realloc the packet buffer */
+
+static my_bool net_realloc(NET *net, ulong length)
+{
+ uchar *buff;
+ ulong pkt_length;
+ if (length >= max_allowed_packet)
+ {
+ DBUG_PRINT("error",("Packet too large (%lu)", length));
+ net->error=1;
+ net->last_errno=ER_NET_PACKET_TOO_LARGE;
+ return 1;
+ }
+ pkt_length = (length+IO_SIZE-1) & ~(IO_SIZE-1);
+ if (!(buff=(uchar*) my_realloc((char*) net->buff, pkt_length, MYF(MY_WME))))
+ {
+ net->error=1;
+#ifdef MYSQL_SERVER
+ net->last_errno=ER_OUT_OF_RESOURCES;
+#endif
+ return 1;
+ }
+ net->buff=net->write_pos=buff;
+ net->buff_end=buff+(net->max_packet=pkt_length);
+ return 0;
+}
+
+ /* Remove unwanted characters from connection */
+
+void net_clear(NET *net)
+{
+#ifndef EXTRA_DEBUG
+ int count;
+ 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 */
+ {
+ 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);
+ }
+#endif /* EXTRA_DEBUG */
+ net->pkt_nr=0; /* Ready for new command */
+ net->write_pos=net->buff;
+}
+
+ /* Flush write_buffer if not empty. */
+
+int net_flush(NET *net)
+{
+ int error=0;
+ DBUG_ENTER("net_flush");
+ if (net->buff != net->write_pos)
+ {
+ error=net_real_write(net,(char*) net->buff,
+ (uint) (net->write_pos - net->buff));
+ net->write_pos=net->buff;
+ }
+ DBUG_RETURN(error);
+}
+
+
+/*****************************************************************************
+** 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!
+*/
+
+int
+my_net_write(NET *net,const char *packet,ulong len)
+{
+ uchar buff[NET_HEADER_SIZE];
+ int3store(buff,len);
+ buff[3]= (net->compress) ? 0 : (uchar) (net->pkt_nr++);
+ if (net_write_buff(net,(char*) buff,NET_HEADER_SIZE))
+ return 1;
+ return net_write_buff(net,packet,len);
+}
+
+int
+net_write_command(NET *net,uchar command,const char *packet,ulong len)
+{
+ uchar buff[NET_HEADER_SIZE+1];
+ uint length=len+1; /* 1 extra byte for command */
+
+ 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));
+}
+
+
+static int
+net_write_buff(NET *net,const char *packet,uint len)
+{
+ uint left_length=(uint) (net->buff_end - net->write_pos);
+
+ while (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;
+ }
+ memcpy((char*) net->write_pos,packet,len);
+ net->write_pos+=len;
+ return 0;
+}
+
+/* Read and write using timeouts */
+
+int
+net_real_write(NET *net,const char *packet,ulong len)
+{
+ int length;
+ char *pos,*end;
+ thr_alarm_t alarmed;
+#if (!defined(__WIN__) && !defined(__EMX__))
+ ALARM alarm_buff;
+#endif
+ uint retry_count=0;
+ my_bool net_blocking = vio_is_blocking(net->vio);
+ DBUG_ENTER("net_real_write");
+
+ if (net->error == 2)
+ DBUG_RETURN(-1); /* socket can't be used */
+
+ net->reading_or_writing=2;
+#ifdef HAVE_COMPRESS
+ if (net->compress)
+ {
+ 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))))
+ {
+#ifdef MYSQL_SERVER
+ net->last_errno=ER_OUT_OF_RESOURCES;
+ net->error=2;
+#endif
+ net->reading_or_writing=0;
+ DBUG_RETURN(1);
+ }
+ 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++);
+ len+= header_length;
+ packet= (char*) b;
+ }
+#endif /* HAVE_COMPRESS */
+
+ /* DBUG_DUMP("net",packet,len); */
+#ifdef MYSQL_SERVER
+ thr_alarm_init(&alarmed);
+ if (net_blocking)
+ thr_alarm(&alarmed,(uint) net_write_timeout,&alarm_buff);
+#else
+ alarmed=0;
+#endif /* MYSQL_SERVER */
+
+ pos=(char*) packet; end=pos+len;
+ while (pos != end)
+ {
+ if ((int) (length=vio_write(net->vio,pos,(size_t) (end-pos))) <= 0)
+ {
+ my_bool interrupted = vio_should_retry(net->vio);
+#if (!defined(__WIN__) && !defined(__EMX__))
+ if ((interrupted || length==0) && !thr_alarm_in_use(alarmed))
+ {
+ if (!thr_alarm(&alarmed,(uint) net_write_timeout,&alarm_buff))
+ { /* Always true for client */
+ if (!vio_is_blocking(net->vio))
+ {
+ while (vio_blocking(net->vio, TRUE) < 0)
+ {
+ if (vio_should_retry(net->vio) && retry_count++ < 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));
+#endif /* EXTRA_DEBUG */
+ net->error=2; /* Close socket */
+ goto end;
+ }
+ }
+ retry_count=0;
+ continue;
+ }
+ }
+ else
+#endif /* (!defined(__WIN__) && !defined(__EMX__)) */
+ if (thr_alarm_in_use(alarmed) && !thr_got_alarm(alarmed) &&
+ interrupted)
+ {
+ if (retry_count++ < RETRY_COUNT)
+ continue;
+#ifdef EXTRA_DEBUG
+ fprintf(stderr, "%s: write looped, aborting thread\n",
+ my_progname);
+#endif /* EXTRA_DEBUG */
+ }
+#if defined(THREAD_SAFE_CLIENT) && !defined(MYSQL_SERVER)
+ if (vio_errno(net->vio) == EINTR)
+ {
+ DBUG_PRINT("warning",("Interrupted write. Retrying..."));
+ continue;
+ }
+#endif /* defined(THREAD_SAFE_CLIENT) && !defined(MYSQL_SERVER) */
+ net->error=2; /* Close socket */
+#ifdef MYSQL_SERVER
+ net->last_errno= (interrupted ? ER_NET_WRITE_INTERRUPTED :
+ ER_NET_ERROR_ON_WRITE);
+#endif /* MYSQL_SERVER */
+ break;
+ }
+ pos+=length;
+ statistic_add(bytes_sent,length,&LOCK_bytes_sent);
+ }
+#ifndef __WIN__
+ end:
+#endif
+#ifdef HAVE_COMPRESS
+ if (net->compress)
+ my_free((char*) packet,MYF(0));
+#endif
+ if (thr_alarm_in_use(alarmed))
+ {
+ thr_end_alarm(&alarmed);
+ vio_blocking(net->vio, net_blocking);
+ }
+ net->reading_or_writing=0;
+ DBUG_RETURN(((int) (pos != end)));
+}
+
+
+/*****************************************************************************
+** Read something from server/clinet
+*****************************************************************************/
+
+#ifdef MYSQL_SERVER
+
+/*
+ Help function to clear the commuication buffer when we get a too
+ big packet
+*/
+
+static void my_net_skip_rest(NET *net, ulong remain, thr_alarm_t *alarmed)
+{
+ char buff[1024];
+ ALARM alarm_buff;
+ uint retry_count=0;
+ 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
+ }
+ while (remain > 0)
+ {
+ ulong length;
+ if ((int) (length=vio_read(net->vio,(char*) net->buff,remain)) <= 0L)
+ {
+ 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;
+ }
+ remain -=(ulong) length;
+ statistic_add(bytes_received,(ulong) length,&LOCK_bytes_received);
+ }
+}
+#endif /* MYSQL_SERVER */
+
+
+static uint
+my_real_read(NET *net, ulong *complen)
+{
+ uchar *pos;
+ long length;
+ uint i,retry_count=0;
+ ulong len=packet_error;
+ thr_alarm_t alarmed;
+#if (!defined(__WIN__) && !defined(__EMX__)) || defined(MYSQL_SERVER)
+ 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);
+ *complen = 0;
+
+ net->reading_or_writing=1;
+ thr_alarm_init(&alarmed);
+#ifdef MYSQL_SERVER
+ if (net_blocking)
+ thr_alarm(&alarmed,net->timeout,&alarm_buff);
+#endif /* MYSQL_SERVER */
+
+ pos = net->buff + net->where_b; /* net->packet -4 */
+ for (i=0 ; i < 2 ; i++)
+ {
+ while (remain > 0)
+ {
+ /* First read is done with non blocking mode */
+ if ((int) (length=vio_read(net->vio,(char*) pos,remain)) <= 0L)
+ {
+ my_bool interrupted = vio_should_retry(net->vio);
+
+ DBUG_PRINT("info",("vio_read returned %d, errno: %d",
+ length, vio_errno(net->vio)));
+#if (!defined(__WIN__) && !defined(__EMX__)) || defined(MYSQL_SERVER)
+ /*
+ We got an error that there was no data on the socket. We now set up
+ an alarm to not 'read forever', change the socket to non blocking
+ mode and try again
+ */
+ if ((interrupted || length == 0) && !thr_alarm_in_use(alarmed))
+ {
+ if (!thr_alarm(&alarmed,net->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)));
+#ifdef EXTRA_DEBUG
+ 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 */
+#ifdef MYSQL_SERVER
+ net->last_errno=ER_NET_FCNTL_ERROR;
+#endif
+ goto end;
+ }
+ }
+ retry_count=0;
+ continue;
+ }
+ }
+#endif /* (!defined(__WIN__) && !defined(__EMX__)) || defined(MYSQL_SERVER) */
+ if (thr_alarm_in_use(alarmed) && !thr_got_alarm(alarmed) &&
+ interrupted)
+ { /* Probably in MIT threads */
+ if (retry_count++ < RETRY_COUNT)
+ continue;
+#ifdef EXTRA_DEBUG
+ fprintf(stderr, "%s: read looped with error %d, aborting thread\n",
+ my_progname,vio_errno(net->vio));
+#endif /* EXTRA_DEBUG */
+ }
+#if defined(THREAD_SAFE_CLIENT) && !defined(MYSQL_SERVER)
+ if (vio_should_retry(net->vio))
+ {
+ DBUG_PRINT("warning",("Interrupted read. Retrying..."));
+ continue;
+ }
+#endif
+ DBUG_PRINT("error",("Couldn't read packet: remain: %d errno: %d length: %d alarmed: %d", remain,vio_errno(net->vio),length,alarmed));
+ len= packet_error;
+ net->error=2; /* Close socket */
+#ifdef MYSQL_SERVER
+ net->last_errno= (interrupted ? ER_NET_READ_INTERRUPTED :
+ ER_NET_READ_ERROR);
+#endif
+ goto end;
+ }
+ remain -= (ulong) length;
+ pos+= (ulong) length;
+ statistic_add(bytes_received,(ulong) length,&LOCK_bytes_received);
+ }
+ if (i == 0)
+ { /* First parts is packet length */
+ ulong helping;
+ if (net->buff[net->where_b + 3] != (uchar) net->pkt_nr)
+ {
+ if (net->buff[net->where_b] != (uchar) 255)
+ {
+ DBUG_PRINT("error",
+ ("Packets out of order (Found: %d, expected %d)",
+ (int) net->buff[net->where_b + 3],
+ (uint) (uchar) net->pkt_nr));
+#ifdef EXTRA_DEBUG
+ fprintf(stderr,"Packets out of order (Found: %d, expected %d)\n",
+ (int) net->buff[net->where_b + 3],
+ (uint) (uchar) net->pkt_nr);
+#endif
+ }
+ len= packet_error;
+#ifdef MYSQL_SERVER
+ net->last_errno=ER_NET_PACKETS_OUT_OF_ORDER;
+#endif
+ goto end;
+ }
+ net->pkt_nr++;
+#ifdef HAVE_COMPRESS
+ if (net->compress)
+ {
+ /* complen is > 0 if package is really compressed */
+ *complen=uint3korr(&(net->buff[net->where_b + NET_HEADER_SIZE]));
+ }
+#endif
+
+ len=uint3korr(net->buff+net->where_b);
+ 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))
+ {
+#ifdef MYSQL_SERVER
+ if (i == 1)
+ my_net_skip_rest(net, len, &alarmed);
+#endif
+ len= packet_error; /* Return error */
+ goto end;
+ }
+ }
+ pos=net->buff + net->where_b;
+ remain = len;
+ }
+ }
+
+end:
+ if (thr_alarm_in_use(alarmed))
+ {
+ thr_end_alarm(&alarmed);
+ vio_blocking(net->vio, net_blocking);
+ }
+ net->reading_or_writing=0;
+ return(len);
+}
+
+uint
+my_net_read(NET *net)
+{
+ ulong len,complen;
+
+#ifdef HAVE_COMPRESS
+ if (!net->compress)
+ {
+#endif
+ len = my_real_read (net,&complen);
+ 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 (;;)
+ {
+ if (net->remain_in_buf)
+ {
+ uchar *pos = net->buff + net->buf_length - net->remain_in_buf;
+ if (net->remain_in_buf >= 4)
+ {
+ net->length = uint3korr(pos);
+ if (net->length <= net->remain_in_buf - 4)
+ {
+ /* 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 */
+ }
+ }
+ /* Move data down to read next data packet after current one */
+ if (net->buf_length != net->remain_in_buf)
+ {
+ memmove(net->buff,pos,net->remain_in_buf);
+ net->buf_length=net->remain_in_buf;
+ }
+ 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 */
+#ifdef MYSQL_SERVER
+ net->last_errno=ER_NET_UNCOMPRESS_ERROR;
+#endif
+ break;
+ }
+ net->buf_length+=len;
+ net->remain_in_buf+=len;
+ }
+ if (len != packet_error)
+ {
+ net->save_char= net->read_pos[len]; /* Must be saved */
+ net->read_pos[len]=0; /* Safeguard for mysql_use_result */
+ }
+ return len;
+#endif
+}
diff --git a/sql/nt_servc.cc b/sql/nt_servc.cc
new file mode 100644
index 00000000000..5884300fe95
--- /dev/null
+++ b/sql/nt_servc.cc
@@ -0,0 +1,400 @@
+/* ------------------------------------------------------------------------
+ Windows NT Service class library
+ Copyright Abandoned 1998 Irena Pancirov - Irnet Snc
+ This file is public domain and comes with NO WARRANTY of any kind
+ -------------------------------------------------------------------------- */
+#include <windows.h>
+#include <process.h>
+#include "nt_servc.h"
+
+
+static NTService *pService;
+
+/* ------------------------------------------------------------------------
+
+ -------------------------------------------------------------------------- */
+NTService::NTService()
+{
+
+ bOsNT = FALSE;
+ //service variables
+ ServiceName = NULL;
+ hExitEvent = 0;
+ bPause = FALSE;
+ bRunning = FALSE;
+ hThreadHandle = 0;
+ fpServiceThread = NULL;
+
+ //time-out variables
+ nStartTimeOut = 15000;
+ nStopTimeOut = 15000;
+ nPauseTimeOut = 5000;
+ nResumeTimeOut = 5000;
+
+ //install variables
+ dwDesiredAccess = SERVICE_ALL_ACCESS;
+ dwServiceType = SERVICE_WIN32_OWN_PROCESS;
+ dwStartType = SERVICE_AUTO_START;
+ dwErrorControl = SERVICE_ERROR_NORMAL;
+ szLoadOrderGroup = NULL;
+ lpdwTagID = NULL;
+ szDependencies = NULL;
+
+ my_argc = 0;
+ my_argv = NULL;
+ hShutdownEvent = 0;
+ nError = 0;
+ dwState = 0;
+}
+
+/* ------------------------------------------------------------------------
+
+ -------------------------------------------------------------------------- */
+NTService::~NTService()
+{
+ if(ServiceName != NULL) delete[] ServiceName;
+}
+/* ------------------------------------------------------------------------
+
+ -------------------------------------------------------------------------- */
+BOOL NTService::GetOS()
+{
+ bOsNT = FALSE;
+ memset(&osVer, 0, sizeof(OSVERSIONINFO));
+ osVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ if (GetVersionEx(&osVer))
+ {
+ if (osVer.dwPlatformId == VER_PLATFORM_WIN32_NT)
+ bOsNT = TRUE;
+ }
+ return bOsNT;
+}
+
+/* ------------------------------------------------------------------------
+ Init() Registers the main service thread with the service manager
+
+ ServiceThread - pointer to the main programs entry function
+ when the service is started
+ -------------------------------------------------------------------------- */
+long NTService::Init(LPCSTR szInternName,void *ServiceThread)
+{
+
+ pService = this;
+
+ fpServiceThread = (THREAD_FC)ServiceThread;
+ ServiceName = new char[lstrlen(szInternName)+1];
+ lstrcpy(ServiceName,szInternName);
+
+ SERVICE_TABLE_ENTRY stb[] =
+ {
+ { (char *)szInternName,(LPSERVICE_MAIN_FUNCTION) ServiceMain} ,
+ { NULL, NULL }
+ };
+
+ return StartServiceCtrlDispatcher(stb); //register with the Service Manager
+}
+/* ------------------------------------------------------------------------
+ Install() - Installs the service with Service manager
+ nError values:
+ 0 success
+ 1 Can't open the Service manager
+ 2 Failed to create service
+ -------------------------------------------------------------------------- */
+BOOL NTService::Install(LPCSTR szInternName,LPCSTR szDisplayName,
+ LPCSTR szFullPath, LPCSTR szAccountName,LPCSTR szPassword)
+{
+ SC_HANDLE newService, scm;
+
+ nError=0;
+
+ // open a connection to the SCM
+ scm = OpenSCManager(0, 0,SC_MANAGER_CREATE_SERVICE);
+ if(scm) // Install the new service
+ { newService = CreateService(
+ scm,
+ szInternName,
+ szDisplayName,
+ dwDesiredAccess, //default: SERVICE_ALL_ACCESS
+ dwServiceType, //default: SERVICE_WIN32_OWN_PROCESS
+ dwStartType, //default: SERVICE_AUTOSTART
+ dwErrorControl, //default: SERVICE_ERROR_NORMAL
+ szFullPath, //exec full path
+ szLoadOrderGroup, //default: NULL
+ lpdwTagID, //default: NULL
+ szDependencies, //default: NULL
+ szAccountName, //default: NULL
+ szPassword); //default: NULL
+
+ if (newService) CloseServiceHandle(newService); // clean up
+ else nError=2;
+
+ // clean up
+ CloseServiceHandle(scm);
+ }
+ else nError=1;
+
+ return (!nError);
+}
+/* ------------------------------------------------------------------------
+ Remove() - Removes the service
+ nError values:
+ 0 success
+ 1 Can't open the Service manager
+ 2 Failed to locate service
+ 3 Failed to delete service
+ -------------------------------------------------------------------------- */
+BOOL NTService::Remove(LPCSTR szInternName)
+{
+
+ SC_HANDLE service, scm;
+
+ nError=0;
+
+ // open a connection to the SCM
+ scm = OpenSCManager(0, 0,SC_MANAGER_CREATE_SERVICE);
+
+ if (scm)
+ {
+ //open the service
+ service = OpenService(scm,szInternName, DELETE );
+ if(service)
+ {
+ if(!DeleteService(service)) nError=3;
+ CloseServiceHandle(service);
+ }
+ else
+ {
+ //MessageBox(NULL,"Can't find the service","Remove Error",MB_OK|MB_ICONHAND);
+ nError=2;
+ }
+ CloseServiceHandle(scm);
+ }
+ else nError=1;
+
+ return (!nError);
+}
+
+/* ------------------------------------------------------------------------
+ Stop() - this function should be called before the app. exits to stop
+ the service
+ -------------------------------------------------------------------------- */
+void NTService::Stop(void)
+{
+ SetStatus(SERVICE_STOP_PENDING,NO_ERROR, 0, 1, 60000);
+ StopService();
+ SetStatus(SERVICE_STOPPED, NO_ERROR, 0, 1, 1000);
+}
+
+/* ------------------------------------------------------------------------
+ ServiceMain() - This is the function that is called from the
+ service manager to start the service
+ -------------------------------------------------------------------------- */
+void NTService::ServiceMain(DWORD argc, LPTSTR *argv)
+{
+
+ // registration function
+ pService->hServiceStatusHandle =
+ RegisterServiceCtrlHandler(pService->ServiceName,
+ (LPHANDLER_FUNCTION )NTService::ServiceCtrlHandler);
+
+ if(!pService->hServiceStatusHandle)
+ {
+ pService->Exit(GetLastError());
+ return;
+ }
+
+ // notify SCM of progress
+ if(!pService->SetStatus(SERVICE_START_PENDING,NO_ERROR, 0, 1, 8000))
+ {
+ pService->Exit(GetLastError());
+ return;
+ }
+
+ // create the exit event
+ pService->hExitEvent = CreateEvent (0, TRUE, FALSE,0);
+ if(!pService->hExitEvent)
+ {
+ pService->Exit(GetLastError());
+ return;
+ }
+
+ if(!pService->SetStatus(SERVICE_START_PENDING,NO_ERROR, 0, 3, pService->nStartTimeOut))
+ {
+ pService->Exit(GetLastError());
+ return;
+ }
+
+ // save start arguments
+ pService->my_argc=argc;
+ pService->my_argv=argv;
+
+ // start the service
+ if(!pService->StartService())
+ {
+ pService->Exit(GetLastError());
+ return;
+ }
+
+ // the service is now running.
+ if(!pService->SetStatus(SERVICE_RUNNING,NO_ERROR, 0, 0, 0))
+ {
+ pService->Exit(GetLastError());
+ return;
+ }
+
+ // wait for exit event
+ WaitForSingleObject (pService->hExitEvent, INFINITE);
+
+ // wait for thread to exit
+ WaitForSingleObject (pService->hThreadHandle, 30000);
+
+ pService->Exit(0);
+}
+
+/* ------------------------------------------------------------------------
+ StartService() - starts the appliaction thread
+ -------------------------------------------------------------------------- */
+BOOL NTService::StartService()
+{
+
+ // Start the real service's thread (application)
+ hThreadHandle = (HANDLE) _beginthread((THREAD_FC)fpServiceThread,0,(void *)this);
+
+ if (hThreadHandle==0) return FALSE;
+
+ bRunning = TRUE;
+ return TRUE;
+}
+/* ------------------------------------------------------------------------
+
+ -------------------------------------------------------------------------- */
+void NTService::StopService()
+{
+ bRunning=FALSE;
+
+ // Set the event for application
+ if(hShutdownEvent)
+ SetEvent(hShutdownEvent);
+
+ // Set the event for ServiceMain
+ SetEvent(hExitEvent);
+}
+/* ------------------------------------------------------------------------
+
+ -------------------------------------------------------------------------- */
+void NTService::PauseService()
+{
+ bPause = TRUE;
+ SuspendThread(hThreadHandle);
+}
+/* ------------------------------------------------------------------------
+
+ -------------------------------------------------------------------------- */
+void NTService::ResumeService()
+{
+ bPause=FALSE;
+ ResumeThread(hThreadHandle);
+}
+/* ------------------------------------------------------------------------
+
+ -------------------------------------------------------------------------- */
+BOOL NTService::SetStatus (DWORD dwCurrentState,DWORD dwWin32ExitCode,
+ DWORD dwServiceSpecificExitCode,DWORD dwCheckPoint,DWORD dwWaitHint)
+{
+ BOOL bRet;
+ SERVICE_STATUS serviceStatus;
+
+ dwState=dwCurrentState;
+
+ serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
+ serviceStatus.dwCurrentState = dwCurrentState;
+
+ if (dwCurrentState == SERVICE_START_PENDING)
+ serviceStatus.dwControlsAccepted = 0; //don't accept conrol events
+ else
+ serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
+ SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN;
+
+ // if a specific exit code is defined,set up the win32 exit code properly
+ if (dwServiceSpecificExitCode == 0)
+ serviceStatus.dwWin32ExitCode = dwWin32ExitCode;
+ else
+ serviceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
+
+ serviceStatus.dwServiceSpecificExitCode = dwServiceSpecificExitCode;
+
+ serviceStatus.dwCheckPoint = dwCheckPoint;
+ serviceStatus.dwWaitHint = dwWaitHint;
+
+ // Pass the status to the Service Manager
+ bRet=SetServiceStatus (hServiceStatusHandle, &serviceStatus);
+
+ if(!bRet) StopService();
+
+ return bRet;
+}
+/* ------------------------------------------------------------------------
+
+ -------------------------------------------------------------------------- */
+void NTService::ServiceCtrlHandler(DWORD ctrlCode)
+{
+
+ DWORD dwState = 0;
+
+ if(!pService) return;
+
+ dwState=pService->dwState; // get current state
+
+ switch(ctrlCode)
+ {
+
+ /*********** do we need this ? *******************************
+ case SERVICE_CONTROL_PAUSE:
+ if (pService->bRunning && ! pService->bPause)
+ {
+ dwState = SERVICE_PAUSED;
+ pService->SetStatus(SERVICE_PAUSE_PENDING,NO_ERROR, 0, 1, pService->nPauseTimeOut);
+ pService->PauseService();
+ }
+ break;
+
+ case SERVICE_CONTROL_CONTINUE:
+ if (pService->bRunning && pService->bPause)
+ {
+ dwState = SERVICE_RUNNING;
+ pService->SetStatus(SERVICE_CONTINUE_PENDING,NO_ERROR, 0, 1, pService->nResumeTimeOut);
+ pService->ResumeService();
+ }
+ break;
+ ****************************************************************/
+
+ case SERVICE_CONTROL_SHUTDOWN:
+ case SERVICE_CONTROL_STOP:
+ dwState = SERVICE_STOP_PENDING;
+ pService->SetStatus(SERVICE_STOP_PENDING,NO_ERROR, 0, 1, pService->nStopTimeOut);
+ pService->StopService();
+ break;
+
+ default:
+ pService->SetStatus(dwState, NO_ERROR,0, 0, 0);
+ break;
+ }
+ //pService->SetStatus(dwState, NO_ERROR,0, 0, 0);
+}
+/* ------------------------------------------------------------------------
+
+ -------------------------------------------------------------------------- */
+void NTService::Exit(DWORD error)
+{
+ if (hExitEvent) CloseHandle(hExitEvent);
+
+ // Send a message to the scm to tell that we stop
+ if (hServiceStatusHandle)
+ SetStatus(SERVICE_STOPPED, error,0, 0, 0);
+
+ // If the thread has started kill it ???
+ // if (hThreadHandle) CloseHandle(hThreadHandle);
+
+}
+
+/* ------------------------- the end -------------------------------------- */
diff --git a/sql/nt_servc.h b/sql/nt_servc.h
new file mode 100644
index 00000000000..5fda96dc4d8
--- /dev/null
+++ b/sql/nt_servc.h
@@ -0,0 +1,80 @@
+/* ------------------------------------------------------------------------
+ Windows NT Service class library
+ Copyright Abandoned 1998 Irena Pancirov - Irnet Snc
+ This file is public domain and comes with NO WARRANTY of any kind
+ -------------------------------------------------------------------------- */
+
+// main application thread
+typedef void (*THREAD_FC)(void *);
+
+class NTService
+{
+ public:
+ NTService();
+ ~NTService();
+
+ BOOL bOsNT; // true if OS is NT, false for Win95
+ //install optinos
+ DWORD dwDesiredAccess;
+ DWORD dwServiceType;
+ DWORD dwStartType;
+ DWORD dwErrorControl;
+
+ LPSTR szLoadOrderGroup;
+ LPDWORD lpdwTagID;
+ LPSTR szDependencies;
+ OSVERSIONINFO osVer;
+
+ // time-out (in milisec)
+ int nStartTimeOut;
+ int nStopTimeOut;
+ int nPauseTimeOut;
+ int nResumeTimeOut;
+
+ //
+ DWORD my_argc;
+ LPTSTR *my_argv;
+ HANDLE hShutdownEvent;
+ int nError;
+ DWORD dwState;
+
+ BOOL GetOS(); // returns TRUE if WinNT
+ BOOL IsNT() { return bOsNT;}
+ //init service entry point
+ long Init(LPCSTR szInternName,void *ServiceThread);
+
+ //application shutdown event
+ void SetShutdownEvent(HANDLE hEvent){ hShutdownEvent=hEvent; }
+
+
+ //service install / un-install
+ BOOL Install(LPCSTR szInternName,LPCSTR szDisplayName,LPCSTR szFullPath,
+ LPCSTR szAccountName=NULL,LPCSTR szPassword=NULL);
+ BOOL Remove(LPCSTR szInternName);
+
+ void Stop(void); //to be called from app. to stop service
+
+ protected:
+ LPSTR ServiceName;
+ HANDLE hExitEvent;
+ SERVICE_STATUS_HANDLE hServiceStatusHandle;
+ BOOL bPause;
+ BOOL bRunning;
+ HANDLE hThreadHandle;
+ THREAD_FC fpServiceThread;
+
+ void PauseService();
+ void ResumeService();
+ void StopService();
+ BOOL StartService();
+
+ static void ServiceMain(DWORD argc, LPTSTR *argv);
+ static void ServiceCtrlHandler (DWORD ctrlCode);
+
+ void Exit(DWORD error);
+ BOOL SetStatus (DWORD dwCurrentState,DWORD dwWin32ExitCode,
+ DWORD dwServiceSpecificExitCode,
+ DWORD dwCheckPoint,DWORD dwWaitHint);
+
+};
+/* ------------------------- the end -------------------------------------- */
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
new file mode 100644
index 00000000000..4f1abf9b685
--- /dev/null
+++ b/sql/opt_range.cc
@@ -0,0 +1,2543 @@
+/* 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 __GNUC__
+#pragma implementation // gcc: Class implementation
+#endif
+
+#include "mysql_priv.h"
+#include <m_ctype.h>
+#include <nisam.h>
+#include "sql_select.h"
+
+
+#ifndef EXTRA_DEBUG
+#define test_rb_tree(A,B) {}
+#define test_use_count(A) {}
+#endif
+
+
+static int sel_cmp(Field *f,char *a,char *b,uint8 a_flag,uint8 b_flag);
+
+static char is_null_string[2]= {1,0};
+
+class SEL_ARG :public Sql_alloc
+{
+public:
+ uint8 min_flag,max_flag,maybe_flag;
+ uint8 part; // Which key part
+ uint8 maybe_null;
+ uint16 elements; // Elements in tree
+ ulong use_count; // use of this sub_tree
+ Field *field;
+ char *min_value,*max_value; // Pointer to range
+
+ SEL_ARG *left,*right,*next,*prev,*parent,*next_key_part;
+ enum leaf_color { BLACK,RED } color;
+ enum Type { IMPOSSIBLE, MAYBE, MAYBE_KEY, KEY_RANGE } type;
+
+ SEL_ARG() {}
+ SEL_ARG(SEL_ARG &);
+ SEL_ARG(Field *,const char *,const char *);
+ 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) {}
+ inline bool is_same(SEL_ARG *arg)
+ {
+ if (type != arg->type)
+ return 0;
+ if (type != KEY_RANGE)
+ return 1;
+ return cmp_min_to_min(arg) == 0 && cmp_max_to_max(arg) == 0;
+ }
+ inline void merge_flags(SEL_ARG *arg) { maybe_flag|=arg->maybe_flag; }
+ inline void maybe_smaller() { maybe_flag=1; }
+ inline int cmp_min_to_min(SEL_ARG* arg)
+ {
+ return sel_cmp(field,min_value, arg->min_value, min_flag, arg->min_flag);
+ }
+ inline int cmp_min_to_max(SEL_ARG* arg)
+ {
+ return sel_cmp(field,min_value, arg->max_value, min_flag, arg->max_flag);
+ }
+ inline int cmp_max_to_max(SEL_ARG* arg)
+ {
+ return sel_cmp(field,max_value, arg->max_value, max_flag, arg->max_flag);
+ }
+ inline int cmp_max_to_min(SEL_ARG* arg)
+ {
+ return sel_cmp(field,max_value, arg->min_value, max_flag, arg->min_flag);
+ }
+ SEL_ARG *clone_and(SEL_ARG* arg)
+ { // Get overlapping range
+ char *new_min,*new_max;
+ uint8 flag_min,flag_max;
+ if (cmp_min_to_min(arg) >= 0)
+ {
+ new_min=min_value; flag_min=min_flag;
+ }
+ else
+ {
+ new_min=arg->min_value; flag_min=arg->min_flag; /* purecov: deadcode */
+ }
+ if (cmp_max_to_max(arg) <= 0)
+ {
+ new_max=max_value; flag_max=max_flag;
+ }
+ else
+ {
+ new_max=arg->max_value; flag_max=arg->max_flag;
+ }
+ return new SEL_ARG(field, part, new_min, new_max, flag_min, flag_max,
+ test(maybe_flag && arg->maybe_flag));
+ }
+ SEL_ARG *clone_first(SEL_ARG *arg)
+ { // min <= X < arg->min
+ return new SEL_ARG(field,part, min_value, arg->min_value,
+ min_flag, arg->min_flag & NEAR_MIN ? 0 : NEAR_MAX,
+ maybe_flag | arg->maybe_flag);
+ }
+ SEL_ARG *clone_last(SEL_ARG *arg)
+ { // min <= X <= key_max
+ return new SEL_ARG(field, part, min_value, arg->max_value,
+ min_flag, arg->max_flag, maybe_flag | arg->maybe_flag);
+ }
+ SEL_ARG *clone(SEL_ARG *new_parent,SEL_ARG **next);
+
+ bool copy_min(SEL_ARG* arg)
+ { // Get overlapping range
+ if (cmp_min_to_min(arg) > 0)
+ {
+ min_value=arg->min_value; min_flag=arg->min_flag;
+ if ((max_flag & (NO_MAX_RANGE | NO_MIN_RANGE)) ==
+ (NO_MAX_RANGE | NO_MIN_RANGE))
+ return 1; // Full range
+ }
+ maybe_flag|=arg->maybe_flag;
+ return 0;
+ }
+ bool copy_max(SEL_ARG* arg)
+ { // Get overlapping range
+ if (cmp_max_to_max(arg) <= 0)
+ {
+ max_value=arg->max_value; max_flag=arg->max_flag;
+ if ((max_flag & (NO_MAX_RANGE | NO_MIN_RANGE)) ==
+ (NO_MAX_RANGE | NO_MIN_RANGE))
+ return 1; // Full range
+ }
+ maybe_flag|=arg->maybe_flag;
+ return 0;
+ }
+
+ void copy_min_to_min(SEL_ARG *arg)
+ {
+ min_value=arg->min_value; min_flag=arg->min_flag;
+ }
+ void copy_min_to_max(SEL_ARG *arg)
+ {
+ max_value=arg->min_value;
+ max_flag=arg->min_flag & NEAR_MIN ? 0 : NEAR_MAX;
+ }
+ void copy_max_to_min(SEL_ARG *arg)
+ {
+ min_value=arg->max_value;
+ min_flag=arg->max_flag & NEAR_MAX ? 0 : NEAR_MIN;
+ }
+ void store(uint length,char **min_key,uint min_key_flag,
+ char **max_key, uint max_key_flag)
+ {
+ if (!(min_flag & NO_MIN_RANGE) &&
+ !(min_key_flag & (NO_MIN_RANGE | NEAR_MIN)))
+ {
+ if (maybe_null && *min_value)
+ {
+ **min_key=1;
+ bzero(*min_key+1,length);
+ }
+ else
+ memcpy(*min_key,min_value,length+(int) maybe_null);
+ (*min_key)+= length+(int) maybe_null;
+ }
+ if (!(max_flag & NO_MAX_RANGE) &&
+ !(max_key_flag & (NO_MAX_RANGE | NEAR_MAX)))
+ {
+ if (maybe_null && *max_value)
+ {
+ **max_key=1;
+ bzero(*max_key+1,length);
+ }
+ else
+ memcpy(*max_key,max_value,length+(int) maybe_null);
+ (*max_key)+= length+(int) maybe_null;
+ }
+ }
+
+ void store_min_key(KEY_PART *key,char **range_key, uint *range_key_flag)
+ {
+ SEL_ARG *key_tree= first();
+ key_tree->store(key[key_tree->part].part_length,
+ range_key,*range_key_flag,range_key,NO_MAX_RANGE);
+ *range_key_flag|= key_tree->min_flag;
+ if (key_tree->next_key_part &&
+ key_tree->next_key_part->part == key_tree->part+1 &&
+ !(*range_key_flag & (NO_MIN_RANGE | NEAR_MIN)) &&
+ key_tree->next_key_part->type == SEL_ARG::KEY_RANGE)
+ key_tree->next_key_part->store_min_key(key,range_key, range_key_flag);
+ }
+
+ void store_max_key(KEY_PART *key,char **range_key, uint *range_key_flag)
+ {
+ SEL_ARG *key_tree= last();
+ key_tree->store(key[key_tree->part].part_length,
+ range_key, NO_MIN_RANGE, range_key,*range_key_flag);
+ (*range_key_flag)|= key_tree->max_flag;
+ if (key_tree->next_key_part &&
+ key_tree->next_key_part->part == key_tree->part+1 &&
+ !(*range_key_flag & (NO_MAX_RANGE | NEAR_MAX)) &&
+ key_tree->next_key_part->type == SEL_ARG::KEY_RANGE)
+ key_tree->next_key_part->store_max_key(key,range_key, range_key_flag);
+ }
+
+ SEL_ARG *insert(SEL_ARG *key);
+ SEL_ARG *tree_delete(SEL_ARG *key);
+ SEL_ARG *find_range(SEL_ARG *key);
+ SEL_ARG *rb_insert(SEL_ARG *leaf);
+ friend SEL_ARG *rb_delete_fixup(SEL_ARG *root,SEL_ARG *key, SEL_ARG *par);
+#ifdef EXTRA_DEBUG
+ friend int test_rb_tree(SEL_ARG *element,SEL_ARG *parent);
+ void test_use_count(SEL_ARG *root);
+#endif
+ SEL_ARG *first();
+ SEL_ARG *last();
+ void make_root();
+ inline bool simple_key()
+ {
+ return !next_key_part && elements == 1;
+ }
+ void increment_use_count(long count)
+ {
+ if (next_key_part)
+ {
+ next_key_part->use_count+=count;
+ count*= (next_key_part->use_count-count);
+ for (SEL_ARG *pos=next_key_part->first(); pos ; pos=pos->next)
+ if (pos->next_key_part)
+ pos->increment_use_count(count);
+ }
+ }
+ void free_tree()
+ {
+ for (SEL_ARG *pos=first(); pos ; pos=pos->next)
+ if (pos->next_key_part)
+ {
+ pos->next_key_part->use_count--;
+ pos->next_key_part->free_tree();
+ }
+ }
+
+ inline SEL_ARG **parent_ptr()
+ {
+ return parent->left == this ? &parent->left : &parent->right;
+ }
+ SEL_ARG *clone_tree();
+};
+
+
+class SEL_TREE :public Sql_alloc
+{
+public:
+ enum Type { IMPOSSIBLE, ALWAYS, MAYBE, KEY, KEY_SMALLER } type;
+ SEL_TREE(enum Type type_arg) :type(type_arg) {}
+ SEL_TREE() :type(KEY) { bzero((char*) keys,sizeof(keys));}
+ SEL_ARG *keys[MAX_KEY];
+};
+
+
+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];
+ uint real_keynr[MAX_KEY];
+ char min_key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH],
+ max_key[MAX_KEY_LENGTH+MAX_FIELD_WIDTH];
+} 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,
+ 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,
+ char max_sort_char,uint *min_length,uint *max_length);
+static SEL_TREE *get_mm_tree(PARAM *param,COND *cond);
+static ha_rows check_quick_select(PARAM *param,uint index,SEL_ARG *key_tree);
+static ha_rows check_quick_keys(PARAM *param,uint index,SEL_ARG *key_tree,
+ char *min_key,uint min_key_flag,
+ char *max_key, uint max_key_flag);
+
+static QUICK_SELECT *get_quick_select(PARAM *param,uint index,
+ SEL_ARG *key_tree);
+#ifndef DBUG_OFF
+static void print_quick(QUICK_SELECT *quick,key_map needed_reg);
+#endif
+static SEL_TREE *tree_and(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2);
+static SEL_TREE *tree_or(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2);
+static SEL_ARG *sel_add(SEL_ARG *key1,SEL_ARG *key2);
+static SEL_ARG *key_or(SEL_ARG *key1,SEL_ARG *key2);
+static SEL_ARG *key_and(SEL_ARG *key1,SEL_ARG *key2,uint clone_flag);
+static bool get_range(SEL_ARG **e1,SEL_ARG **e2,SEL_ARG *root1);
+static bool get_quick_keys(PARAM *param,QUICK_SELECT *quick,KEY_PART *key,
+ SEL_ARG *key_tree,char *min_key,uint min_key_flag,
+ char *max_key,uint max_key_flag);
+static bool eq_tree(SEL_ARG* a,SEL_ARG *b);
+
+static SEL_ARG null_element(SEL_ARG::IMPOSSIBLE);
+
+
+/***************************************************************************
+** Basic functions for SQL_SELECT and QUICK_SELECT
+***************************************************************************/
+
+ /* make a select from mysql info
+ Error is set as following:
+ 0 = ok
+ 1 = Got some error (out of memory?)
+ */
+
+SQL_SELECT *make_select(TABLE *head, table_map const_tables,
+ table_map read_tables, COND *conds, int *error)
+{
+ SQL_SELECT *select;
+ DBUG_ENTER("make_select");
+
+ *error=0;
+ if (!conds)
+ DBUG_RETURN(0);
+ if (!(select= new SQL_SELECT))
+ {
+ *error= 1;
+ DBUG_RETURN(0); /* purecov: inspected */
+ }
+ select->read_tables=read_tables;
+ select->const_tables=const_tables;
+ select->head=head;
+ select->cond=conds;
+
+ if (head->io_cache)
+ {
+ select->file= *head->io_cache;
+ select->records=(ha_rows) (select->file.end_of_file/
+ head->file->ref_length);
+ my_free((gptr) (head->io_cache),MYF(0));
+ head->io_cache=0;
+ }
+ DBUG_RETURN(select);
+}
+
+
+SQL_SELECT::SQL_SELECT() :quick(0),cond(0),free_cond(0)
+{
+ quick_keys=0; needed_reg=0;
+ my_b_clear(&file);
+}
+
+
+SQL_SELECT::~SQL_SELECT()
+{
+ delete quick;
+ if (free_cond)
+ delete cond;
+ close_cached_file(&file);
+}
+
+#undef index // Fix or 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),
+ it(ranges),range(0)
+{
+ if (!no_alloc)
+ {
+ init_sql_alloc(&alloc,1024); // Allocates everything here
+ my_pthread_setspecific_ptr(THR_MALLOC,&alloc);
+ }
+ else
+ bzero((char*) &alloc,sizeof(alloc));
+ file=head->file;
+ error=file->index_init(index);
+ record=head->record[0];
+}
+
+QUICK_SELECT::~QUICK_SELECT()
+{
+ file->index_end();
+ free_root(&alloc);
+}
+
+
+QUICK_RANGE::QUICK_RANGE()
+ :min_key(0),max_key(0),min_length(0),max_length(0),
+ flag(NO_MIN_RANGE | NO_MAX_RANGE)
+{}
+
+
+SEL_ARG::SEL_ARG(SEL_ARG &arg) :Sql_alloc()
+{
+ type=arg.type;
+ min_flag=arg.min_flag;
+ max_flag=arg.max_flag;
+ maybe_flag=arg.maybe_flag;
+ maybe_null=arg.maybe_null;
+ part=arg.part;
+ field=arg.field;
+ min_value=arg.min_value;
+ max_value=arg.max_value;
+ next_key_part=arg.next_key_part;
+ use_count=1; elements=1;
+}
+
+
+inline void SEL_ARG::make_root()
+{
+ left=right= &null_element;
+ color=BLACK;
+ next=prev=0;
+ use_count=0; elements=1;
+}
+
+SEL_ARG::SEL_ARG(Field *f,const char *min_value_arg,const char *max_value_arg)
+ :min_flag(0), max_flag(0), maybe_flag(0), maybe_null(f->real_maybe_null()),
+ elements(1), use_count(1), field(f), min_value((char*) min_value_arg),
+ max_value((char*) max_value_arg), next(0),prev(0),
+ next_key_part(0),color(BLACK),type(KEY_RANGE)
+{
+ left=right= &null_element;
+}
+
+SEL_ARG::SEL_ARG(Field *field_,uint8 part_,char *min_value_,char *max_value_,
+ uint8 min_flag_,uint8 max_flag_,uint8 maybe_flag_)
+ :min_flag(min_flag_),max_flag(max_flag_),maybe_flag(maybe_flag_),
+ part(part_),maybe_null(field_->real_maybe_null()), elements(1),use_count(1),
+ field(field_), min_value(min_value_), max_value(max_value_),
+ next(0),prev(0),next_key_part(0),color(BLACK),type(KEY_RANGE)
+{
+ left=right= &null_element;
+}
+
+SEL_ARG *SEL_ARG::clone(SEL_ARG *new_parent,SEL_ARG **next_arg)
+{
+ SEL_ARG *tmp;
+ if (type != KEY_RANGE)
+ {
+ tmp=new SEL_ARG(type);
+ tmp->prev= *next_arg; // Link into next/prev chain
+ (*next_arg)->next=tmp;
+ (*next_arg)= tmp;
+ }
+ else
+ {
+ tmp=new SEL_ARG(field,part, min_value,max_value,
+ min_flag, max_flag, maybe_flag);
+ tmp->parent=new_parent;
+ tmp->next_key_part=next_key_part;
+ if (left != &null_element)
+ tmp->left=left->clone(tmp,next_arg);
+
+ tmp->prev= *next_arg; // Link into next/prev chain
+ (*next_arg)->next=tmp;
+ (*next_arg)= tmp;
+
+ if (right != &null_element)
+ tmp->right=right->clone(tmp,next_arg);
+ }
+ increment_use_count(1);
+ return tmp;
+}
+
+SEL_ARG *SEL_ARG::first()
+{
+ SEL_ARG *next_arg=this;
+ if (!next_arg->left)
+ return 0; // MAYBE_KEY
+ while (next_arg->left != &null_element)
+ next_arg=next_arg->left;
+ return next_arg;
+}
+
+SEL_ARG *SEL_ARG::last()
+{
+ SEL_ARG *next_arg=this;
+ if (!next_arg->right)
+ return 0; // MAYBE_KEY
+ while (next_arg->right != &null_element)
+ next_arg=next_arg->right;
+ 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)
+{
+ int cmp;
+ /* First check if there was a compare to a min or max element */
+ if (a_flag & (NO_MIN_RANGE | NO_MAX_RANGE))
+ {
+ if ((a_flag & (NO_MIN_RANGE | NO_MAX_RANGE)) ==
+ (b_flag & (NO_MIN_RANGE | NO_MAX_RANGE)))
+ return 0;
+ return (a_flag & NO_MIN_RANGE) ? -1 : 1;
+ }
+ if (b_flag & (NO_MIN_RANGE | NO_MAX_RANGE))
+ return (b_flag & NO_MIN_RANGE) ? 1 : -1;
+
+ if (field->real_maybe_null()) // If null is part of key
+ {
+ if (*a != *b)
+ {
+ return *a ? -1 : 1;
+ }
+ if (*a)
+ goto end; // NULL where equal
+ a++; b++; // Skipp NULL marker
+ }
+ cmp=field->key_cmp((byte*) a,(byte*) b);
+ if (cmp) return cmp < 0 ? -1 : 1; // The values differed
+
+ // Check if the compared equal arguments was defined with open/closed range
+ end:
+ if (a_flag & (NEAR_MIN | NEAR_MAX))
+ {
+ if ((a_flag & (NEAR_MIN | NEAR_MAX)) == (b_flag & (NEAR_MIN | NEAR_MAX)))
+ return 0;
+ if (!(b_flag & (NEAR_MIN | NEAR_MAX)))
+ return (a_flag & NEAR_MIN) ? 2 : -2;
+ return (a_flag & NEAR_MIN) ? 1 : -1;
+ }
+ if (b_flag & (NEAR_MIN | NEAR_MAX))
+ return (b_flag & NEAR_MIN) ? -2 : 2;
+ return 0; // The elements where equal
+}
+
+
+SEL_ARG *SEL_ARG::clone_tree()
+{
+ SEL_ARG tmp_link,*next_arg,*root;
+ next_arg= &tmp_link;
+ root=clone((SEL_ARG *) 0, &next_arg);
+ next_arg->next=0; // Fix last link
+ tmp_link.next->prev=0; // Fix first link
+ root->use_count=0;
+ return root;
+}
+
+/*****************************************************************************
+** Test if a key can be used in different ranges
+** Returns:
+** -1 if impossible select
+** 0 if can't use quick_select
+** 1 if found usably range
+** Updates the following in the select parameter:
+** needed_reg ; Bits for keys with may be used if all prev regs are read
+** quick ; Parameter to use when reading records.
+** In the table struct the following information is updated:
+** quick_keys ; Which keys can be used
+** quick_rows ; How many rows the key matches
+*****************************************************************************/
+
+int SQL_SELECT::test_quick_select(key_map keys_to_use, table_map prev_tables,
+ ha_rows limit, bool force_quick_range)
+{
+ uint basflag;
+ uint idx;
+ double scan_time;
+ DBUG_ENTER("test_quick_select");
+
+ delete quick;
+ quick=0;
+ needed_reg=0; quick_keys=0;
+ 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) &&
+ keys_to_use == (uint) ~0 || !keys_to_use)
+ DBUG_RETURN(0); /* Not smart database */
+ records=head->file->records;
+ if (!records)
+ records++; /* purecov: inspected */
+ scan_time=(double) records / TIME_FOR_COMPARE+1;
+ read_time=(double) head->file->scan_time()+ scan_time + 1.0;
+ 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_PRINT("info",("Time to scan table: %ld",(long) read_time));
+
+ keys_to_use&=head->keys_in_use_for_query;
+ if (keys_to_use)
+ {
+ MEM_ROOT *old_root,alloc;
+ SEL_TREE *tree;
+ KEY_PART *key_parts;
+ PARAM param;
+
+ /* set up parameter that is passed to all functions */
+ param.baseflag=basflag;
+ param.prev_tables=prev_tables | const_tables;
+ param.read_tables=read_tables;
+ param.current_table= head->map;
+ param.table=head;
+ param.keys=0;
+
+ current_thd->no_errors=1; // Don't warn about NULL
+ init_sql_alloc(&alloc,2048);
+ if (!(param.key_parts = (KEY_PART*) alloc_root(&alloc,
+ sizeof(KEY_PART)*
+ head->key_parts)))
+ {
+ current_thd->no_errors=0;
+ free_root(&alloc); // Return memory & allocator
+ DBUG_RETURN(0); // Can't use range
+ }
+ key_parts= param.key_parts;
+ old_root=my_pthread_getspecific_ptr(MEM_ROOT*,THR_MALLOC);
+ my_pthread_setspecific_ptr(THR_MALLOC,&alloc);
+
+ for (idx=0 ; idx < head->keys ; idx++)
+ {
+ if (!(keys_to_use & ((key_map) 1L << idx)))
+ continue;
+ KEY *key_info= &head->key_info[idx];
+ if (key_info->flags & HA_FULLTEXT)
+ continue; // ToDo: ft-keys in non-ft ranges, if possible SerG
+
+ param.key[param.keys]=key_parts;
+ for (uint part=0 ; part < key_info->key_parts ; part++,key_parts++)
+ {
+ key_parts->key=param.keys;
+ key_parts->part=part;
+ key_parts->part_length= key_info->key_part[part].length;
+ key_parts->field= key_info->key_part[part].field;
+ key_parts->null_bit= key_info->key_part[part].null_bit;
+ if (key_parts->field->type() == FIELD_TYPE_BLOB)
+ key_parts->part_length+=HA_KEY_BLOB_LENGTH;
+ }
+ param.real_keynr[param.keys++]=idx;
+ }
+ param.key_parts_end=key_parts;
+
+ if ((tree=get_mm_tree(&param,cond)))
+ {
+ if (tree->type == SEL_TREE::IMPOSSIBLE)
+ {
+ records=0L; // Return -1 from this function
+ read_time= (double) HA_POS_ERROR;
+ }
+ else if (tree->type == SEL_TREE::KEY ||
+ tree->type == SEL_TREE::KEY_SMALLER)
+ {
+ SEL_ARG **key,**end,**best_key=0;
+
+ for (idx=0,key=tree->keys, end=key+param.keys ;
+ key != end ;
+ key++,idx++)
+ {
+ ha_rows found_records;
+ double found_read_time;
+
+ if (*key)
+ {
+ if ((*key)->type == SEL_ARG::MAYBE_KEY ||
+ (*key)->maybe_flag)
+ needed_reg|= (key_map) 1 << param.real_keynr[idx];
+
+ found_records=check_quick_select(&param,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))
+ {
+ /*
+ ** 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+1;
+ found_read_time=((double) (found_records+keys_per_block-1)/
+ (double) keys_per_block);
+ }
+ else
+ found_read_time= head->file->read_time(found_records)+
+ (double) found_records / TIME_FOR_COMPARE;
+ if (read_time > found_read_time)
+ {
+ read_time=found_read_time;
+ records=found_records;
+ best_key=key;
+ }
+ }
+ }
+ if (best_key && records)
+ {
+ if ((quick=get_quick_select(&param,(uint) (best_key-tree->keys),
+ *best_key)))
+ {
+ quick->records=records;
+ quick->read_time=read_time;
+ }
+ }
+ }
+ }
+ free_root(&alloc); // Return memory & allocator
+ my_pthread_setspecific_ptr(THR_MALLOC,old_root);
+ current_thd->no_errors=0;
+ }
+ DBUG_EXECUTE("info",print_quick(quick,needed_reg););
+ /*
+ Assume that if the user is using 'limit' we will only need to scan
+ limit rows if we are using a key
+ */
+ DBUG_RETURN(records ? test(quick) : -1);
+}
+
+ /* make a select tree of all keys in condition */
+
+static SEL_TREE *get_mm_tree(PARAM *param,COND *cond)
+{
+ SEL_TREE *tree=0;
+ DBUG_ENTER("get_mm_tree");
+
+ if (cond->type() == Item::COND_ITEM)
+ {
+ List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
+
+ if (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC)
+ {
+ tree=0;
+ Item *item;
+ while ((item=li++))
+ {
+ SEL_TREE *new_tree=get_mm_tree(param,item);
+ tree=tree_and(param,tree,new_tree);
+ if (tree && tree->type == SEL_TREE::IMPOSSIBLE)
+ break;
+ }
+ }
+ else
+ { // COND OR
+ tree=get_mm_tree(param,li++);
+ if (tree)
+ {
+ Item *item;
+ while ((item=li++))
+ {
+ SEL_TREE *new_tree=get_mm_tree(param,item);
+ if (!new_tree)
+ DBUG_RETURN(0);
+ tree=tree_or(param,tree,new_tree);
+ if (!tree || tree->type == SEL_TREE::ALWAYS)
+ break;
+ }
+ }
+ }
+ DBUG_RETURN(tree);
+ }
+ /* Here when simple cond */
+ if (cond->const_item())
+ {
+ if (cond->val_int())
+ DBUG_RETURN(new SEL_TREE(SEL_TREE::ALWAYS));
+ DBUG_RETURN(new SEL_TREE(SEL_TREE::IMPOSSIBLE));
+ }
+ table_map ref_tables=cond->used_tables();
+ if (ref_tables & ~(param->prev_tables | param->read_tables |
+ param->current_table))
+ DBUG_RETURN(0); // Can't be calculated yet
+ if (cond->type() != Item::FUNC_ITEM)
+ { // Should be a field
+ if (ref_tables & param->current_table)
+ DBUG_RETURN(0);
+ DBUG_RETURN(new SEL_TREE(SEL_TREE::MAYBE));
+ }
+ if (!(ref_tables & param->current_table))
+ DBUG_RETURN(new SEL_TREE(SEL_TREE::MAYBE)); // This may be false or true
+ Item_func *cond_func= (Item_func*) cond;
+ if (cond_func->select_optimize() == Item_func::OPTIMIZE_NONE)
+ DBUG_RETURN(0); // Can't be calculated
+
+ if (cond_func->functype() == Item_func::BETWEEN)
+ {
+ if (cond_func->arguments()[0]->type() == Item::FIELD_ITEM)
+ {
+ Field *field=((Item_field*) (cond_func->arguments()[0]))->field;
+ Item_result cmp_type=field->cmp_type();
+ tree= get_mm_parts(param,field,Item_func::GE_FUNC,
+ cond_func->arguments()[1],cmp_type);
+ DBUG_RETURN(tree_and(param,tree,
+ get_mm_parts(param, field,
+ Item_func::LE_FUNC,
+ cond_func->arguments()[2],cmp_type)));
+ }
+ DBUG_RETURN(0);
+ }
+ if (cond_func->functype() == Item_func::IN_FUNC)
+ { // COND OR
+ Item_func_in *func=(Item_func_in*) cond_func;
+ if (func->key_item()->type() == Item::FIELD_ITEM)
+ {
+ Field *field=((Item_field*) (func->key_item()))->field;
+ Item_result cmp_type=field->cmp_type();
+ tree= get_mm_parts(param,field,Item_func::EQ_FUNC,
+ func->arguments()[0],cmp_type);
+ if (!tree)
+ DBUG_RETURN(tree); // Not key field
+ for (uint i=1 ; i < func->argument_count(); i++)
+ {
+ SEL_TREE *new_tree=get_mm_parts(param,field,Item_func::EQ_FUNC,
+ func->arguments()[i],cmp_type);
+ tree=tree_or(param,tree,new_tree);
+ }
+ DBUG_RETURN(tree);
+ }
+ DBUG_RETURN(0); // Can't optimize this IN
+ }
+
+ /* check field op const */
+ /* btw, ft_func's arguments()[0] isn't FIELD_ITEM. SerG*/
+ if (cond_func->arguments()[0]->type() == Item::FIELD_ITEM)
+ {
+ tree= get_mm_parts(param,
+ ((Item_field*) (cond_func->arguments()[0]))->field,
+ cond_func->functype(),
+ cond_func->arg_count > 1 ? cond_func->arguments()[1] :
+ 0,
+ ((Item_field*) (cond_func->arguments()[0]))->field->
+ cmp_type());
+ }
+ /* check const op field */
+ if (!tree &&
+ cond_func->have_rev_func() &&
+ cond_func->arguments()[1]->type() == Item::FIELD_ITEM)
+ {
+ DBUG_RETURN(get_mm_parts(param,
+ ((Item_field*)
+ (cond_func->arguments()[1]))->field,
+ ((Item_bool_func2*) cond_func)->rev_functype(),
+ cond_func->arguments()[0],
+ ((Item_field*)
+ (cond_func->arguments()[1]))->field->cmp_type()
+ ));
+ }
+ DBUG_RETURN(tree);
+}
+
+
+static SEL_TREE *
+get_mm_parts(PARAM *param,Field *field, Item_func::Functype type,Item *value,
+ Item_result cmp_type)
+{
+ DBUG_ENTER("get_mm_parts");
+ if (field->table != param->table)
+ DBUG_RETURN(0);
+
+ KEY_PART *key_part = param->key_parts,*end=param->key_parts_end;
+ SEL_TREE *tree=0;
+ if (value &&
+ value->used_tables() & ~(param->prev_tables | param->read_tables))
+ DBUG_RETURN(0);
+ for ( ; key_part != end ; key_part++)
+ {
+ if (field->eq(key_part->field))
+ {
+ SEL_ARG *sel_arg=0;
+ if (!tree)
+ tree=new SEL_TREE();
+ if (!value || !(value->used_tables() & ~param->read_tables))
+ {
+ sel_arg=get_mm_leaf(key_part->field,key_part,type,value);
+ if (!sel_arg)
+ continue;
+ if (sel_arg->type == SEL_ARG::IMPOSSIBLE)
+ {
+ tree->type=SEL_TREE::IMPOSSIBLE;
+ DBUG_RETURN(tree);
+ }
+ }
+ else
+ sel_arg=new SEL_ARG(SEL_ARG::MAYBE_KEY);// This key may be used later
+ sel_arg->part=(uchar) key_part->part;
+ tree->keys[key_part->key]=sel_add(tree->keys[key_part->key],sel_arg);
+ }
+ }
+ DBUG_RETURN(tree);
+}
+
+
+static SEL_ARG *
+get_mm_leaf(Field *field,KEY_PART *key_part,
+ Item_func::Functype type,Item *value)
+{
+ uint maybe_null=(uint) field->real_maybe_null();
+ uint field_length=field->pack_length()+maybe_null;
+ SEL_ARG *tree;
+ DBUG_ENTER("get_mm_leaf");
+
+ if (type == Item_func::LIKE_FUNC)
+ {
+ bool like_error;
+ char buff1[MAX_FIELD_WIDTH],*min_str,*max_str;
+ String tmp(buff1,sizeof(buff1)),*res;
+ uint length,offset,min_length,max_length;
+
+ if (!(res= value->val_str(&tmp)))
+ DBUG_RETURN(&null_element);
+
+ // Check if this was a function. This should have be optimized away
+ // in the sql_select.cc
+ if (res != &tmp)
+ {
+ tmp.copy(*res); // Get own copy
+ res= &tmp;
+ }
+ if (field->cmp_type() != STRING_RESULT)
+ DBUG_RETURN(0); // Can only optimize strings
+
+ offset=maybe_null;
+ length=key_part->part_length;
+ if (field->type() == FIELD_TYPE_BLOB)
+ {
+ offset+=HA_KEY_BLOB_LENGTH;
+ field_length=key_part->part_length-HA_KEY_BLOB_LENGTH;
+ }
+ else
+ {
+ if (length < field_length)
+ length=field_length; // Only if overlapping key
+ else
+ field_length=length;
+ }
+ length+=offset;
+ if (!(min_str= (char*) sql_alloc(length*2)))
+ DBUG_RETURN(0);
+ max_str=min_str+length;
+ if (maybe_null)
+ max_str[0]= min_str[0]=0;
+ if (field->binary())
+ like_error=like_range(res->ptr(),res->length(),wild_prefix,field_length,
+ min_str+offset,max_str+offset,(char) 255,
+ &min_length,&max_length);
+ else
+ {
+#ifdef USE_STRCOLL
+ if (use_strcoll(default_charset_info))
+ like_error= my_like_range(default_charset_info,
+ res->ptr(),res->length(),wild_prefix,
+ field_length, min_str+maybe_null,
+ max_str+maybe_null,&min_length,&max_length);
+ else
+#endif
+ like_error=like_range(res->ptr(),res->length(),wild_prefix,field_length,
+ min_str+offset,max_str+offset,
+ max_sort_char,&min_length,&max_length);
+ }
+ if (like_error) // Can't optimize with LIKE
+ DBUG_RETURN(0);
+ if (offset != maybe_null) // Blob
+ {
+ int2store(min_str+maybe_null,min_length);
+ int2store(max_str+maybe_null,max_length);
+ }
+ DBUG_RETURN(new SEL_ARG(field,min_str,max_str));
+ }
+
+ if (!value) // IS NULL or IS NOT NULL
+ {
+ if (field->table->outer_join) // Can't use a key on this
+ DBUG_RETURN(0);
+ if (!maybe_null) // Not null field
+ DBUG_RETURN(type == Item_func::ISNULL_FUNC ? &null_element : 0);
+ tree=new SEL_ARG(field,is_null_string,is_null_string);
+ if (!tree)
+ DBUG_RETURN(0);
+ if (type == Item_func::ISNOTNULL_FUNC)
+ {
+ tree->min_flag=NEAR_MIN; /* IS NOT NULL -> X > NULL */
+ tree->max_flag=NO_MAX_RANGE;
+ }
+ DBUG_RETURN(tree);
+ }
+
+ if (!field->optimize_range() && type != Item_func::EQ_FUNC &&
+ type != Item_func::EQUAL_FUNC)
+ DBUG_RETURN(0); // Can't optimize this
+
+ /* 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 */
+ if (field->result_type() == STRING_RESULT &&
+ value->result_type() != STRING_RESULT &&
+ field->cmp_type() != value->result_type())
+ DBUG_RETURN(0);
+
+ if (value->save_in_field(field))
+ {
+ 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); // NULL is never true
+ }
+ // Get local copy of key
+ char *str= (char*) sql_alloc(key_part->part_length+maybe_null);
+ if (!str)
+ DBUG_RETURN(0);
+ if (maybe_null)
+ *str=0; // Not NULL
+ field->get_key_image(str+maybe_null,key_part->part_length);
+ if (!(tree=new SEL_ARG(field,str,str)))
+ DBUG_RETURN(0);
+
+ switch (type) {
+ case Item_func::LT_FUNC:
+ if (field_is_equal_to_item(field,value))
+ tree->max_flag=NEAR_MAX;
+ /* fall through */
+ case Item_func::LE_FUNC:
+ if (!maybe_null)
+ tree->min_flag=NO_MIN_RANGE; /* From start */
+ else
+ { // > NULL
+ tree->min_value=is_null_string;
+ tree->min_flag=NEAR_MIN;
+ }
+ break;
+ case Item_func::GT_FUNC:
+ if (field_is_equal_to_item(field,value))
+ tree->min_flag=NEAR_MIN;
+ /* fall through */
+ case Item_func::GE_FUNC:
+ tree->max_flag=NO_MAX_RANGE;
+ break;
+ default:
+ break;
+ }
+ DBUG_RETURN(tree);
+}
+
+
+/*
+** Calculate min_str and max_str that ranges a LIKE string.
+** Arguments:
+** ptr Pointer to LIKE string.
+** ptr_length Length of LIKE string.
+** escape Escape character in LIKE. (Normally '\').
+** All escape characters should be removed from min_str and max_str
+** res_length Length of min_str and max_str.
+** min_str Smallest case sensitive string that ranges LIKE.
+** Should be space padded to res_length.
+** max_str Largest case sensitive string that ranges LIKE.
+** Normally padded with the biggest character sort value.
+**
+** The function should return 0 if ok and 1 if the LIKE string can't be
+** optimized !
+*/
+
+static bool like_range(const char *ptr,uint ptr_length,char escape,
+ uint res_length, char *min_str,char *max_str,
+ char max_sort_chr, uint *min_length, uint *max_length)
+{
+ const char *end=ptr+ptr_length;
+ char *min_org=min_str;
+ char *min_end=min_str+res_length;
+
+ for (; ptr != end && min_str != min_end ; ptr++)
+ {
+ if (*ptr == escape && ptr+1 != end)
+ {
+ ptr++; // Skipp escape
+ *min_str++= *max_str++ = *ptr;
+ continue;
+ }
+ if (*ptr == wild_one) // '_' in SQL
+ {
+ *min_str++='\0'; // This should be min char
+ *max_str++=max_sort_chr;
+ continue;
+ }
+ if (*ptr == wild_many) // '%' in SQL
+ {
+ *min_length= (uint) (min_str - min_org);
+ *max_length=res_length;
+ do {
+ *min_str++ = ' '; // Because if key compression
+ *max_str++ = max_sort_chr;
+ } while (min_str != min_end);
+ return 0;
+ }
+ *min_str++= *max_str++ = *ptr;
+ }
+ *min_length= *max_length = (uint) (min_str - min_org);
+ while (min_str != min_end)
+ *min_str++ = *max_str++ = ' '; // Because if key compression
+ return 0;
+}
+
+
+/******************************************************************************
+** Tree manipulation functions
+** If tree is 0 it means that the condition can't be tested. It refers
+** to a non existent table or to a field in current table with isn't a key.
+** The different tree flags:
+** IMPOSSIBLE: Condition is never true
+** ALWAYS: Condition is always true
+** MAYBE: Condition may exists when tables are read
+** MAYBE_KEY: Condition refers to a key that may be used in join loop
+** KEY_RANGE: Condition uses a key
+******************************************************************************/
+
+/*
+** Add a new key test to a key when scanning through all keys
+** This will never be called for same key parts.
+*/
+
+static SEL_ARG *
+sel_add(SEL_ARG *key1,SEL_ARG *key2)
+{
+ SEL_ARG *root,**key_link;
+
+ if (!key1)
+ return key2;
+ if (!key2)
+ return key1;
+
+ key_link= &root;
+ while (key1 && key2)
+ {
+ if (key1->part < key2->part)
+ {
+ *key_link= key1;
+ key_link= &key1->next_key_part;
+ key1=key1->next_key_part;
+ }
+ else
+ {
+ *key_link= key2;
+ key_link= &key2->next_key_part;
+ key2=key2->next_key_part;
+ }
+ }
+ *key_link=key1 ? key1 : key2;
+ return root;
+}
+
+#define CLONE_KEY1_MAYBE 1
+#define CLONE_KEY2_MAYBE 2
+#define swap_clone_flag(A) ((A & 1) << 1) | ((A & 2) >> 1)
+
+
+static SEL_TREE *
+tree_and(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2)
+{
+ DBUG_ENTER("tree_and");
+ if (!tree1)
+ DBUG_RETURN(tree2);
+ if (!tree2)
+ DBUG_RETURN(tree1);
+ if (tree1->type == SEL_TREE::IMPOSSIBLE || tree2->type == SEL_TREE::ALWAYS)
+ DBUG_RETURN(tree1);
+ if (tree2->type == SEL_TREE::IMPOSSIBLE || tree1->type == SEL_TREE::ALWAYS)
+ DBUG_RETURN(tree2);
+ if (tree1->type == SEL_TREE::MAYBE)
+ {
+ if (tree2->type == SEL_TREE::KEY)
+ tree2->type=SEL_TREE::KEY_SMALLER;
+ DBUG_RETURN(tree2);
+ }
+ if (tree2->type == SEL_TREE::MAYBE)
+ {
+ tree1->type=SEL_TREE::KEY_SMALLER;
+ DBUG_RETURN(tree1);
+ }
+
+ /* Join the trees key per key */
+ SEL_ARG **key1,**key2,**end;
+ for (key1= tree1->keys,key2= tree2->keys,end=key1+param->keys ;
+ key1 != end ; key1++,key2++)
+ {
+ uint flag=0;
+ if (*key1 || *key2)
+ {
+ if (*key1 && !(*key1)->simple_key())
+ flag|=CLONE_KEY1_MAYBE;
+ if (*key2 && !(*key2)->simple_key())
+ flag|=CLONE_KEY2_MAYBE;
+ *key1=key_and(*key1,*key2,flag);
+ if ((*key1)->type == SEL_ARG::IMPOSSIBLE)
+ {
+ tree1->type= SEL_TREE::IMPOSSIBLE;
+ break;
+ }
+#ifdef EXTRA_DEBUG
+ (*key1)->test_use_count(*key1);
+#endif
+ }
+ }
+ DBUG_RETURN(tree1);
+}
+
+
+
+static SEL_TREE *
+tree_or(PARAM *param,SEL_TREE *tree1,SEL_TREE *tree2)
+{
+ DBUG_ENTER("tree_or");
+ if (!tree1 || !tree2)
+ DBUG_RETURN(0);
+ if (tree1->type == SEL_TREE::IMPOSSIBLE || tree2->type == SEL_TREE::ALWAYS)
+ DBUG_RETURN(tree2);
+ if (tree2->type == SEL_TREE::IMPOSSIBLE || tree1->type == SEL_TREE::ALWAYS)
+ DBUG_RETURN(tree1);
+ if (tree1->type == SEL_TREE::MAYBE)
+ DBUG_RETURN(tree1); // Can't use this
+ if (tree2->type == SEL_TREE::MAYBE)
+ DBUG_RETURN(tree2);
+
+ /* Join the trees key per key */
+ SEL_ARG **key1,**key2,**end;
+ SEL_TREE *result=0;
+ for (key1= tree1->keys,key2= tree2->keys,end=key1+param->keys ;
+ key1 != end ; key1++,key2++)
+ {
+ *key1=key_or(*key1,*key2);
+ if (*key1)
+ {
+ result=tree1; // Added to tree1
+#ifdef EXTRA_DEBUG
+ (*key1)->test_use_count(*key1);
+#endif
+ }
+ }
+ DBUG_RETURN(result);
+}
+
+
+/* And key trees where key1->part < key2 -> part */
+
+static SEL_ARG *
+and_all_keys(SEL_ARG *key1,SEL_ARG *key2,uint clone_flag)
+{
+ SEL_ARG *next;
+ ulong use_count=key1->use_count;
+
+ if (key1->elements != 1)
+ {
+ key2->use_count+=key1->elements-1;
+ key2->increment_use_count((int) key1->elements-1);
+ }
+ if (key1->type == SEL_ARG::MAYBE_KEY)
+ {
+ key1->left= &null_element; key1->next=0;
+ }
+ for (next=key1->first(); next ; next=next->next)
+ {
+ if (next->next_key_part)
+ {
+ SEL_ARG *tmp=key_and(next->next_key_part,key2,clone_flag);
+ if (tmp && tmp->type == SEL_ARG::IMPOSSIBLE)
+ {
+ key1=key1->tree_delete(next);
+ continue;
+ }
+ next->next_key_part=tmp;
+ if (use_count)
+ next->increment_use_count(use_count);
+ }
+ else
+ next->next_key_part=key2;
+ }
+ if (!key1)
+ return &null_element; // Impossible ranges
+ key1->use_count++;
+ return key1;
+}
+
+
+
+static SEL_ARG *
+key_and(SEL_ARG *key1,SEL_ARG *key2,uint clone_flag)
+{
+ if (!key1)
+ return key2;
+ if (!key2)
+ return key1;
+ if (key1->part != key2->part)
+ {
+ if (key1->part > key2->part)
+ {
+ swap(SEL_ARG *,key1,key2);
+ clone_flag=swap_clone_flag(clone_flag);
+ }
+ // key1->part < key2->part
+ key1->use_count--;
+ if (key1->use_count > 0)
+ key1=key1->clone_tree();
+ return and_all_keys(key1,key2,clone_flag);
+ }
+
+ if (((clone_flag & CLONE_KEY2_MAYBE) &&
+ !(clone_flag & CLONE_KEY1_MAYBE)) ||
+ key1->type == SEL_ARG::MAYBE_KEY)
+ { // Put simple key in key2
+ swap(SEL_ARG *,key1,key2);
+ clone_flag=swap_clone_flag(clone_flag);
+ }
+
+ // If one of the key is MAYBE_KEY then the found region may be smaller
+ if (key2->type == SEL_ARG::MAYBE_KEY)
+ {
+ if (key1->use_count > 1)
+ {
+ key1->use_count--;
+ key1=key1->clone_tree();
+ key1->use_count++;
+ }
+ if (key1->type == SEL_ARG::MAYBE_KEY)
+ { // Both are maybe key
+ key1->next_key_part=key_and(key1->next_key_part,key2->next_key_part,
+ clone_flag);
+ if (key1->next_key_part &&
+ key1->next_key_part->type == SEL_ARG::IMPOSSIBLE)
+ return key1;
+ }
+ else
+ {
+ key1->maybe_smaller();
+ if (key2->next_key_part)
+ return and_all_keys(key1,key2,clone_flag);
+ key2->use_count--; // Key2 doesn't have a tree
+ }
+ return key1;
+ }
+
+ key1->use_count--;
+ key2->use_count--;
+ SEL_ARG *e1=key1->first(), *e2=key2->first(), *new_tree=0;
+
+ while (e1 && e2)
+ {
+ int cmp=e1->cmp_min_to_min(e2);
+ if (cmp < 0)
+ {
+ if (get_range(&e1,&e2,key1))
+ continue;
+ }
+ else if (get_range(&e2,&e1,key2))
+ continue;
+ SEL_ARG *next=key_and(e1->next_key_part,e2->next_key_part,clone_flag);
+ e1->increment_use_count(1);
+ e2->increment_use_count(1);
+ if (!next || next->type != SEL_ARG::IMPOSSIBLE)
+ {
+ SEL_ARG *new_arg= e1->clone_and(e2);
+ new_arg->next_key_part=next;
+ if (!new_tree)
+ {
+ new_tree=new_arg;
+ }
+ else
+ new_tree=new_tree->insert(new_arg);
+ }
+ if (e1->cmp_max_to_max(e2) < 0)
+ e1=e1->next; // e1 can't overlapp next e2
+ else
+ e2=e2->next;
+ }
+ key1->free_tree();
+ key2->free_tree();
+ if (!new_tree)
+ return &null_element; // Impossible range
+ return new_tree;
+}
+
+
+static bool
+get_range(SEL_ARG **e1,SEL_ARG **e2,SEL_ARG *root1)
+{
+ (*e1)=root1->find_range(*e2); // first e1->min < e2->min
+ if ((*e1)->cmp_max_to_min(*e2) < 0)
+ {
+ if (!((*e1)=(*e1)->next))
+ return 1;
+ if ((*e1)->cmp_min_to_max(*e2) > 0)
+ {
+ (*e2)=(*e2)->next;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+static SEL_ARG *
+key_or(SEL_ARG *key1,SEL_ARG *key2)
+{
+ if (!key1)
+ {
+ if (key2)
+ {
+ key2->use_count--;
+ key2->free_tree();
+ }
+ return 0;
+ }
+ else if (!key2)
+ {
+ key1->use_count--;
+ key1->free_tree();
+ return 0;
+ }
+ key1->use_count--;
+ key2->use_count--;
+
+ if (key1->part != key2->part)
+ {
+ key1->free_tree();
+ key2->free_tree();
+ return 0; // Can't optimize this
+ }
+
+ // If one of the key is MAYBE_KEY then the found region may be bigger
+ if (key1->type == SEL_ARG::MAYBE_KEY)
+ {
+ key2->free_tree();
+ key1->use_count++;
+ return key1;
+ }
+ if (key2->type == SEL_ARG::MAYBE_KEY)
+ {
+ key1->free_tree();
+ key2->use_count++;
+ return key2;
+ }
+
+ if (key1->use_count > 0)
+ {
+ if (key2->use_count == 0 || key1->elements > key2->elements)
+ {
+ swap(SEL_ARG *,key1,key2);
+ }
+ else
+ key1=key1->clone_tree();
+ }
+
+ // Add tree at key2 to tree at key1
+ bool key2_shared=key2->use_count != 0;
+ key1->maybe_flag|=key2->maybe_flag;
+
+ for (key2=key2->first(); key2; )
+ {
+ SEL_ARG *tmp=key1->find_range(key2); // Find key1.min <= key2.min
+ int cmp;
+
+ if (!tmp)
+ {
+ tmp=key1->first(); // tmp.min > key2.min
+ cmp= -1;
+ }
+ else if ((cmp=tmp->cmp_max_to_min(key2)) < 0)
+ { // Found tmp.max < key2.min
+ SEL_ARG *next=tmp->next;
+ if (cmp == -2 && eq_tree(tmp->next_key_part,key2->next_key_part))
+ {
+ // Join near ranges like tmp.max < 0 and key2.min >= 0
+ SEL_ARG *key2_next=key2->next;
+ if (key2_shared)
+ {
+ key2=new SEL_ARG(*key2);
+ key2->increment_use_count(key1->use_count+1);
+ key2->next=key2_next; // New copy of key2
+ }
+ key2->copy_min(tmp);
+ if (!(key1=key1->tree_delete(tmp)))
+ { // Only one key in tree
+ key1=key2;
+ key1->make_root();
+ key2=key2_next;
+ break;
+ }
+ }
+ if (!(tmp=next)) // tmp.min > key2.min
+ break; // Copy rest of key2
+ }
+ if (cmp < 0)
+ { // tmp.min > key2.min
+ int tmp_cmp;
+ if ((tmp_cmp=tmp->cmp_min_to_max(key2)) > 0) // if tmp.min > key2.max
+ {
+ if (tmp_cmp == 2 && eq_tree(tmp->next_key_part,key2->next_key_part))
+ { // ranges are connected
+ tmp->copy_min_to_min(key2);
+ key1->merge_flags(key2);
+ if (tmp->min_flag & NO_MIN_RANGE &&
+ tmp->max_flag & NO_MAX_RANGE)
+ {
+ if (key1->maybe_flag)
+ return new SEL_ARG(SEL_ARG::MAYBE_KEY);
+ return 0;
+ }
+ key2->increment_use_count(-1); // Free not used tree
+ key2=key2->next;
+ continue;
+ }
+ else
+ {
+ SEL_ARG *next=key2->next; // Keys are not overlapping
+ if (key2_shared)
+ {
+ key1=key1->insert(new SEL_ARG(*key2)); // Must make copy
+ key2->increment_use_count(key1->use_count+1);
+ }
+ else
+ key1=key1->insert(key2); // Will destroy key2_root
+ key2=next;
+ continue;
+ }
+ }
+ }
+
+ // tmp.max >= key2.min && tmp.min <= key.max (overlapping ranges)
+ if (eq_tree(tmp->next_key_part,key2->next_key_part))
+ {
+ if (tmp->is_same(key2))
+ {
+ tmp->merge_flags(key2); // Copy maybe flags
+ key2->increment_use_count(-1); // Free not used tree
+ }
+ else
+ {
+ SEL_ARG *last=tmp;
+ while (last->next && last->next->cmp_min_to_max(key2) <= 0 &&
+ eq_tree(last->next->next_key_part,key2->next_key_part))
+ {
+ SEL_ARG *save=last;
+ last=last->next;
+ key1=key1->tree_delete(save);
+ }
+ if (last->copy_min(key2) || last->copy_max(key2))
+ { // Full range
+ key1->free_tree();
+ for (; key2 ; key2=key2->next)
+ key2->increment_use_count(-1); // Free not used tree
+ if (key1->maybe_flag)
+ return new SEL_ARG(SEL_ARG::MAYBE_KEY);
+ return 0;
+ }
+ }
+ key2=key2->next;
+ continue;
+ }
+
+ if (cmp >= 0 && tmp->cmp_min_to_min(key2) < 0)
+ { // tmp.min <= x < key2.min
+ SEL_ARG *new_arg=tmp->clone_first(key2);
+ if ((new_arg->next_key_part= key1->next_key_part))
+ new_arg->increment_use_count(key1->use_count+1);
+ tmp->copy_min_to_min(key2);
+ key1=key1->insert(new_arg);
+ }
+
+ // tmp.min >= key2.min && tmp.min <= key2.max
+ SEL_ARG key(*key2); // Get copy we can modify
+ for (;;)
+ {
+ if (tmp->cmp_min_to_min(&key) > 0)
+ { // key.min <= x < tmp.min
+ SEL_ARG *new_arg=key.clone_first(tmp);
+ if ((new_arg->next_key_part=key.next_key_part))
+ new_arg->increment_use_count(key1->use_count+1);
+ key1=key1->insert(new_arg);
+ }
+ if ((cmp=tmp->cmp_max_to_max(&key)) <= 0)
+ { // tmp.min. <= x <= tmp.max
+ tmp->maybe_flag|= key.maybe_flag;
+ key.increment_use_count(key1->use_count+1);
+ tmp->next_key_part=key_or(tmp->next_key_part,key.next_key_part);
+ if (!cmp) // Key2 is ready
+ break;
+ key.copy_max_to_min(tmp);
+ if (!(tmp=tmp->next))
+ {
+ key1=key1->insert(new SEL_ARG(key));
+ key2=key2->next;
+ goto end;
+ }
+ if (tmp->cmp_min_to_max(&key) > 0)
+ {
+ key1=key1->insert(new SEL_ARG(key));
+ break;
+ }
+ }
+ else
+ {
+ SEL_ARG *new_arg=tmp->clone_last(&key); // tmp.min <= x <= key.max
+ tmp->copy_max_to_min(&key);
+ tmp->increment_use_count(key1->use_count+1);
+ new_arg->next_key_part=key_or(tmp->next_key_part,key.next_key_part);
+ key1=key1->insert(new_arg);
+ break;
+ }
+ }
+ key2=key2->next;
+ }
+
+end:
+ while (key2)
+ {
+ SEL_ARG *next=key2->next;
+ if (key2_shared)
+ {
+ key2->increment_use_count(key1->use_count+1);
+ key1=key1->insert(new SEL_ARG(*key2)); // Must make copy
+ }
+ else
+ key1=key1->insert(key2); // Will destroy key2_root
+ key2=next;
+ }
+ key1->use_count++;
+ return key1;
+}
+
+
+/* Compare if two trees are equal */
+
+static bool eq_tree(SEL_ARG* a,SEL_ARG *b)
+{
+ if (a == b)
+ return 1;
+ if (!a || !b || !a->is_same(b))
+ return 0;
+ if (a->left != &null_element && b->left != &null_element)
+ {
+ if (!eq_tree(a->left,b->left))
+ return 0;
+ }
+ else if (a->left != &null_element || b->left != &null_element)
+ return 0;
+ if (a->right != &null_element && b->right != &null_element)
+ {
+ if (!eq_tree(a->right,b->right))
+ return 0;
+ }
+ else if (a->right != &null_element || b->right != &null_element)
+ return 0;
+ if (a->next_key_part != b->next_key_part)
+ { // Sub range
+ if (!a->next_key_part != !b->next_key_part ||
+ !eq_tree(a->next_key_part, b->next_key_part))
+ return 0;
+ }
+ return 1;
+}
+
+
+SEL_ARG *
+SEL_ARG::insert(SEL_ARG *key)
+{
+ SEL_ARG *element,**par,*last_element;
+
+ LINT_INIT(par); LINT_INIT(last_element);
+ for (element= this; element != &null_element ; )
+ {
+ last_element=element;
+ if (key->cmp_min_to_min(element) > 0)
+ {
+ par= &element->right; element= element->right;
+ }
+ else
+ {
+ par = &element->left; element= element->left;
+ }
+ }
+ *par=key;
+ key->parent=last_element;
+ /* Link in list */
+ if (par == &last_element->left)
+ {
+ key->next=last_element;
+ if ((key->prev=last_element->prev))
+ key->prev->next=key;
+ last_element->prev=key;
+ }
+ else
+ {
+ if ((key->next=last_element->next))
+ key->next->prev=key;
+ key->prev=last_element;
+ last_element->next=key;
+ }
+ key->left=key->right= &null_element;
+ SEL_ARG *root=rb_insert(key); // rebalance tree
+ root->use_count=this->use_count; // copy root info
+ root->elements= this->elements+1;
+ root->maybe_flag=this->maybe_flag;
+ return root;
+}
+
+
+/*
+** Find best key with min <= given key
+** Because the call context this should never return 0 to get_range
+*/
+
+SEL_ARG *
+SEL_ARG::find_range(SEL_ARG *key)
+{
+ SEL_ARG *element=this,*found=0;
+
+ for (;;)
+ {
+ if (element == &null_element)
+ return found;
+ int cmp=element->cmp_min_to_min(key);
+ if (cmp == 0)
+ return element;
+ if (cmp < 0)
+ {
+ found=element;
+ element=element->right;
+ }
+ else
+ element=element->left;
+ }
+}
+
+
+/*
+** Remove a element from the tree
+** This also frees all sub trees that is used by the element
+*/
+
+SEL_ARG *
+SEL_ARG::tree_delete(SEL_ARG *key)
+{
+ enum leaf_color remove_color;
+ SEL_ARG *root,*nod,**par,*fix_par;
+ root=this; this->parent= 0;
+
+ /* Unlink from list */
+ if (key->prev)
+ key->prev->next=key->next;
+ if (key->next)
+ key->next->prev=key->prev;
+ key->increment_use_count(-1);
+ if (!key->parent)
+ par= &root;
+ else
+ par=key->parent_ptr();
+
+ if (key->left == &null_element)
+ {
+ *par=nod=key->right;
+ fix_par=key->parent;
+ if (nod != &null_element)
+ nod->parent=fix_par;
+ remove_color= key->color;
+ }
+ else if (key->right == &null_element)
+ {
+ *par= nod=key->left;
+ nod->parent=fix_par=key->parent;
+ remove_color= key->color;
+ }
+ else
+ {
+ SEL_ARG *tmp=key->next; // next bigger key (exist!)
+ nod= *tmp->parent_ptr()= tmp->right; // unlink tmp from tree
+ fix_par=tmp->parent;
+ if (nod != &null_element)
+ nod->parent=fix_par;
+ remove_color= tmp->color;
+
+ tmp->parent=key->parent; // Move node in place of key
+ (tmp->left=key->left)->parent=tmp;
+ if ((tmp->right=key->right) != &null_element)
+ tmp->right->parent=tmp;
+ tmp->color=key->color;
+ *par=tmp;
+ if (fix_par == key) // key->right == key->next
+ fix_par=tmp; // new parent of nod
+ }
+
+ if (root == &null_element)
+ return 0; // Maybe root later
+ if (remove_color == BLACK)
+ root=rb_delete_fixup(root,nod,fix_par);
+ test_rb_tree(root,root->parent);
+
+ root->use_count=this->use_count; // Fix root counters
+ root->elements=this->elements-1;
+ root->maybe_flag=this->maybe_flag;
+ return root;
+}
+
+
+ /* Functions to fix up the tree after insert and delete */
+
+static void left_rotate(SEL_ARG **root,SEL_ARG *leaf)
+{
+ SEL_ARG *y=leaf->right;
+ leaf->right=y->left;
+ if (y->left != &null_element)
+ y->left->parent=leaf;
+ if (!(y->parent=leaf->parent))
+ *root=y;
+ else
+ *leaf->parent_ptr()=y;
+ y->left=leaf;
+ leaf->parent=y;
+}
+
+static void right_rotate(SEL_ARG **root,SEL_ARG *leaf)
+{
+ SEL_ARG *y=leaf->left;
+ leaf->left=y->right;
+ if (y->right != &null_element)
+ y->right->parent=leaf;
+ if (!(y->parent=leaf->parent))
+ *root=y;
+ else
+ *leaf->parent_ptr()=y;
+ y->right=leaf;
+ leaf->parent=y;
+}
+
+
+SEL_ARG *
+SEL_ARG::rb_insert(SEL_ARG *leaf)
+{
+ SEL_ARG *y,*par,*par2,*root;
+ root= this; root->parent= 0;
+
+ leaf->color=RED;
+ while (leaf != root && (par= leaf->parent)->color == RED)
+ { // This can't be root or 1 level under
+ if (par == (par2= leaf->parent->parent)->left)
+ {
+ y= par2->right;
+ if (y->color == RED)
+ {
+ par->color=BLACK;
+ y->color=BLACK;
+ leaf=par2;
+ leaf->color=RED; /* And the loop continues */
+ }
+ else
+ {
+ if (leaf == par->right)
+ {
+ left_rotate(&root,leaf->parent);
+ par=leaf; /* leaf is now parent to old leaf */
+ }
+ par->color=BLACK;
+ par2->color=RED;
+ right_rotate(&root,par2);
+ break;
+ }
+ }
+ else
+ {
+ y= par2->left;
+ if (y->color == RED)
+ {
+ par->color=BLACK;
+ y->color=BLACK;
+ leaf=par2;
+ leaf->color=RED; /* And the loop continues */
+ }
+ else
+ {
+ if (leaf == par->left)
+ {
+ right_rotate(&root,par);
+ par=leaf;
+ }
+ par->color=BLACK;
+ par2->color=RED;
+ left_rotate(&root,par2);
+ break;
+ }
+ }
+ }
+ root->color=BLACK;
+ test_rb_tree(root,root->parent);
+ return root;
+}
+
+
+SEL_ARG *rb_delete_fixup(SEL_ARG *root,SEL_ARG *key,SEL_ARG *par)
+{
+ SEL_ARG *x,*w;
+ root->parent=0;
+
+ x= key;
+ while (x != root && x->color == SEL_ARG::BLACK)
+ {
+ if (x == par->left)
+ {
+ w=par->right;
+ if (w->color == SEL_ARG::RED)
+ {
+ w->color=SEL_ARG::BLACK;
+ par->color=SEL_ARG::RED;
+ left_rotate(&root,par);
+ w=par->right;
+ }
+ if (w->left->color == SEL_ARG::BLACK && w->right->color == SEL_ARG::BLACK)
+ {
+ w->color=SEL_ARG::RED;
+ x=par;
+ }
+ else
+ {
+ if (w->right->color == SEL_ARG::BLACK)
+ {
+ w->left->color=SEL_ARG::BLACK;
+ w->color=SEL_ARG::RED;
+ right_rotate(&root,w);
+ w=par->right;
+ }
+ w->color=par->color;
+ par->color=SEL_ARG::BLACK;
+ w->right->color=SEL_ARG::BLACK;
+ left_rotate(&root,par);
+ x=root;
+ break;
+ }
+ }
+ else
+ {
+ w=par->left;
+ if (w->color == SEL_ARG::RED)
+ {
+ w->color=SEL_ARG::BLACK;
+ par->color=SEL_ARG::RED;
+ right_rotate(&root,par);
+ w=par->left;
+ }
+ if (w->right->color == SEL_ARG::BLACK && w->left->color == SEL_ARG::BLACK)
+ {
+ w->color=SEL_ARG::RED;
+ x=par;
+ }
+ else
+ {
+ if (w->left->color == SEL_ARG::BLACK)
+ {
+ w->right->color=SEL_ARG::BLACK;
+ w->color=SEL_ARG::RED;
+ left_rotate(&root,w);
+ w=par->left;
+ }
+ w->color=par->color;
+ par->color=SEL_ARG::BLACK;
+ w->left->color=SEL_ARG::BLACK;
+ right_rotate(&root,par);
+ x=root;
+ break;
+ }
+ }
+ par=x->parent;
+ }
+ x->color=SEL_ARG::BLACK;
+ return root;
+}
+
+
+ /* Test that the proporties for a red-black tree holds */
+
+#ifdef EXTRA_DEBUG
+int test_rb_tree(SEL_ARG *element,SEL_ARG *parent)
+{
+ int count_l,count_r;
+
+ if (element == &null_element)
+ return 0; // Found end of tree
+ if (element->parent != parent)
+ {
+ sql_print_error("Wrong tree: Parent doesn't point at parent");
+ return -1;
+ }
+ if (element->color == SEL_ARG::RED &&
+ (element->left->color == SEL_ARG::RED ||
+ element->right->color == SEL_ARG::RED))
+ {
+ sql_print_error("Wrong tree: Found two red in a row");
+ return -1;
+ }
+ if (element->left == element->right && element->left != &null_element)
+ { // Dummy test
+ sql_print_error("Wrong tree: Found right == left");
+ return -1;
+ }
+ count_l=test_rb_tree(element->left,element);
+ count_r=test_rb_tree(element->right,element);
+ if (count_l >= 0 && count_r >= 0)
+ {
+ if (count_l == count_r)
+ return count_l+(element->color == SEL_ARG::BLACK);
+ sql_print_error("Wrong tree: Incorrect black-count: %d - %d",
+ count_l,count_r);
+ }
+ return -1; // Error, no more warnings
+}
+
+static ulong count_key_part_usage(SEL_ARG *root, SEL_ARG *key)
+{
+ ulong count= 0;
+ for (root=root->first(); root ; root=root->next)
+ {
+ if (root->next_key_part)
+ {
+ if (root->next_key_part == key)
+ count++;
+ if (root->next_key_part->part < key->part)
+ count+=count_key_part_usage(root->next_key_part,key);
+ }
+ }
+ return count;
+}
+
+
+void SEL_ARG::test_use_count(SEL_ARG *root)
+{
+ if (this == root && use_count != 1)
+ {
+ sql_print_error("Use_count: Wrong count %lu for root",use_count);
+ return;
+ }
+ if (this->type != SEL_ARG::KEY_RANGE)
+ return;
+ uint e_count=0;
+ for (SEL_ARG *pos=first(); pos ; pos=pos->next)
+ {
+ e_count++;
+ if (pos->next_key_part)
+ {
+ ulong count=count_key_part_usage(root,pos->next_key_part);
+ if (count > pos->next_key_part->use_count)
+ {
+ sql_print_error("Use_count: Wrong count for key at %lx, %lu should be %lu",
+ pos,pos->next_key_part->use_count,count);
+ return;
+ }
+ pos->next_key_part->test_use_count(root);
+ }
+ }
+ if (e_count != elements)
+ sql_print_error("Wrong use count: %u for tree at %lx", e_count,
+ (gptr) this);
+}
+
+#endif
+
+
+
+/*****************************************************************************
+** Check how many records we will find by using the found tree
+*****************************************************************************/
+
+static ha_rows
+check_quick_select(PARAM *param,uint idx,SEL_ARG *tree)
+{
+ ha_rows records;
+ DBUG_ENTER("check_quick_select");
+
+ if (!tree)
+ DBUG_RETURN(HA_POS_ERROR); // Can't use it
+ if (tree->type == SEL_ARG::IMPOSSIBLE)
+ DBUG_RETURN(0L); // Impossible select. return
+ if (tree->type != SEL_ARG::KEY_RANGE || tree->part != 0)
+ DBUG_RETURN(HA_POS_ERROR); // Don't use tree
+ param->max_key_part=0;
+ records=check_quick_keys(param,idx,tree,param->min_key,0,param->max_key,0);
+ if (records != HA_POS_ERROR)
+ {
+ uint key=param->real_keynr[idx];
+ param->table->quick_keys|= (key_map) 1 << key;
+ param->table->quick_rows[key]=records;
+ param->table->quick_key_parts[key]=param->max_key_part+1;
+ }
+ DBUG_RETURN(records);
+}
+
+
+static ha_rows
+check_quick_keys(PARAM *param,uint idx,SEL_ARG *key_tree,
+ char *min_key,uint min_key_flag, char *max_key,
+ uint max_key_flag)
+{
+ ha_rows records=0,tmp;
+
+ param->max_key_part=max(param->max_key_part,key_tree->part);
+ if (key_tree->left != &null_element)
+ {
+ records=check_quick_keys(param,idx,key_tree->left,min_key,min_key_flag,
+ max_key,max_key_flag);
+ if (records == HA_POS_ERROR) // Impossible
+ return records;
+ }
+
+ uint tmp_min_flag,tmp_max_flag,keynr;
+ char *tmp_min_key=min_key,*tmp_max_key=max_key;
+
+ key_tree->store(param->key[idx][key_tree->part].part_length,
+ &tmp_min_key,min_key_flag,&tmp_max_key,max_key_flag);
+ uint min_key_length= (uint) (tmp_min_key- param->min_key);
+ uint max_key_length= (uint) (tmp_max_key- param->max_key);
+
+ if (key_tree->next_key_part &&
+ key_tree->next_key_part->part == key_tree->part+1 &&
+ key_tree->next_key_part->type == SEL_ARG::KEY_RANGE)
+ { // const key as prefix
+ if (min_key_length == max_key_length &&
+ !memcmp(min_key,max_key, (uint) (tmp_max_key - max_key)) &&
+ !key_tree->min_flag && !key_tree->max_flag)
+ {
+ tmp=check_quick_keys(param,idx,key_tree->next_key_part,
+ tmp_min_key, min_key_flag | key_tree->min_flag,
+ tmp_max_key, max_key_flag | key_tree->max_flag);
+ goto end; // Ugly, but efficient
+ }
+ tmp_min_flag=key_tree->min_flag;
+ tmp_max_flag=key_tree->max_flag;
+ if (!tmp_min_flag)
+ key_tree->next_key_part->store_min_key(param->key[idx], &tmp_min_key,
+ &tmp_min_flag);
+ if (!tmp_max_flag)
+ key_tree->next_key_part->store_max_key(param->key[idx], &tmp_max_key,
+ &tmp_max_flag);
+ min_key_length= (uint) (tmp_min_key- param->min_key);
+ max_key_length= (uint) (tmp_max_key- param->max_key);
+ }
+ else
+ {
+ tmp_min_flag=min_key_flag | key_tree->min_flag;
+ tmp_max_flag=max_key_flag | key_tree->max_flag;
+ }
+
+ keynr=param->real_keynr[idx];
+ if (!tmp_min_flag && ! tmp_max_flag &&
+ (uint) key_tree->part+1 == param->table->key_info[keynr].key_parts &&
+ (param->table->key_info[keynr].flags & HA_NOSAME) &&
+ min_key_length == max_key_length &&
+ !memcmp(param->min_key,param->max_key,min_key_length))
+ tmp=1; // Max one record
+ else
+ tmp=param->table->file->
+ records_in_range((int) keynr,
+ (byte*) (!min_key_length ? NullS :
+ param->min_key),
+ min_key_length,
+ (tmp_min_flag & NEAR_MIN ?
+ HA_READ_AFTER_KEY : HA_READ_KEY_EXACT),
+ (byte*) (!max_key_length ? NullS :
+ param->max_key),
+ max_key_length,
+ (tmp_max_flag & NEAR_MAX ?
+ HA_READ_BEFORE_KEY : HA_READ_AFTER_KEY));
+ end:
+ if (tmp == HA_POS_ERROR) // Impossible range
+ return tmp;
+ records+=tmp;
+ if (key_tree->right != &null_element)
+ {
+ tmp=check_quick_keys(param,idx,key_tree->right,min_key,min_key_flag,
+ max_key,max_key_flag);
+ if (tmp == HA_POS_ERROR)
+ return tmp;
+ records+=tmp;
+ }
+ return records;
+}
+
+
+/****************************************************************************
+** change a tree to a structure to be used by quick_select
+** This uses it's own malloc tree
+****************************************************************************/
+
+static QUICK_SELECT *
+get_quick_select(PARAM *param,uint idx,SEL_ARG *key_tree)
+{
+ QUICK_SELECT *quick;
+ DBUG_ENTER("get_quick_select");
+ if ((quick=new QUICK_SELECT(param->table,param->real_keynr[idx])))
+ {
+ if (quick->error ||
+ get_quick_keys(param,quick,param->key[idx],key_tree,param->min_key,0,
+ param->max_key,0))
+ {
+ delete quick;
+ quick=0;
+ }
+ else
+ {
+ quick->key_parts=(KEY_PART*)
+ sql_memdup(param->key[idx],
+ sizeof(KEY_PART)*
+ param->table->key_info[param->real_keynr[idx]].key_parts);
+ }
+ }
+ DBUG_RETURN(quick);
+}
+
+
+/*
+** Fix this to get all possible sub_ranges
+*/
+
+static bool
+get_quick_keys(PARAM *param,QUICK_SELECT *quick,KEY_PART *key,
+ SEL_ARG *key_tree,char *min_key,uint min_key_flag,
+ char *max_key, uint max_key_flag)
+{
+ QUICK_RANGE *range;
+ uint flag;
+
+ if (key_tree->left != &null_element)
+ {
+ if (get_quick_keys(param,quick,key,key_tree->left,
+ min_key,min_key_flag, max_key, max_key_flag))
+ return 1;
+ }
+ char *tmp_min_key=min_key,*tmp_max_key=max_key;
+ key_tree->store(key[key_tree->part].part_length,
+ &tmp_min_key,min_key_flag,&tmp_max_key,max_key_flag);
+
+ if (key_tree->next_key_part &&
+ key_tree->next_key_part->part == key_tree->part+1 &&
+ key_tree->next_key_part->type == SEL_ARG::KEY_RANGE)
+ { // const key as prefix
+ if (!((tmp_min_key - min_key) != (tmp_max_key - max_key) ||
+ memcmp(min_key,max_key, (uint) (tmp_max_key - max_key)) ||
+ key_tree->min_flag || key_tree->max_flag))
+ {
+ if (get_quick_keys(param,quick,key,key_tree->next_key_part,
+ tmp_min_key, min_key_flag | key_tree->min_flag,
+ tmp_max_key, max_key_flag | key_tree->max_flag))
+ return 1;
+ goto end; // Ugly, but efficient
+ }
+ {
+ uint tmp_min_flag=key_tree->min_flag,tmp_max_flag=key_tree->max_flag;
+ if (!tmp_min_flag)
+ key_tree->next_key_part->store_min_key(key, &tmp_min_key,
+ &tmp_min_flag);
+ if (!tmp_max_flag)
+ key_tree->next_key_part->store_max_key(key, &tmp_max_key,
+ &tmp_max_flag);
+ flag=tmp_min_flag | tmp_max_flag;
+ }
+ }
+ else
+ flag=key_tree->min_flag | key_tree->max_flag;
+
+ /* Ensure that some part of min_key and max_key are used. If not,
+ regard this as no lower/upper range */
+ if (tmp_min_key != param->min_key)
+ flag&= ~NO_MIN_RANGE;
+ else
+ flag|= NO_MIN_RANGE;
+ if (tmp_max_key != param->max_key)
+ flag&= ~NO_MAX_RANGE;
+ else
+ flag|= NO_MAX_RANGE;
+
+ if (flag == 0)
+ {
+ uint length= (uint) (tmp_min_key - param->min_key);
+ if (length == (uint) (tmp_max_key - param->max_key) &&
+ !memcmp(param->min_key,param->max_key,length))
+ {
+ KEY *table_key=quick->head->key_info+quick->index;
+ flag=EQ_RANGE;
+ if (table_key->flags & HA_NOSAME && key->part == table_key->key_parts-1)
+ flag|= UNIQUE_RANGE;
+ }
+ }
+
+ /* Get range for retrieving rows in QUICK_SELECT::get_next */
+ range= new QUICK_RANGE(param->min_key,
+ (uint) (tmp_min_key - param->min_key),
+ param->max_key,
+ (uint) (tmp_max_key - param->max_key),
+ flag);
+ set_if_bigger(quick->max_used_key_length,range->min_length);
+ set_if_bigger(quick->max_used_key_length,range->max_length);
+ if (!range) // Not enough memory
+ return 1;
+ quick->ranges.push_back(range);
+
+ end:
+ if (key_tree->right != &null_element)
+ return get_quick_keys(param,quick,key,key_tree->right,
+ min_key,min_key_flag,
+ max_key,max_key_flag);
+ return 0;
+}
+
+/*
+ Return 1 if there is only one range and this uses the whole primary key
+*/
+
+bool QUICK_SELECT::unique_key_range()
+{
+ if (ranges.elements == 1)
+ {
+ QUICK_RANGE *tmp;
+ if ((tmp=ranges.head())->flag & EQ_RANGE)
+ {
+ KEY *key=head->key_info+index;
+ return ((key->flags & HA_NOSAME) &&
+ key->key_length == tmp->min_length);
+ }
+ }
+ return 0;
+}
+
+/****************************************************************************
+** Create a QUICK RANGE based on a key
+****************************************************************************/
+
+QUICK_SELECT *get_quick_select_for_ref(TABLE *table, TABLE_REF *ref)
+{
+ QUICK_SELECT *quick=new QUICK_SELECT(table, ref->key, 1);
+ KEY *key_info = &table->key_info[ref->key];
+ KEY_PART *key_part;
+ uint part;
+
+ if (!quick)
+ return 0;
+ QUICK_RANGE *range= new QUICK_RANGE();
+ if (!range || cp_buffer_from_ref(ref))
+ goto err;
+ range->min_key=range->max_key=(char*) ref->key_buff;
+ range->min_length=range->max_length=ref->key_length;
+ range->flag= ((ref->key_length == key_info->key_length &&
+ (key_info->flags & HA_NOSAME)) ? EQ_RANGE : 0);
+
+ if (!(quick->key_parts=key_part=(KEY_PART *)
+ sql_alloc(sizeof(KEY_PART)*ref->key_parts)))
+ goto err;
+
+ for (part=0 ; part < ref->key_parts ;part++,key_part++)
+ {
+ key_part->part=part;
+ key_part->field= key_info->key_part[part].field;
+ key_part->part_length= key_info->key_part[part].length;
+ if (key_part->field->type() == FIELD_TYPE_BLOB)
+ key_part->part_length+=HA_KEY_BLOB_LENGTH;
+ key_part->null_bit= key_info->key_part[part].null_bit;
+ }
+ if (!quick->ranges.push_back(range))
+ return quick;
+
+err:
+ delete quick;
+ return 0;
+}
+
+ /* get next possible record using quick-struct */
+
+int QUICK_SELECT::get_next()
+{
+ DBUG_ENTER("get_next");
+
+ for (;;)
+ {
+ if (range)
+ { // Already read through key
+ int result=((range->flag & EQ_RANGE) ?
+ file->index_next_same(record, (byte*) range->min_key,
+ range->min_length) :
+ file->index_next(record));
+ if (!result && !cmp_next(*it.ref()))
+ DBUG_RETURN(0);
+ }
+ if (!(range=it++))
+ 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
+ if (cmp_next(range) == 0)
+ DBUG_RETURN(0); // No matching records
+ range=0; // To next range
+ continue;
+ }
+ if (file->index_read(record,(byte*) range->min_key,
+ range->min_length,
+ ((range->flag & NEAR_MIN) ?
+ HA_READ_AFTER_KEY:
+ (range->flag & EQ_RANGE) ?
+ HA_READ_KEY_EXACT :
+ HA_READ_KEY_OR_NEXT)))
+
+ {
+ range=0; // Not found, to next range
+ continue;
+ }
+ if (cmp_next(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
+ }
+}
+
+ /* compare if found key is over max-value */
+ /* Returns 0 if key <= range->max_key */
+
+int QUICK_SELECT::cmp_next(QUICK_RANGE *range)
+{
+ if (range->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;
+ key < end;
+ key+= key_part++->part_length)
+ {
+ int cmp;
+ if (key_part->null_bit)
+ {
+ if (*key++)
+ {
+ if (!key_part->field->is_null())
+ return 1;
+ continue;
+ }
+ else if (key_part->field->is_null())
+ return 0;
+ }
+ if ((cmp=key_part->field->key_cmp((byte*) key, key_part->part_length)) < 0)
+ return 0;
+ if (cmp > 0)
+ return 1;
+ }
+ return (range->flag & NEAR_MAX) ? 1 : 0; // Exact match
+}
+
+/*****************************************************************************
+** Print a quick range for debugging
+** TODO:
+** This should be changed to use a String to store each row instead
+** of locking the DEBUG stream !
+*****************************************************************************/
+
+#ifndef DBUG_OFF
+
+static void
+print_key(KEY_PART *key_part,const char *key,uint used_length)
+{
+ char buff[1024];
+ String tmp(buff,sizeof(buff));
+
+ for (uint length=0;
+ length < used_length ;
+ length+=key_part->part_length, key+=key_part->part_length, key_part++)
+ {
+ Field *field=key_part->field;
+ if (length != 0)
+ fputc('/',DBUG_FILE);
+ if (field->real_maybe_null())
+ {
+ length++;
+ if (*key++)
+ {
+ fwrite("NULL",sizeof(char),4,DBUG_FILE);
+ continue;
+ }
+ }
+ field->set_key_image((char*) key,key_part->part_length);
+ field->val_str(&tmp,&tmp);
+ fwrite(tmp.ptr(),sizeof(char),tmp.length(),DBUG_FILE);
+ }
+}
+
+static void print_quick(QUICK_SELECT *quick,key_map needed_reg)
+{
+ QUICK_RANGE *range;
+ DBUG_ENTER("print_param");
+ if (! _db_on_ || !quick)
+ DBUG_VOID_RETURN;
+
+ List_iterator<QUICK_RANGE> li(quick->ranges);
+ DBUG_LOCK_FILE;
+ fprintf(DBUG_FILE,"Used quick_range on key: %d (other_keys: %lu):\n",
+ quick->index, (ulong) needed_reg);
+ while ((range=li++))
+ {
+ if (!(range->flag & NO_MIN_RANGE))
+ {
+ print_key(quick->key_parts,range->min_key,range->min_length);
+ if (range->flag & NEAR_MIN)
+ fputs(" < ",DBUG_FILE);
+ else
+ fputs(" <= ",DBUG_FILE);
+ }
+ fputs("X",DBUG_FILE);
+
+ if (!(range->flag & NO_MAX_RANGE))
+ {
+ if (range->flag & NEAR_MAX)
+ fputs(" < ",DBUG_FILE);
+ else
+ fputs(" <= ",DBUG_FILE);
+ print_key(quick->key_parts,range->max_key,range->max_length);
+ }
+ fputs("\n",DBUG_FILE);
+ }
+ DBUG_UNLOCK_FILE;
+ DBUG_VOID_RETURN;
+}
+
+#endif
+
+/*****************************************************************************
+** Instansiate templates
+*****************************************************************************/
+
+#ifdef __GNUC__
+template class List<QUICK_RANGE>;
+template class List_iterator<QUICK_RANGE>;
+#endif
diff --git a/sql/opt_range.h b/sql/opt_range.h
new file mode 100644
index 00000000000..06775874494
--- /dev/null
+++ b/sql/opt_range.h
@@ -0,0 +1,106 @@
+/* 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 use when handling where clause */
+
+#ifndef _opt_range_h
+#define _opt_range_h
+
+#ifdef __GNUC__
+#pragma interface /* gcc class implementation */
+#endif
+
+#define NO_MIN_RANGE 1
+#define NO_MAX_RANGE 2
+#define NEAR_MIN 4
+#define NEAR_MAX 8
+#define UNIQUE_RANGE 16
+#define EQ_RANGE 32
+
+typedef struct st_key_part {
+ uint16 key,part,part_length;
+ uint8 null_bit;
+ Field *field;
+} KEY_PART;
+
+class QUICK_RANGE :public Sql_alloc {
+ public:
+ char *min_key,*max_key;
+ uint16 min_length,max_length,flag;
+ 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)
+ {}
+};
+
+class QUICK_SELECT {
+public:
+ bool next;
+ int error;
+ uint index,max_used_key_length;
+ TABLE *head;
+ handler *file;
+ byte *record;
+ List<QUICK_RANGE> ranges;
+ List_iterator<QUICK_RANGE> it;
+ QUICK_RANGE *range;
+ MEM_ROOT alloc;
+
+ KEY_PART *key_parts;
+ ha_rows records;
+ double read_time;
+
+ QUICK_SELECT(TABLE *table,uint index_arg,bool no_alloc=0);
+ ~QUICK_SELECT();
+ void reset(void) { next=0; it.rewind(); }
+ int get_next();
+ int cmp_next(QUICK_RANGE *range);
+ bool unique_key_range();
+};
+
+
+class SQL_SELECT :public Sql_alloc {
+ public:
+ QUICK_SELECT *quick; // If quick-select used
+ COND *cond; // where condition
+ TABLE *head;
+ IO_CACHE file; // Positions to used records
+ ha_rows records; // Records in use if read from file
+ double read_time; // Time to read rows
+ key_map quick_keys; // Possible quick keys
+ key_map needed_reg; // Possible quick keys after prev tables.
+ table_map const_tables,read_tables;
+ bool free_cond;
+
+ SQL_SELECT();
+ ~SQL_SELECT();
+ bool check_quick(bool force_quick_range=0, ha_rows limit = HA_POS_ERROR)
+ { return test_quick_select(~0L,0,limit, force_quick_range) < 0; }
+ inline bool skipp_record() { return cond ? cond->val_int() == 0 : 0; }
+ int test_quick_select(key_map keys,table_map prev_tables,ha_rows limit,
+ bool force_quick_range=0);
+};
+
+QUICK_SELECT *get_quick_select_for_ref(TABLE *table, struct st_table_ref *ref);
+
+#endif
diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc
new file mode 100644
index 00000000000..beac829bb74
--- /dev/null
+++ b/sql/opt_sum.cc
@@ -0,0 +1,359 @@
+/* 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 */
+
+
+/* Optimizing of many different type of queries with GROUP functions */
+
+#include "mysql_priv.h"
+#include "sql_select.h"
+
+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
+****************************************************************************/
+
+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;
+ Item *item;
+
+ while ((item= it++))
+ {
+ if (item->type() == Item::SUM_FUNC_ITEM)
+ {
+ Item_sum *item_sum= (((Item_sum*) item));
+ switch (item_sum->sum_func()) {
+ case Item_sum::COUNT_FUNC:
+ /*
+ If the expr in count(expr) can never be null we can change this
+ to the number of rows in the tables
+ */
+ if (!conds && !((Item_sum_count*) item)->args[0]->maybe_null)
+ {
+ longlong count=1;
+ TABLE_LIST *table;
+ for (table=tables; table ; table=table->next)
+ {
+ if (table->on_expr || (table->table->file->option_flag() &
+ HA_NOT_EXACT_COUNT))
+ {
+ const_result=0; // Can't optimize left join
+ break;
+ }
+ tables->table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
+ count*= table->table->file->records;
+ }
+ if (!table)
+ {
+ ((Item_sum_count*) item)->make_const(count);
+ recalc_const_item=1;
+ }
+ }
+ else
+ const_result=0;
+ break;
+ case Item_sum::MIN_FUNC:
+ {
+ /*
+ If MIN(expr) is the first part of a key or if all previous
+ parts of the key is found in the COND, then we can use
+ indexes to find the key.
+ */
+ Item *expr=item_sum->args[0];
+ if (expr->type() == Item::FIELD_ITEM)
+ {
+ byte key_buff[MAX_KEY_LENGTH];
+ TABLE_REF ref;
+ ref.key_buff=key_buff;
+
+ if (!find_range_key(&ref, ((Item_field*) expr)->field,conds))
+ {
+ const_result=0;
+ break;
+ }
+ TABLE *table=((Item_field*) expr)->field->table;
+ bool error=table->file->index_init((uint) ref.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);
+ if (table->key_read)
+ {
+ table->key_read=0;
+ table->file->extra(HA_EXTRA_NO_KEYREAD);
+ }
+ table->file->index_end();
+ if (error)
+ return -1; // Impossible query
+ removed_tables|= table->map;
+ }
+ else if (!expr->const_item()) // This is VERY seldom false
+ {
+ const_result=0;
+ break;
+ }
+ ((Item_sum_min*) item_sum)->reset();
+ ((Item_sum_min*) item_sum)->make_const();
+ recalc_const_item=1;
+ break;
+ }
+ case Item_sum::MAX_FUNC:
+ {
+ /*
+ If MAX(expr) is the first part of a key or if all previous
+ parts of the key is found in the COND, then we can use
+ indexes to find the key.
+ */
+ Item *expr=item_sum->args[0];
+ if (expr->type() == Item::FIELD_ITEM)
+ {
+ byte key_buff[MAX_KEY_LENGTH];
+ TABLE_REF ref;
+ ref.key_buff=key_buff;
+
+ if (!find_range_key(&ref, ((Item_field*) expr)->field,conds))
+ {
+ const_result=0;
+ break;
+ }
+ TABLE *table=((Item_field*) expr)->field->table;
+ bool error=table->file->index_init((uint) ref.key);
+
+ if (!ref.key_length)
+ error=table->file->index_last(table->record[0]) !=0;
+ else
+ {
+ (void) table->file->index_read(table->record[0], key_buff,
+ ref.key_length,
+ HA_READ_AFTER_KEY);
+ error=table->file->index_prev(table->record[0]) ||
+ key_cmp(table,key_buff,ref.key,ref.key_length);
+ }
+ if (table->key_read)
+ {
+ table->key_read=0;
+ table->file->extra(HA_EXTRA_NO_KEYREAD);
+ }
+ table->file->index_end();
+ if (error)
+ return -1; // Impossible query
+ removed_tables|= table->map;
+ }
+ else if (!expr->const_item()) // This is VERY seldom false
+ {
+ const_result=0;
+ break;
+ }
+ ((Item_sum_min*) item_sum)->reset();
+ ((Item_sum_min*) item_sum)->make_const();
+ recalc_const_item=1;
+ break;
+ }
+ default:
+ const_result=0;
+ break;
+ }
+ }
+ else if (const_result)
+ {
+ if (recalc_const_item)
+ item->update_used_tables();
+ if (!item->const_item())
+ const_result=0;
+ }
+ }
+ if (conds && (conds->used_tables() & ~ removed_tables))
+ const_result=0;
+ return const_result;
+}
+
+/* Count in how many times table is used (up to MAX_KEY_PARTS+1) */
+
+uint count_table_entries(COND *cond,TABLE *table)
+{
+ if (cond->type() == Item::COND_ITEM)
+ {
+ 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());
+ Item *item;
+ uint count=0;
+ while ((item=li++))
+ {
+ if ((count+=count_table_entries(item,table)) > MAX_REF_PARTS)
+ return MAX_REF_PARTS+1;
+ }
+ return count;
+ }
+ if (cond->type() == Item::FUNC_ITEM &&
+ (((Item_func*) cond)->functype() == Item_func::EQ_FUNC ||
+ (((Item_func*) cond)->functype() == Item_func::EQUAL_FUNC)) &&
+ cond->used_tables() == table->map)
+ {
+ Item *left_item= ((Item_func*) cond)->arguments()[0];
+ Item *right_item= ((Item_func*) cond)->arguments()[1];
+ if (left_item->type() == Item::FIELD_ITEM)
+ {
+ if (!(((Item_field*) left_item)->field->flags & PART_KEY_FLAG) ||
+ !right_item->const_item())
+ return MAX_REF_PARTS+1;
+ return 1;
+ }
+ if (right_item->type() == Item::FIELD_ITEM)
+ {
+ if (!(((Item_field*) right_item)->field->flags & PART_KEY_FLAG) ||
+ !left_item->const_item())
+ return MAX_REF_PARTS+1;
+ return 1;
+ }
+ }
+ return (cond->used_tables() & table->map) ? MAX_REF_PARTS+1 : 0;
+}
+
+
+/* check that the field is usable as key part */
+
+bool part_of_cond(COND *cond,Field *field)
+{
+ if (cond->type() == Item::COND_ITEM)
+ {
+ if (((Item_cond*) cond)->functype() == Item_func::COND_OR_FUNC)
+ return 0; // Already checked
+
+ List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
+ Item *item;
+ while ((item=li++))
+ {
+ if (part_of_cond(item,field))
+ return 1;
+ }
+ return 0;
+ }
+ if (cond->type() == Item::FUNC_ITEM &&
+ (((Item_func*) cond)->functype() == Item_func::EQ_FUNC ||
+ ((Item_func*) cond)->functype() == Item_func::EQUAL_FUNC) &&
+ cond->used_tables() == field->table->map)
+ {
+ Item *left_item= ((Item_func*) cond)->arguments()[0];
+ Item *right_item= ((Item_func*) cond)->arguments()[1];
+ if (left_item->type() == Item::FIELD_ITEM)
+ {
+ if (((Item_field*) left_item)->field != field ||
+ !right_item->const_item())
+ return 0;
+ }
+ else if (right_item->type() == Item::FIELD_ITEM)
+ {
+ if (((Item_field*) right_item)->field != field ||
+ !left_item->const_item())
+ return 0;
+ right_item=left_item; // const item in right
+ }
+ store_val_in_field(field,right_item);
+ return 1;
+ }
+ return 0;
+}
+
+
+/* Check if we can get value for field by using a key */
+
+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
+
+ 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)))
+ {
+ for (key_map key=field->key_start ; !(key & 1) ; idx++)
+ key>>=1;
+ ref->key_length=0;
+ ref->key=idx;
+ if (field->part_of_key & ((table_map) 1 << idx))
+ {
+ table->key_read=1;
+ table->file->extra(HA_EXTRA_KEYREAD);
+ }
+ return 1; // Ok to use key
+ }
+ /*
+ ** Check if WHERE consist of exactly the previous key parts for some key
+ */
+ if (!cond)
+ return 0;
+ uint table_entries= count_table_entries(cond,table);
+ if (!table_entries || table_entries > MAX_REF_PARTS)
+ return 0;
+
+ KEY *keyinfo,*keyinfo_end;
+ for (keyinfo=table->key_info, keyinfo_end=keyinfo+table->keys ;
+ keyinfo != keyinfo_end;
+ keyinfo++,idx++)
+ {
+ if (table_entries < keyinfo->key_parts)
+ {
+ byte *key_ptr=ref->key_buff;
+ KEY_PART_INFO *part,*part_end;
+ int left_length=MAX_KEY_LENGTH;
+
+ for (part=keyinfo->key_part, part_end=part+table_entries ;
+ part != part_end ;
+ part++)
+ {
+ if (!part_of_cond(cond,part->field))
+ break;
+ // Save found constant
+ if (part->null_bit)
+ *key_ptr++= (byte) test(part->field->is_null());
+ if (left_length - part->length < 0)
+ break; // Can't use this key
+ part->field->get_image((char*) key_ptr,part->length);
+ key_ptr+=part->length;
+ left_length-=part->length;
+ }
+ if (part == part_end && part->field == field)
+ {
+ ref->key_length= (uint) (key_ptr-ref->key_buff);
+ ref->key=idx;
+ if (field->part_of_key & ((table_map) 1 << idx))
+ {
+ table->key_read=1;
+ table->file->extra(HA_EXTRA_KEYREAD);
+ }
+ return 1; // Ok to use key
+ }
+ }
+ }
+ return 0; // No possible key
+}
diff --git a/sql/password.c b/sql/password.c
new file mode 100644
index 00000000000..63ab9def651
--- /dev/null
+++ b/sql/password.c
@@ -0,0 +1,191 @@
+/* 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 */
+
+/* password checking routines */
+/*****************************************************************************
+ The main idea is that no password are sent between client & server on
+ connection and that no password are saved in mysql in a decodable form.
+
+ On connection a random string is generated and sent to the client.
+ The client generates a new string with a random generator inited with
+ the hash values from the password and the sent string.
+ This 'check' string is sent to the server where it is compared with
+ a string generated from the stored hash_value of the password and the
+ random string.
+
+ The password is saved (in user.password) by using the PASSWORD() function in
+ mysql.
+
+ Example:
+ update user set password=PASSWORD("hello") where user="test"
+ This saves a hashed number as a string in the password field.
+*****************************************************************************/
+
+#include <global.h>
+#include <my_sys.h>
+#include <m_string.h>
+#include "mysql.h"
+
+
+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 */
+#endif
+ rand_st->max_value= 0x3FFFFFFFL;
+ rand_st->max_value_dbl=(double) rand_st->max_value;
+ rand_st->seed1=seed1%rand_st->max_value ;
+ rand_st->seed2=seed2%rand_st->max_value;
+}
+
+static void old_randominit(struct rand_struct *rand_st,ulong seed1)
+{ /* For mysql 3.20.# */
+ rand_st->max_value= 0x01FFFFFFL;
+ rand_st->max_value_dbl=(double) rand_st->max_value;
+ seed1%=rand_st->max_value;
+ rand_st->seed1=seed1 ; rand_st->seed2=seed1/2;
+}
+
+double rnd(struct rand_struct *rand_st)
+{
+ rand_st->seed1=(rand_st->seed1*3+rand_st->seed2) % rand_st->max_value;
+ rand_st->seed2=(rand_st->seed1+rand_st->seed2+33) % rand_st->max_value;
+ return (((double) rand_st->seed1)/rand_st->max_value_dbl);
+}
+
+void hash_password(ulong *result, const char *password)
+{
+ register ulong nr=1345345333L, add=7, nr2=0x12345671L;
+ ulong tmp;
+ for (; *password ; password++)
+ {
+ if (*password == ' ' || *password == '\t')
+ continue; /* skipp space in password */
+ tmp= (ulong) (uchar) *password;
+ nr^= (((nr & 63)+add)*tmp)+ (nr << 8);
+ nr2+=(nr2 << 8) ^ nr;
+ add+=tmp;
+ }
+ result[0]=nr & (((ulong) 1L << 31) -1L); /* Don't use sign bit (str2int) */;
+ result[1]=nr2 & (((ulong) 1L << 31) -1L);
+ return;
+}
+
+void make_scrambled_password(char *to,const char *password)
+{
+ ulong hash_res[2];
+ hash_password(hash_res,password);
+ sprintf(to,"%08lx%08lx",hash_res[0],hash_res[1]);
+}
+
+static inline uint char_val(char X)
+{
+ return (uint) (X >= '0' && X <= '9' ? X-'0' :
+ X >= 'A' && X <= 'Z' ? X-'A'+10 :
+ X-'a'+10);
+}
+
+/*
+** This code assumes that len(password) is divideable with 8 and that
+** res is big enough (2 in mysql)
+*/
+
+void get_salt_from_password(ulong *res,const char *password)
+{
+ res[0]=res[1]=0;
+ if (password)
+ {
+ while (*password)
+ {
+ ulong val=0;
+ uint i;
+ for (i=0 ; i < 8 ; i++)
+ val=(val << 4)+char_val(*password++);
+ *res++=val;
+ }
+ }
+ return;
+}
+
+void make_password_from_salt(char *to, ulong *hash_res)
+{
+ sprintf(to,"%08lx%08lx",hash_res[0],hash_res[1]);
+}
+
+
+/*
+ * Genererate a new message based on message and password
+ * The same thing is done in client and server and the results are checked.
+ */
+
+char *scramble(char *to,const char *message,const char *password,
+ my_bool old_ver)
+{
+ struct rand_struct rand_st;
+ ulong hash_pass[2],hash_message[2];
+ if (password && password[0])
+ {
+ char *to_start=to;
+ hash_password(hash_pass,password);
+ hash_password(hash_message,message);
+ if (old_ver)
+ old_randominit(&rand_st,hash_pass[0] ^ hash_message[0]);
+ else
+ randominit(&rand_st,hash_pass[0] ^ hash_message[0],
+ hash_pass[1] ^ hash_message[1]);
+ while (*message++)
+ *to++= (char) (floor(rnd(&rand_st)*31)+64);
+ if (!old_ver)
+ { /* Make it harder to break */
+ char extra=(char) (floor(rnd(&rand_st)*31));
+ while (to_start != to)
+ *(to_start++)^=extra;
+ }
+ }
+ *to=0;
+ return to;
+}
+
+
+my_bool check_scramble(const char *scrambled, const char *message,
+ ulong *hash_pass, my_bool old_ver)
+{
+ struct rand_struct rand_st;
+ ulong hash_message[2];
+ char buff[16],*to,extra; /* Big enough for check */
+ const char *pos;
+
+ hash_password(hash_message,message);
+ if (old_ver)
+ old_randominit(&rand_st,hash_pass[0] ^ hash_message[0]);
+ else
+ randominit(&rand_st,hash_pass[0] ^ hash_message[0],
+ hash_pass[1] ^ hash_message[1]);
+ to=buff;
+ for (pos=scrambled ; *pos ; pos++)
+ *to++=(char) (floor(rnd(&rand_st)*31)+64);
+ if (old_ver)
+ extra=0;
+ else
+ extra=(char) (floor(rnd(&rand_st)*31));
+ to=buff;
+ while (*scrambled)
+ {
+ if (*scrambled++ != (char) (*to++ ^ extra))
+ return 1; /* Wrong password */
+ }
+ return 0;
+}
diff --git a/sql/procedure.cc b/sql/procedure.cc
new file mode 100644
index 00000000000..526bbe0feab
--- /dev/null
+++ b/sql/procedure.cc
@@ -0,0 +1,71 @@
+/* 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 */
+
+
+/* Procedures (functions with changes output of select) */
+
+#ifdef __GNUC__
+#pragma implementation // gcc: Class implementation
+#endif
+
+#include "mysql_priv.h"
+#include "procedure.h"
+#include "sql_analyse.h" // Includes procedure
+#ifdef USE_PROC_RANGE
+#include "proc_range.h"
+#endif
+
+static struct st_procedure_def {
+ const char *name;
+ Procedure *(*init)(THD *thd,ORDER *param,select_result *result,
+ List<Item> &field_list);
+} sql_procs[] = {
+#ifdef USE_PROC_RANGE
+ { "split_sum",proc_sum_range_init }, // Internal procedure at TCX
+ { "split_count",proc_count_range_init }, // Internal procedure at TCX
+ { "matris_ranges",proc_matris_range_init }, // Internal procedure at TCX
+#endif
+ { "analyse",proc_analyse_init } // Analyse a result
+};
+
+/*****************************************************************************
+** Setup handling of procedure
+** Return 0 if everything is ok
+*****************************************************************************/
+
+Procedure *
+setup_procedure(THD *thd,ORDER *param,select_result *result,
+ List<Item> &field_list,int *error)
+{
+ uint i;
+ DBUG_ENTER("setup_procedure");
+ *error=0;
+ if (!param)
+ DBUG_RETURN(0);
+ for (i=0 ; i < array_elements(sql_procs) ; i++)
+ {
+ if (!my_strcasecmp((*param->item)->name,sql_procs[i].name))
+ {
+ Procedure *proc=(*sql_procs[i].init)(thd,param,result,field_list);
+ *error= !proc;
+ DBUG_RETURN(proc);
+ }
+ }
+ my_printf_error(ER_UNKNOWN_PROCEDURE,ER(ER_UNKNOWN_PROCEDURE),MYF(0),
+ (*param->item)->name);
+ *error=1;
+ DBUG_RETURN(0);
+}
diff --git a/sql/procedure.h b/sql/procedure.h
new file mode 100644
index 00000000000..7632bf7347c
--- /dev/null
+++ b/sql/procedure.h
@@ -0,0 +1,124 @@
+/* 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 */
+
+
+/* When using sql procedures */
+
+#ifdef __GNUC__
+#pragma interface /* gcc class implementation */
+#endif
+
+#define PROC_NO_SORT 1 /* Bits in flags */
+#define PROC_GROUP 2 /* proc must have group */
+
+/* Procedure items used by procedures to store values for send_fields */
+
+class Item_proc :public Item
+{
+public:
+ Item_proc(const char *name_par): Item()
+ {
+ this->name=(char*) name_par;
+ }
+ enum Type type() const { return Item::PROC_ITEM; }
+ virtual void set(double nr)=0;
+ virtual void set(const char *str,uint length)=0;
+ virtual void set(longlong nr)=0;
+ virtual enum_field_types field_type() const=0;
+ void set(const char *str) { set(str,strlen(str)); }
+ void make_field(Send_field *tmp_field)
+ {
+ init_make_field(tmp_field,field_type());
+ }
+};
+
+class Item_proc_real :public Item_proc
+{
+ double value;
+public:
+ Item_proc_real(const char *name_par,uint dec) : Item_proc(name_par)
+ {
+ decimals=dec; max_length=float_length(dec);
+ }
+ enum Item_result result_type () const { return REAL_RESULT; }
+ enum_field_types field_type() const { return FIELD_TYPE_DOUBLE; }
+ void set(double nr) { value=nr; }
+ void set(longlong nr) { value=(double) nr; }
+ void set(const char *str,uint length __attribute__((unused)))
+ { value=atof(str); }
+ double val() { return value; }
+ longlong val_int() { return (longlong) value; }
+ String *val_str(String *s) { s->set(value,decimals); return s; }
+};
+
+class Item_proc_int :public Item_proc
+{
+ longlong value;
+public:
+ Item_proc_int(const char *name_par) :Item_proc(name_par)
+ { max_length=11; }
+ enum Item_result result_type () const { return INT_RESULT; }
+ enum_field_types field_type() const { return FIELD_TYPE_LONG; }
+ void set(double nr) { value=(longlong) nr; }
+ void set(longlong nr) { value=nr; }
+ void set(const char *str,uint length __attribute__((unused)))
+ { value=strtoll(str,NULL,10); }
+ double val() { return (double) value; }
+ longlong val_int() { return value; }
+ String *val_str(String *s) { s->set(value); return s; }
+};
+
+
+class Item_proc_string :public Item_proc
+{
+public:
+ Item_proc_string(const char *name_par,uint length) :Item_proc(name_par)
+ { this->max_length=length; }
+ enum Item_result result_type () const { return STRING_RESULT; }
+ enum_field_types field_type() const { return FIELD_TYPE_STRING; }
+ void set(double nr) { str_value.set(nr); }
+ void set(longlong nr) { str_value.set(nr); }
+ void set(const char *str, uint length) { str_value.copy(str,length); }
+ double val() { return atof(str_value.ptr()); }
+ longlong val_int() { return strtoll(str_value.ptr(),NULL,10); }
+ String *val_str(String*)
+ {
+ return null_value ? (String*) 0 : (String*) &str_value;
+ }
+};
+
+/* The procedure class definitions */
+
+class Procedure {
+protected:
+ List<Item> *fields;
+ select_result *result;
+public:
+ const uint flags;
+ ORDER *group,*param_fields;
+ Procedure(select_result *res,uint flags_par) :result(res),flags(flags_par),
+ group(0),param_fields(0) {}
+ virtual ~Procedure() {group=param_fields=0; fields=0; }
+ virtual void add(void)=0;
+ virtual void end_group(void)=0;
+ virtual int send_row(List<Item> &fields)=0;
+ virtual bool change_columns(List<Item> &fields)=0;
+ virtual void update_refs(void) {}
+ virtual bool end_of_records() { return 0; }
+};
+
+Procedure *setup_procedure(THD *thd,ORDER *proc_param,select_result *result,
+ List<Item> &field_list,int *error);
diff --git a/sql/records.cc b/sql/records.cc
new file mode 100644
index 00000000000..0493e4183eb
--- /dev/null
+++ b/sql/records.cc
@@ -0,0 +1,335 @@
+/* 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 */
+
+
+/* Functions to read, write and lock records */
+
+#include "mysql_priv.h"
+
+static int rr_quick(READ_RECORD *info);
+static int rr_sequential(READ_RECORD *info);
+static int rr_from_tempfile(READ_RECORD *info);
+static int rr_from_pointers(READ_RECORD *info);
+static int rr_from_cache(READ_RECORD *info);
+static int init_rr_cache(READ_RECORD *info);
+static int rr_cmp(uchar *a,uchar *b);
+
+ /* init struct for read with info->read_record */
+
+void init_read_record(READ_RECORD *info,THD *thd, TABLE *table,
+ SQL_SELECT *select,
+ bool use_record_cache, bool print_error)
+{
+ IO_CACHE *tempfile;
+ DBUG_ENTER("init_read_record");
+
+ bzero((char*) info,sizeof(*info));
+ info->thd=thd;
+ info->table=table;
+ info->file= table->file;
+ info->forms= &info->table; /* Only one table */
+ info->record=table->record[0];
+ info->ref_length=table->file->ref_length;
+ info->select=select;
+ info->print_error=print_error;
+ table->status=0; /* And it's allways 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
+ {
+ DBUG_PRINT("info",("using rr_from_tempfile"));
+ info->read_record=rr_from_tempfile;
+ info->io_cache=tempfile;
+ reinit_io_cache(info->io_cache,READ_CACHE,0L,0,0);
+ info->ref_pos=table->file->ref;
+ table->file->rnd_init(0);
+
+ if (! (specialflag & SPECIAL_SAFE_MODE) &&
+ my_default_record_cache_size &&
+ !table->file->fast_key_read() &&
+ (table->db_stat & HA_READ_ONLY ||
+ table->reginfo.lock_type == TL_READ) &&
+ (ulonglong) table->reclength*(table->file->records+
+ table->file->deleted) >
+ (ulonglong) MIN_FILE_LENGTH_TO_USE_ROW_CACHE &&
+ info->io_cache->end_of_file/info->ref_length*table->reclength >
+ (my_off_t) MIN_ROWS_TO_USE_TABLE_CACHE &&
+ !table->blob_fields)
+ {
+ if (! init_rr_cache(info))
+ {
+ DBUG_PRINT("info",("using rr_from_cache"));
+ info->read_record=rr_from_cache;
+ }
+ }
+ }
+ else if (table->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;
+ info->read_record= rr_from_pointers;
+ }
+ else
+ {
+ DBUG_PRINT("info",("using rr_sequential"));
+ info->read_record=rr_sequential;
+ table->file->rnd_init();
+ /* We can use record cache if we don't update dynamic length tables */
+ if (use_record_cache ||
+ (int) table->reginfo.lock_type <= (int) TL_READ_HIGH_PRIORITY ||
+ !(table->db_options_in_use & HA_OPTION_PACK_RECORD))
+ VOID(table->file->extra(HA_EXTRA_CACHE)); // Cache reads
+ }
+ DBUG_VOID_RETURN;
+} /* init_read_record */
+
+
+void end_read_record(READ_RECORD *info)
+{ /* free cache if used */
+ if (info->cache)
+ {
+ my_free_lock((char*) info->cache,MYF(0));
+ info->cache=0;
+ }
+ if (info->table)
+ {
+ (void) info->file->extra(HA_EXTRA_NO_CACHE);
+ (void) info->file->rnd_end();
+ info->table=0;
+ }
+}
+
+ /* Read a record from head-database */
+
+static int rr_quick(READ_RECORD *info)
+{
+ int tmp=info->select->quick->get_next();
+ if (tmp)
+ {
+ if (tmp == HA_ERR_END_OF_FILE)
+ tmp= -1;
+ else if (info->print_error)
+ info->file->print_error(tmp,MYF(0));
+ }
+ return tmp;
+}
+
+
+static int rr_sequential(READ_RECORD *info)
+{
+ int tmp;
+ while ((tmp=info->file->rnd_next(info->record)))
+ {
+ if (info->thd->killed)
+ {
+ my_error(ER_SERVER_SHUTDOWN,MYF(0));
+ return 1;
+ }
+ if (tmp != HA_ERR_RECORD_DELETED)
+ {
+ if (tmp == HA_ERR_END_OF_FILE)
+ tmp= -1;
+ else if (info->print_error)
+ info->table->file->print_error(tmp,MYF(0));
+ break;
+ }
+ }
+ return tmp;
+}
+
+
+static int rr_from_tempfile(READ_RECORD *info)
+{
+ if (my_b_read(info->io_cache,info->ref_pos,info->ref_length))
+ return -1; /* End of file */
+ if (TEST_IF_LASTREF(info->ref_pos,info->ref_length))
+ return -1; /* File ends with this */
+ int tmp=info->file->rnd_pos(info->record,info->ref_pos);
+ if (tmp)
+ {
+ if (tmp == HA_ERR_END_OF_FILE)
+ tmp= -1;
+ else if (info->print_error)
+ info->file->print_error(tmp,MYF(0));
+ }
+ return tmp;
+} /* rr_from_tempfile */
+
+
+static int rr_from_pointers(READ_RECORD *info)
+{
+ if (info->cache_pos == info->cache_end)
+ return -1; /* End of file */
+ byte *cache_pos=info->cache_pos;
+ info->cache_pos+=info->ref_length;
+
+ int tmp=info->file->rnd_pos(info->record,cache_pos);
+ if (tmp)
+ {
+ if (tmp == HA_ERR_END_OF_FILE)
+ tmp= -1;
+ else if (info->print_error)
+ info->file->print_error(tmp,MYF(0));
+ }
+ return tmp;
+}
+
+ /* cacheing of records from a database */
+
+static int init_rr_cache(READ_RECORD *info)
+{
+ uint rec_cache_size;
+ DBUG_ENTER("init_rr_cache");
+
+ info->struct_length=3+MAX_REFLENGTH;
+ info->reclength=ALIGN_SIZE(info->table->reclength+1);
+ if (info->reclength < info->struct_length)
+ info->reclength=ALIGN_SIZE(info->struct_length);
+
+ info->error_offset=info->table->reclength;
+ info->cache_records=my_default_record_cache_size/
+ (info->reclength+info->struct_length);
+ rec_cache_size=info->cache_records*info->reclength;
+ info->rec_cache_size=info->cache_records*info->ref_length;
+
+ if (info->cache_records <= 2 ||
+ !(info->cache=(byte*) my_malloc_lock(rec_cache_size+info->cache_records*
+ info->struct_length,
+ MYF(0))))
+ DBUG_RETURN(1);
+#ifdef HAVE_purify
+ bzero(info->cache,rec_cache_size); // Avoid warnings in qsort
+#endif
+ DBUG_PRINT("info",("Allocated buffert for %d records",info->cache_records));
+ info->read_positions=info->cache+rec_cache_size;
+ info->cache_pos=info->cache_end=info->cache;
+ DBUG_RETURN(0);
+} /* init_rr_cache */
+
+
+static int rr_from_cache(READ_RECORD *info)
+{
+ reg1 uint i;
+ ulong length;
+ my_off_t rest_of_file;
+ int16 error;
+ byte *position,*ref_position,*record_pos;
+ ulong record;
+
+ for (;;)
+ {
+ if (info->cache_pos != info->cache_end)
+ {
+ if (info->cache_pos[info->error_offset])
+ {
+ shortget(error,info->cache_pos);
+ if (info->print_error)
+ info->table->file->print_error(error,MYF(0));
+ }
+ else
+ {
+ error=0;
+ memcpy(info->record,info->cache_pos,(size_t) info->table->reclength);
+ }
+ info->cache_pos+=info->reclength;
+ return ((int) error);
+ }
+ length=info->rec_cache_size;
+ rest_of_file=info->io_cache->end_of_file - my_b_tell(info->io_cache);
+ if ((my_off_t) length > rest_of_file)
+ length= (ulong) rest_of_file;
+ if (!length || my_b_read(info->io_cache,info->cache,length))
+ {
+ DBUG_PRINT("info",("Found end of file"));
+ return -1; /* End of file */
+ }
+
+ length/=info->ref_length;
+ position=info->cache;
+ ref_position=info->read_positions;
+ for (i=0 ; i < length ; i++,position+=info->ref_length)
+ {
+ if (memcmp(position,last_ref,(size_s) info->ref_length) == 0)
+ { /* End of file */
+ if (!i)
+ {
+ DBUG_PRINT("info",("Found end of file"));
+ return -1; /* Last record and no in buffert */
+ }
+ length=i; // rows in buffer
+ break;
+ }
+ memcpy(ref_position,position,(size_s) info->ref_length);
+ ref_position+=MAX_REFLENGTH;
+ int3store(ref_position,(long) i);
+ ref_position+=3;
+ }
+ qsort(info->read_positions,length,info->struct_length,(qsort_cmp) rr_cmp);
+
+ position=info->read_positions;
+ for (i=0 ; i < length ; i++)
+ {
+ memcpy(info->ref_pos,position,(size_s) info->ref_length);
+ position+=MAX_REFLENGTH;
+ record=uint3korr(position);
+ position+=3;
+ record_pos=info->cache+record*info->reclength;
+ if ((error=(int16) info->file->rnd_pos(record_pos,info->ref_pos)))
+ {
+ record_pos[info->error_offset]=1;
+ shortstore(record_pos,error);
+ DBUG_PRINT("error",("Got error: %d:%d when reading row",
+ my_errno, error));
+ }
+ else
+ record_pos[info->error_offset]=0;
+ }
+ info->cache_end=(info->cache_pos=info->cache)+length*info->reclength;
+ }
+} /* rr_from_cache */
+
+
+static int rr_cmp(uchar *a,uchar *b)
+{
+ if (a[0] != b[0])
+ return (int) a[0] - (int) b[0];
+ if (a[1] != b[1])
+ return (int) a[1] - (int) b[1];
+ if (a[2] != b[2])
+ return (int) a[2] - (int) b[2];
+#if MAX_REFLENGTH == 4
+ return (int) a[3] - (int) b[3];
+#else
+ if (a[3] != b[3])
+ return (int) a[3] - (int) b[3];
+ if (a[4] != b[4])
+ return (int) a[4] - (int) b[4];
+ if (a[5] != b[5])
+ return (int) a[1] - (int) b[5];
+ if (a[6] != b[6])
+ return (int) a[6] - (int) b[6];
+ return (int) a[7] - (int) b[7];
+#endif
+}
diff --git a/sql/share/.cvsignore b/sql/share/.cvsignore
new file mode 100644
index 00000000000..282522db034
--- /dev/null
+++ b/sql/share/.cvsignore
@@ -0,0 +1,2 @@
+Makefile
+Makefile.in
diff --git a/sql/share/Makefile.am b/sql/share/Makefile.am
new file mode 100755
index 00000000000..9b0f0a2991f
--- /dev/null
+++ b/sql/share/Makefile.am
@@ -0,0 +1,22 @@
+## Process this file with automake to create Makefile.in
+
+# This requires gnu cp at distribution time.
+dist-hook:
+ for lang in @AVAILABLE_LANGUAGES@ charsets; \
+ do cp -a $(srcdir)/$$lang $(distdir); done
+
+install-data-local:
+ for lang in @AVAILABLE_LANGUAGES@; \
+ do \
+ $(mkinstalldirs) $(DESTDIR)$(pkgdatadir)/$$lang; \
+ ../../extra/comp_err $(srcdir)/$$lang/errmsg.txt $(srcdir)/$$lang/errmsg.sys; \
+ $(INSTALL_DATA) $(srcdir)/$$lang/errmsg.sys \
+ $(DESTDIR)$(pkgdatadir)/$$lang/errmsg.sys; \
+ $(INSTALL_DATA) $(srcdir)/$$lang/errmsg.txt \
+ $(DESTDIR)$(pkgdatadir)/$$lang/errmsg.txt; \
+ done
+ $(mkinstalldirs) $(DESTDIR)$(pkgdatadir)/charsets
+ (for f in Index README "*.conf"; \
+ do \
+ $(INSTALL_DATA) $(srcdir)/charsets/$$f $(DESTDIR)$(pkgdatadir)/charsets/; \
+ done)
diff --git a/sql/share/charsets/Index b/sql/share/charsets/Index
new file mode 100644
index 00000000000..8d5e7576d56
--- /dev/null
+++ b/sql/share/charsets/Index
@@ -0,0 +1,37 @@
+# sql/share/charsets/Index
+#
+# This file lists all of the available character sets.
+
+# THE ORDER IN WHICH CHARACTER SETS ARE LISTED IS IMPORTANT. See the
+# README file in this directory for details.
+
+
+big5 # 1
+czech # 2
+dec8 # 3
+dos # 4
+german1 # 5
+hp8 # 6
+koi8_ru # 7
+latin1 # 8
+latin2 # 9
+swe7 # 10
+usa7 # 11
+ujis # 12
+sjis # 13
+cp1251 # 14
+danish # 15
+hebrew # 16
+win1251 # 17
+tis620 # 18
+euc_kr # 19
+estonia # 20
+hungarian # 21
+koi8_ukr # 22
+win1251ukr # 23
+gb2312 # 24
+greek # 25
+win1250 # 26
+croat # 27
+gbk # 28
+cp1257 # 29
diff --git a/sql/share/charsets/README b/sql/share/charsets/README
new file mode 100644
index 00000000000..80da6ba9665
--- /dev/null
+++ b/sql/share/charsets/README
@@ -0,0 +1,43 @@
+This directory holds configuration files which allow MySQL to work with
+different character sets. It contains:
+
+*.conf
+ Each conf file contains four tables which describe character types,
+ lower- and upper-case equivalencies and sorting orders for the
+ character values in the set.
+
+Index
+ The Index file lists all of the available charset configurations.
+
+ THE ORDER OF THE CHARACTER SETS IN THIS FILE IS SIGNIFICANT.
+ The first character set is number 1, the second is number 2, etc. The
+ number is stored IN THE DATABASE TABLE FILES and must not be changed.
+ Always add new character sets to the end of the list, so that the
+ numbers of the other character sets will not be changed.
+
+Compiled in or configuration file?
+ When should a character set be compiled in to MySQL's string library
+ (libmystrings), and when should it be placed in a configuration
+ file?
+
+ If the character set requires the strcoll functions or is a
+ multi-byte character set, it MUST be compiled in to the string
+ library. If it does not require these functions, it should be
+ placed in a configuration file.
+
+ If the character set uses any one of the strcoll functions, it
+ must define all of them. Likewise, if the set uses one of the
+ multi-byte functions, it must define them all. See the manual for
+ more information on how to add a complex character set to MySQL.
+
+Syntax of configuration files
+ The syntax is very simple. Comments start with a '#' character and
+ proceed to the end of the line. Words are separated by arbitrary
+ amounts of whitespace.
+
+ For the character set configuration files, every word must be a
+ number in hexadecimal format. The ctype array takes up the first
+ 257 words; the to_lower, to_upper and sort_order arrays take up 256
+ words each after that.
+
+ The Index file is simply a list of the available character sets.
diff --git a/sql/share/charsets/cp1251.conf b/sql/share/charsets/cp1251.conf
new file mode 100644
index 00000000000..97e570757a4
--- /dev/null
+++ b/sql/share/charsets/cp1251.conf
@@ -0,0 +1,74 @@
+# Configuration file for the cp1251 character set
+
+# ctype array (must have 257 elements)
+ 00
+ 20 20 20 20 20 20 20 20 20 28 28 28 28 28 20 20
+ 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
+ 48 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 84 84 84 84 84 84 84 84 84 84 10 10 10 10 10 10
+ 10 81 81 81 81 81 81 01 01 01 01 01 01 01 01 01
+ 01 01 01 01 01 01 01 01 01 01 01 10 10 10 10 10
+ 10 82 82 82 82 82 82 02 02 02 02 02 02 02 02 02
+ 02 02 02 02 02 02 02 02 02 02 02 10 10 10 10 00
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 48 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00
+ 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
+ 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
+ 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02
+ 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02
+
+# to_lower array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
+ 70 71 72 73 74 75 76 77 78 79 7A 5B 5C 5D 5E 5F
+ 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
+ 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
+ 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
+ A0 A1 A2 A3 A4 A5 A6 A7 B8 A9 AA AB AC AD AE AF
+ B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF
+ E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
+ F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
+ E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
+ F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
+
+# to_upper array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F
+ 60 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
+ 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
+ A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
+ B0 B1 B2 B3 B4 B5 B6 B7 A8 B9 BA BB BC BD BE BF
+ C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF
+ C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF
+
+# sort_order array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 7C 7D 7E 7F 80
+ 81 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 82 83 84 85 FF
+ FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
+ FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
+ FF FF FF FF FF FF FF FF 61 FF FF FF FF FF FF FF
+ FF FF FF FF FF FF FF FF 61 FF FF FF FF FF FF FF
+ 5B 5C 5D 5E 5F 60 62 63 64 65 66 67 68 69 6A 6B
+ 6C 6D 6E 6F 70 71 72 73 74 75 76 77 78 79 7A 7B
+ 5B 5C 5D 5E 5F 60 62 63 64 65 66 67 68 69 6A 6B
+ 6C 6D 6E 6F 70 71 72 73 74 75 76 77 78 79 7A 7B
diff --git a/sql/share/charsets/cp1257.conf b/sql/share/charsets/cp1257.conf
new file mode 100644
index 00000000000..610ed5a646f
--- /dev/null
+++ b/sql/share/charsets/cp1257.conf
@@ -0,0 +1,74 @@
+# Configuration file for the cp1257 character set.
+
+# The ctype array must have 257 elements.
+ 00
+ 20 20 20 20 20 20 20 20 20 28 28 28 28 28 20 20
+ 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
+ 48 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 84 84 84 84 84 84 84 84 84 84 10 10 10 10 10 10
+ 10 81 81 81 81 81 81 01 01 01 01 01 01 01 01 01
+ 01 01 01 01 01 01 01 01 01 01 01 10 10 10 10 10
+ 10 82 82 82 82 82 82 02 02 02 02 02 02 02 02 02
+ 02 02 02 02 02 02 02 02 02 02 02 10 10 10 10 00
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 01 01 00 00 00 00 01 00 01 00 00 01 00 00 00 00
+ 01 00 00 00 00 00 00 00 01 00 00 01 00 00 01 00
+ 02 02 00 00 00 00 02 00 02 00 00 02 00 00 00 00
+ 02 00 00 00 00 00 00 00 02 00 00 02 00 00 02 00
+
+# The to_lower array must have 256 elements.
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
+ 70 71 72 73 74 75 76 77 78 79 7A 5B 5C 5D 5E 5F
+ 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
+ 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
+ 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
+ A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
+ B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF
+ E0 E1 C2 C3 C4 C5 E6 C7 E8 C9 CA EB CC CD CE CF
+ F0 D1 D2 D3 D4 D5 D6 D7 F8 D9 DA FB DC DD FE DF
+ E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
+ F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
+
+# The to_upper array must have 256 elements.
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F
+ 60 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
+ 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
+ A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
+ B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF
+ C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF
+ C0 C1 E2 E3 E4 E5 C6 E7 C8 E9 EA CB EC ED EE EF
+ D0 F1 F2 F3 F4 F5 F6 F7 D8 F9 FA DB FC FD DE FF
+
+# The sort_order array must have 256 elements.
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 41 43 44 46 47 4A 4B 4C 4D 50 51 52 53 54 55
+ 56 57 58 59 5B 5C 5F 60 61 4E FF 62 63 64 65 66
+ 67 41 43 44 46 47 4A 4B 4C 4D 50 51 52 53 54 55
+ 56 57 58 59 5B 5C 5F 60 61 4E FF 68 69 6A 6B FF
+ FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
+ FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
+ FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
+ FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
+ 42 4F FF FF FF FF 48 FF 45 FF FF 49 FF FF FF FF
+ 5A FF FF FF FF FF FF FF 5E FF FF 5D FF FF FF FF
+ FF 4F FF FF FF FF 48 FF 45 FF FF 49 FF FF FF FF
+ 5A FF FF FF FF FF FF FF 5E FF FF 5D FF FF FF FF
diff --git a/sql/share/charsets/croat.conf b/sql/share/charsets/croat.conf
new file mode 100644
index 00000000000..fbbe3328547
--- /dev/null
+++ b/sql/share/charsets/croat.conf
@@ -0,0 +1,74 @@
+# Configuration file for the croat character set
+
+# ctype array (must have 257 elements)
+ 00
+ 20 20 20 20 20 20 20 20 20 28 28 28 28 28 20 20
+ 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
+ 48 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 84 84 84 84 84 84 84 84 84 84 10 10 10 10 10 10
+ 10 81 81 81 81 81 81 01 01 01 01 01 01 01 01 01
+ 01 01 01 01 01 01 01 01 01 01 01 10 10 10 10 10
+ 10 82 82 82 82 82 82 02 02 02 02 02 02 02 02 02
+ 02 02 02 02 02 02 02 02 02 02 02 10 10 10 10 20
+ 00 00 00 00 00 00 00 00 00 01 00 00 00 01 00 00
+ 00 00 00 00 00 00 00 00 00 02 00 00 00 02 00 00
+ 48 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
+ 01 01 01 01 01 01 01 10 01 01 01 01 01 01 01 02
+ 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02
+ 02 02 02 02 02 02 02 10 02 02 02 02 02 02 02 02
+
+# to_lower array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
+ 70 71 72 73 74 75 76 77 78 79 7A 5B 5C 5D 5E 5F
+ 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
+ 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 9A 8B 8C 8D 9E 8F
+ 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
+ A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
+ B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF
+ E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
+ F0 F1 F2 F3 F4 F5 F6 D7 F8 F9 FA FB FC FD FE DF
+ E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
+ F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
+
+# to_upper array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F
+ 60 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
+ 90 91 92 93 94 95 96 97 98 99 8A 9B 9C 9D 8E 9F
+ A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
+ B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF
+ C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF
+ C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D0 D1 D2 D3 D4 D5 D6 F7 D8 D9 DA DB DC DD DE FF
+
+# sort_order array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 41 42 43 46 48 49 4A 4B 4C 4D 4E 4F 50 51 52
+ 53 54 55 56 58 59 5A 5B 5C 5D 5E 5B 5C 5D 5E 5F
+ 60 41 42 43 46 48 49 4A 4B 4C 4D 4E 4F 50 51 52
+ 53 54 55 56 58 59 5A 5B 5C 5D 5E 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 57 8B 8C 8D 5F 8F
+ 90 91 92 93 94 95 96 97 98 99 57 9B 9C 9D 5F 9F
+ A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
+ B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF
+ 41 41 41 41 5C 5B 45 43 44 45 45 45 49 49 49 49
+ 47 4E 4F 4F 4F 4F 5D D7 D8 55 55 55 59 59 DE DF
+ 41 41 41 41 5C 5B 45 43 44 45 45 45 49 49 49 49
+ 47 4E 4F 4F 4F 4F 5D F7 D8 55 55 55 59 59 DE FF
diff --git a/sql/share/charsets/danish.conf b/sql/share/charsets/danish.conf
new file mode 100644
index 00000000000..f99590ed6f3
--- /dev/null
+++ b/sql/share/charsets/danish.conf
@@ -0,0 +1,74 @@
+# Configuration file for the danish character set
+
+# ctype array (must have 257 elements)
+ 00
+ 20 20 20 20 20 20 20 20 20 28 28 28 28 28 20 20
+ 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
+ 48 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 84 84 84 84 84 84 84 84 84 84 10 10 10 10 10 10
+ 10 81 81 81 81 81 81 01 01 01 01 01 01 01 01 01
+ 01 01 01 01 01 01 01 01 01 01 01 10 10 10 10 10
+ 10 82 82 82 82 82 82 02 02 02 02 02 02 02 02 02
+ 02 02 02 02 02 02 02 02 02 02 02 10 10 10 10 20
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 48 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
+ 01 01 01 01 01 01 01 10 01 01 01 01 01 01 01 02
+ 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02
+ 02 02 02 02 02 02 02 10 02 02 02 02 02 02 02 02
+
+# to_lower array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
+ 70 71 72 73 74 75 76 77 78 79 7A 5B 5C 5D 5E 5F
+ 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
+ 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
+ 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
+ A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
+ B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF
+ E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
+ F0 F1 F2 F3 F4 F5 F6 D7 F8 F9 FA FB FC FD FE DF
+ E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
+ F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
+
+# to_upper array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F
+ 60 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
+ 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
+ A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
+ B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF
+ C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF
+ C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D0 D1 D2 D3 D4 D5 D6 F7 D8 D9 DA DB DC DD DE FF
+
+# sort_order array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F
+ 60 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
+ 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
+ A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
+ B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF
+ 41 41 41 41 5B 5D 5B 43 45 45 45 45 49 49 49 49
+ 44 4E 4F 4F 4F 4F 5C D7 5C 55 55 55 59 59 DE DF
+ 41 41 41 41 5B 5D 5B 43 45 45 45 45 49 49 49 49
+ 44 4E 4F 4F 4F 4F 5C F7 5C 55 55 55 59 59 DE FF
diff --git a/sql/share/charsets/dec8.conf b/sql/share/charsets/dec8.conf
new file mode 100644
index 00000000000..a4849aaa04c
--- /dev/null
+++ b/sql/share/charsets/dec8.conf
@@ -0,0 +1,74 @@
+# Configuration file for the dec8 character set
+
+# ctype array (must have 257 elements)
+ 00
+ 20 20 20 20 20 20 20 20 20 28 28 28 28 28 20 20
+ 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
+ 48 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 84 84 84 84 84 84 84 84 84 84 10 10 10 10 10 10
+ 10 81 81 81 81 81 81 01 01 01 01 01 01 01 01 01
+ 01 01 01 01 01 01 01 01 01 01 01 10 10 10 10 10
+ 10 82 82 82 82 82 82 02 02 02 02 02 02 02 02 02
+ 02 02 02 02 02 02 02 02 02 02 02 10 10 10 10 20
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 48 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
+ 01 01 01 01 01 01 01 10 01 01 01 01 01 01 01 02
+ 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02
+ 02 02 02 02 02 02 02 10 02 02 02 02 02 02 02 02
+
+# to_lower array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
+ 70 71 72 73 74 75 76 77 78 79 7A 5B 5C 5D 5E 5F
+ 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
+ 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
+ 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
+ A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
+ B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF
+ E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
+ F0 F1 F2 F3 F4 F5 F6 D7 F8 F9 FA FB FC FD FE DF
+ E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
+ F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
+
+# to_upper array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F
+ 60 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
+ 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
+ A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
+ B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF
+ C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF
+ C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D0 D1 D2 D3 D4 D5 D6 F7 D8 D9 DA DB DC DD DE FF
+
+# sort_order array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F
+ 60 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
+ 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
+ A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
+ B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF
+ 41 41 41 41 5C 5B 5C 43 45 45 45 45 49 49 49 49
+ 44 4E 4F 4F 4F 4F 5D D7 D8 55 55 55 59 59 DE DF
+ 41 41 41 41 5C 5B 5C 43 45 45 45 45 49 49 49 49
+ 44 4E 4F 4F 4F 4F 5D F7 D8 55 55 55 59 59 DE FF
diff --git a/sql/share/charsets/dos.conf b/sql/share/charsets/dos.conf
new file mode 100644
index 00000000000..dda86d0f3e8
--- /dev/null
+++ b/sql/share/charsets/dos.conf
@@ -0,0 +1,74 @@
+# Configuration file for the dos character set
+
+# ctype array (must have 257 elements)
+ 00
+ 20 30 30 30 30 30 30 20 20 28 28 28 28 28 30 30
+ 30 30 30 30 30 30 30 30 30 30 20 30 30 30 30 30
+ 48 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 84 84 84 84 84 84 84 84 84 84 10 10 10 10 10 10
+ 10 81 81 81 81 81 81 01 01 01 01 01 01 01 01 01
+ 01 01 01 01 01 01 01 01 01 01 01 10 10 10 10 10
+ 10 82 82 82 82 82 82 02 02 02 02 02 02 02 02 02
+ 02 02 02 02 02 02 02 02 02 02 02 10 10 10 10 30
+ 02 02 02 02 02 02 02 02 02 02 02 02 02 02 01 01
+ 01 02 01 02 02 02 02 02 02 01 01 10 10 10 10 10
+ 02 02 02 02 02 01 10 10 10 10 10 10 10 10 10 10
+ 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 20
+
+# to_lower array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
+ 70 71 72 73 74 75 76 77 78 79 7A 5B 5C 5D 5E 5F
+ 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
+ 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F
+ 87 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 84 86
+ 82 91 91 93 94 95 96 97 98 94 81 9B 9C 9D 9E 9F
+ A0 A1 A2 A3 A4 A4 A6 A7 A8 A9 AA AB AC AD AE AF
+ B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF
+ C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF
+ E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
+ F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
+
+# to_upper array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F
+ 60 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 7B 7C 7D 7E 7F
+ 80 9A 90 41 8E 41 8F 80 45 45 45 49 49 49 8E 8F
+ 90 92 92 4F 99 4F 55 55 59 99 9A 9B 9C 9D 9E 9F
+ 41 49 4F 55 A5 A5 A6 A7 A8 A9 AA AB AC AD AE AF
+ B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF
+ C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF
+ E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
+ F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
+
+# sort_order array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F
+ 60 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 7B 7C 7D 7E 7F
+ 43 59 45 41 5C 41 5B 43 45 45 45 49 49 49 5C 5B
+ 45 5C 5C 4F 5D 4F 55 55 59 5D 59 24 24 24 24 24
+ 41 49 4F 55 4E 4E A6 A7 3F A9 AA AB AC 21 22 22
+ B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF
+ C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF
+ E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
+ F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
diff --git a/sql/share/charsets/estonia.conf b/sql/share/charsets/estonia.conf
new file mode 100644
index 00000000000..76bbc021b0c
--- /dev/null
+++ b/sql/share/charsets/estonia.conf
@@ -0,0 +1,74 @@
+# Configuration file for the estonia character set.
+
+# The ctype array must have 257 elements.
+ 00
+ 20 20 20 20 20 20 20 20 20 28 28 28 28 28 20 20
+ 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
+ 48 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 84 84 84 84 84 84 84 84 84 84 10 10 10 10 10 10
+ 10 81 81 81 81 81 81 01 01 01 01 01 01 01 01 01
+ 01 01 01 01 01 01 01 01 01 01 01 10 10 10 10 10
+ 10 82 82 82 82 82 82 02 02 02 02 02 02 02 02 02
+ 02 02 02 02 02 02 02 02 02 02 02 10 10 10 10 20
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 48 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
+ 01 01 01 01 01 01 01 10 01 01 01 01 01 01 01 02
+ 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02
+ 02 02 02 02 02 02 02 10 02 02 02 02 02 02 02 02
+
+# The to_lower array must have 256 elements.
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
+ 70 71 72 73 74 75 76 77 78 79 7A 5B 5C 5D 5E 5F
+ 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
+ 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
+ 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
+ A0 A1 A2 A3 A4 A5 A6 A7 B8 A9 BA AB AC AD AE BF
+ B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF
+ E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
+ F0 F1 F2 F3 F4 F5 F6 D7 F8 F9 FA FB FC FD FE DF
+ E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
+ F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
+
+# The to_upper array must have 256 elements.
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F
+ 60 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
+ 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
+ A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
+ B0 B1 B2 B3 B4 B5 B6 B7 A8 B9 AA BB BC BD BE AF
+ C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF
+ C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D0 D1 D2 D3 D4 D5 D6 F7 D8 D9 DA DB DC DD DE FF
+
+# The sort_order array must have 256 elements.
+ 00 02 03 04 05 06 07 08 09 2E 2F 30 31 32 0A 0B
+ 0C 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B
+ 2C 33 34 35 36 37 38 27 39 3A 3B 5D 3C 28 3D 3E
+ 76 7A 7C 7E 80 81 82 83 84 85 3F 40 5E 5F 60 41
+ 42 86 90 92 98 9A A4 A6 AA AC B2 B4 B8 BE C0 C6
+ CE D0 D2 D6 E5 E8 EE F0 FA FC DD 43 44 45 46 47
+ 48 87 91 93 99 9B A5 A7 AB AD B3 B5 B9 BF C1 C7
+ CF D1 D3 D7 E6 E9 EF F1 FB FD DE 49 4A 4B 4C 1C
+ 01 1D 57 1E 5A 74 71 72 1F 75 20 5B 21 4E 52 51
+ 22 55 56 58 59 73 2A 2B 23 E7 24 5C 25 4F 54 26
+ 2D FE 66 67 68 FF 4D 69 CC 6A D4 62 6B 29 6C 8E
+ 6D 61 7D 7F 50 6E 6F 70 CD 7B D5 63 77 78 79 8F
+ 8C B0 88 94 F4 8A A2 A0 96 9C DF 9E A8 B6 AE BA
+ DB C2 C4 C8 CA F2 F6 64 EC BC D8 EA F8 E1 E3 DA
+ 8D B1 89 95 F5 8B A3 A1 97 9D E0 9F A9 B7 AF BB
+ DC C3 C5 C9 CB F3 F7 65 ED BD D9 EB F9 E2 E4 53
diff --git a/sql/share/charsets/german1.conf b/sql/share/charsets/german1.conf
new file mode 100644
index 00000000000..dac77b1f08f
--- /dev/null
+++ b/sql/share/charsets/german1.conf
@@ -0,0 +1,74 @@
+# Configuration file for the german1 character set
+
+# ctype array (must have 257 elements)
+ 00
+ 20 20 20 20 20 20 20 20 20 28 28 28 28 28 20 20
+ 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
+ 48 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 84 84 84 84 84 84 84 84 84 84 10 10 10 10 10 10
+ 10 81 81 81 81 81 81 01 01 01 01 01 01 01 01 01
+ 01 01 01 01 01 01 01 01 01 01 01 10 10 10 10 10
+ 10 82 82 82 82 82 82 02 02 02 02 02 02 02 02 02
+ 02 02 02 02 02 02 02 02 02 02 02 10 10 10 10 20
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 48 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
+ 01 01 01 01 01 01 01 10 01 01 01 01 01 01 01 02
+ 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02
+ 02 02 02 02 02 02 02 10 02 02 02 02 02 02 02 02
+
+# to_lower array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
+ 70 71 72 73 74 75 76 77 78 79 7A 5B 5C 5D 5E 5F
+ 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
+ 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
+ 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
+ A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
+ B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF
+ E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
+ F0 F1 F2 F3 F4 F5 F6 D7 F8 F9 FA FB FC FD FE DF
+ E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
+ F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
+
+# to_upper array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F
+ 60 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
+ 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
+ A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
+ B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF
+ C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF
+ C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D0 D1 D2 D3 D4 D5 D6 F7 D8 D9 DA DB DC DD DE FF
+
+# sort_order array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F
+ 60 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
+ 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
+ A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
+ B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF
+ 41 41 41 41 41 41 41 43 45 45 45 45 49 49 49 49
+ 44 4E 4F 4F 4F 4F 4F D7 D8 55 55 55 55 59 DE DF
+ 41 41 41 41 41 41 41 43 45 45 45 45 49 49 49 49
+ 44 4E 4F 4F 4F 4F 4F F7 D8 55 55 55 55 59 DE FF
diff --git a/sql/share/charsets/greek.conf b/sql/share/charsets/greek.conf
new file mode 100644
index 00000000000..73d67d6ee71
--- /dev/null
+++ b/sql/share/charsets/greek.conf
@@ -0,0 +1,74 @@
+# Configuration file for the greek character set
+
+# ctype array (must have 257 elements)
+ 00
+ 20 20 20 20 20 20 20 20 20 28 28 28 28 28 20 20
+ 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
+ 48 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 84 84 84 84 84 84 84 84 84 84 10 10 10 10 10 10
+ 10 81 81 81 81 81 81 01 01 01 01 01 01 01 01 01
+ 01 01 01 01 01 01 01 01 01 01 01 10 10 10 10 10
+ 10 82 82 82 82 82 82 02 02 02 02 02 02 02 02 02
+ 02 02 02 02 02 02 02 02 02 02 02 10 10 10 10 20
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 48 10 10 10 00 00 10 10 10 10 00 10 10 10 00 10
+ 10 10 10 10 10 10 01 10 01 01 01 10 01 10 01 01
+ 02 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
+ 01 01 00 01 01 01 01 01 01 01 01 01 02 02 02 02
+ 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02
+ 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 00
+
+# to_lower array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
+ 70 71 72 73 74 75 76 77 78 79 7A 5B 5C 5D 5E 5F
+ 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
+ 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
+ 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
+ A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
+ B0 B1 B2 B3 B4 B5 DC B7 DD DE DF BB FC BD FD FE
+ C0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
+ F0 F1 D2 F3 F4 F5 F6 F7 F8 F9 FA FB DC DD DE DF
+ E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
+ F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
+
+# to_upper array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F
+ 60 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
+ 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
+ A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
+ B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF
+ DA C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB C1 C5 C7 C9
+ DB C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D0 D1 D3 D3 D4 D5 D6 D7 D8 D9 DA DB CF D5 D9 FF
+
+# sort_order array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F
+ 60 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
+ 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
+ A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
+ B0 B1 B2 B3 B4 B5 C1 B7 C5 C7 C9 BB CF BD D5 D9
+ C9 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 C9 D5 C1 C5 C7 C9
+ D5 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D0 D1 D3 D3 D4 D5 D6 D7 D8 D9 C9 D5 CF D5 D9 FF
diff --git a/sql/share/charsets/hebrew.conf b/sql/share/charsets/hebrew.conf
new file mode 100644
index 00000000000..6a5f88eb228
--- /dev/null
+++ b/sql/share/charsets/hebrew.conf
@@ -0,0 +1,74 @@
+# Configuration file for the hebrew character set
+
+# ctype array (must have 257 elements)
+ 00
+ 20 20 20 20 20 20 20 20 20 28 28 28 28 28 20 20
+ 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
+ 48 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 84 84 84 84 84 84 84 84 84 84 10 10 10 10 10 10
+ 10 81 81 81 81 81 81 01 01 01 01 01 01 01 01 01
+ 01 01 01 01 01 01 01 01 01 01 01 10 10 10 10 10
+ 10 82 82 82 82 82 82 02 02 02 02 02 02 02 02 02
+ 02 02 02 02 02 02 02 02 02 02 02 10 10 10 10 20
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 48 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02
+ 02 02 02 02 02 02 02 02 02 02 02 00 00 00 00 00
+
+# to_lower array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
+ 70 71 72 73 74 75 76 77 78 79 7A 5B 5C 5D 5E 5F
+ 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
+ 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
+ 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
+ A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
+ B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF
+ C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF
+ E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
+ F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
+
+# to_upper array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F
+ 60 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
+ 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
+ A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
+ B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF
+ C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF
+ E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
+ F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
+
+# sort_order array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F
+ 60 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
+ 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
+ A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
+ B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF
+ C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D0 D1 D2 D3 D4 D5 D6 F7 D8 D9 DA DB DC DD DE FF
+ E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
+ F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
diff --git a/sql/share/charsets/hp8.conf b/sql/share/charsets/hp8.conf
new file mode 100644
index 00000000000..e9fadacbf76
--- /dev/null
+++ b/sql/share/charsets/hp8.conf
@@ -0,0 +1,74 @@
+# Configuration file for the hp8 character set
+
+# ctype array (must have 257 elements)
+ 00
+ 20 20 20 20 20 20 20 20 20 28 28 28 28 28 20 20
+ 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
+ 48 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 84 84 84 84 84 84 84 84 84 84 10 10 10 10 10 10
+ 10 81 81 81 81 81 81 01 01 01 01 01 01 01 01 01
+ 01 01 01 01 01 01 01 01 01 01 01 10 10 10 10 10
+ 10 82 82 82 82 82 82 02 02 02 02 02 02 02 02 02
+ 02 02 02 02 02 02 02 02 02 02 02 10 10 10 10 20
+ 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
+ 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
+ 20 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 10 20 20 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 10 10 10 10 10 02 10 10 10 10 10 10 02 10 02 02
+ 01 10 10 01 02 10 10 02 01 10 01 01 01 10 10 10
+ 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 10 10 20 20 20 20 10 10 10 10 10 10 10 10 10 20
+
+# to_lower array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
+ 70 71 72 73 74 75 76 77 78 79 7A 5B 5C 5D 5E 5F
+ 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
+ 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
+ 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
+ A0 C8 C0 C9 C1 CD D1 DD A8 A9 AA AB AC CB C3 AF
+ B0 B2 B2 B3 B5 B5 B7 B7 B8 B9 BA BB BC BD BE BF
+ C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D4 D1 D6 D7 D4 D5 D6 D7 CC D9 CE CF C5 DD DE C2
+ C4 E2 E2 E4 E4 D5 D9 C6 CA EA EA EC EC C7 EF EF
+ F1 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
+
+# to_upper array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F
+ 60 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
+ 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
+ A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
+ B0 B1 B1 B3 B4 B4 B6 B6 B8 B9 BA BB BC BD BE BF
+ A2 A4 DF AE E0 DC E7 ED A1 A3 E8 AD D8 A5 DA DB
+ D0 A6 D2 D3 D0 E5 D2 D3 D8 E6 DA DB DC A7 DE DF
+ E0 E1 E1 E3 E3 E5 E6 E7 E8 E9 E9 EB EB ED EE EE
+ F0 F0 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
+
+# sort_order array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 5C 5D 5B 5E 5F
+ 60 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
+ 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
+ A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
+ B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF
+ C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF
+ E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
+ F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
diff --git a/sql/share/charsets/hungarian.conf b/sql/share/charsets/hungarian.conf
new file mode 100644
index 00000000000..db58d62575f
--- /dev/null
+++ b/sql/share/charsets/hungarian.conf
@@ -0,0 +1,74 @@
+# Configuration file for the hungarian character set
+
+# ctype array (must have 257 elements)
+ 00
+ 20 20 20 20 20 20 20 20 20 28 28 28 28 28 20 20
+ 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
+ 48 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 84 84 84 84 84 84 84 84 84 84 10 10 10 10 10 10
+ 10 81 81 81 81 81 81 01 01 01 01 01 01 01 01 01
+ 01 01 01 01 01 01 01 01 01 01 01 10 10 10 10 10
+ 10 82 82 82 82 82 82 02 02 02 02 02 02 02 02 02
+ 02 02 02 02 02 02 02 02 02 02 02 10 10 10 10 20
+ 20 20 20 20 20 20 20 20 28 28 28 28 28 20 20 20
+ 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 48
+ 01 10 01 10 01 01 10 00 00 01 01 01 01 10 01 01
+ 10 02 10 02 10 02 02 10 10 02 02 02 02 10 02 02
+ 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
+ 10 01 01 01 01 01 01 10 01 01 01 01 01 01 01 10
+ 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02
+ 02 02 02 02 02 02 02 10 02 02 02 02 02 02 02 10
+
+# to_lower array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
+ 70 71 72 73 74 75 76 77 78 79 7A 5B 5C 5D 5E 5F
+ 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
+ 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 8B 8B A1 A1 8E A0
+ 82 91 92 93 94 A2 96 A3 96 94 81 9B 9C 9D 9E 9F
+ A0 A1 A2 A3 B5 B6 A6 93 A8 B9 BA BB BC AD BE BF
+ B0 B1 B2 B3 B4 E1 B6 B7 B8 B9 BA BB BC BD BE BF
+ E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
+ D0 F1 F2 F3 F4 F5 F6 D7 F8 F9 FA FB FC FD FE DF
+ A2 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA 96 EC ED EE EF
+ F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
+
+# to_upper array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F
+ 60 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 7B 7C 7D 7E 7F
+ 80 9A 90 83 84 85 86 87 88 89 8A 8A 8C 8D 8E 8F
+ 90 91 92 A7 99 95 98 97 98 99 9A 9B 9C 9D 9E 9F
+ 8F 8D 95 97 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
+ B0 A0 B2 A2 B4 B5 A5 B7 B8 A9 AA AB AC BD AE AF
+ C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF
+ E0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA EB CC CD CE CF
+ F0 D1 D2 D3 D4 D5 D6 F7 D8 D9 DA DB DC DD DE FF
+
+# sort_order array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 41 47 48 4C 4E 53 54 55 56 5A 5B 5C 60 61 64
+ 69 6A 6B 6E 72 75 7A 7B 7C 7D 7F 83 84 85 86 87
+ 88 41 47 48 4C 4E 53 54 55 56 5A 5B 5C 60 61 64
+ 69 6A 6B 6E 72 75 7A 7B 7C 7D 7F 89 8A 8B 8C 00
+ 01 78 4E 04 05 06 07 08 09 0A 67 67 56 56 0F 41
+ 4E 12 13 67 67 64 78 75 78 67 78 1C 1D 1E 1F FF
+ 41 56 64 75 5E 6F FF 67 FF 70 71 73 80 FF 81 82
+ FF 42 FF 5D FF 41 6F FF FF 70 71 73 80 FF 81 82
+ 6C 41 44 45 46 5F 49 4B 4A 4E 51 52 50 56 57 4D
+ FF 62 63 64 66 67 67 FF 6D 77 75 78 78 7E 74 FF
+ 64 41 44 45 46 5F 49 4B 4A 4E 51 78 50 56 58 4D
+ FF 62 63 64 66 67 67 FF 6D 77 75 78 78 7E 74 FF
diff --git a/sql/share/charsets/koi8_ru.conf b/sql/share/charsets/koi8_ru.conf
new file mode 100644
index 00000000000..4cfee67a236
--- /dev/null
+++ b/sql/share/charsets/koi8_ru.conf
@@ -0,0 +1,74 @@
+# Configuration file for the koi8_ru character set
+
+# ctype array (must have 257 elements)
+ 00
+ 20 20 20 20 20 20 20 20 20 28 28 28 28 28 20 20
+ 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
+ 48 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 84 84 84 84 84 84 84 84 84 84 10 10 10 10 10 10
+ 10 81 81 81 81 81 81 01 01 01 01 01 01 01 01 01
+ 01 01 01 01 01 01 01 01 01 01 01 10 10 10 10 10
+ 10 82 82 82 82 82 82 02 02 02 02 02 02 02 02 02
+ 02 02 02 02 02 02 02 02 02 02 02 10 10 10 10 20
+ 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 10 10 10 02 10 10 10 10 10 10 10 10 10 10 10 10
+ 10 10 10 01 10 10 10 10 10 10 10 10 10 10 10 10
+ 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02
+ 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02
+ 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
+ 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
+
+# to_lower array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
+ 70 71 72 73 74 75 76 77 78 79 7A 5B 5C 5D 5E 5F
+ 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
+ 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
+ 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
+ A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
+ B0 B1 B2 A3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF
+ C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF
+ C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF
+
+# to_upper array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F
+ 60 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
+ 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
+ A0 A1 A2 B3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
+ B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF
+ E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
+ F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
+ E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
+ F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
+
+# sort_order array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F
+ 60 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
+ 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
+ A0 A1 A2 E5 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE
+ AF B0 B1 E5 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD
+ FE DF E0 F6 E3 E4 F4 E2 F5 E8 E9 EA EB EC ED EE
+ EF FF F0 F1 F2 F3 E6 E1 FC FB E7 F8 FD F9 F7 FA
+ FE DF E0 F6 E3 E4 F4 E2 F5 E8 E9 EA EB EC ED EE
+ EF FF F0 F1 F2 F3 E6 E1 FC FB E7 F8 FD F9 F7 FA
diff --git a/sql/share/charsets/koi8_ukr.conf b/sql/share/charsets/koi8_ukr.conf
new file mode 100644
index 00000000000..3e2c8e27325
--- /dev/null
+++ b/sql/share/charsets/koi8_ukr.conf
@@ -0,0 +1,74 @@
+# Configuration file for the koi8_ukr character set
+
+# ctype array (must have 257 elements)
+ 00
+ 20 20 20 20 20 20 20 20 20 28 28 28 28 28 20 20
+ 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
+ 48 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 84 84 84 84 84 84 84 84 84 84 10 10 10 10 10 10
+ 10 81 81 81 81 81 81 01 01 01 01 01 01 01 01 01
+ 01 01 01 01 01 01 01 01 01 01 01 10 10 10 10 10
+ 10 82 82 82 82 82 82 02 02 02 02 02 02 02 02 02
+ 02 02 02 02 02 02 02 02 02 02 02 10 10 10 10 20
+ 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 10 10 10 02 02 10 02 02 10 10 10 10 10 02 10 10
+ 10 10 10 01 01 10 01 01 10 10 10 10 10 01 10 10
+ 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02
+ 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02
+ 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
+ 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
+
+# to_lower array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
+ 70 71 72 73 74 75 76 77 78 79 7A 5B 5C 5D 5E 5F
+ 20 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
+ 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F
+ 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
+ 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
+ 20 20 20 A3 A4 20 A6 A7 20 20 20 20 20 AD 20 20
+ 20 20 20 A3 A4 20 A6 A7 20 20 20 20 20 AD 20 20
+ C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF
+ C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF
+
+# to_upper array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F
+ 20 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 7B 7C 7D 7E 7F
+ 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
+ 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
+ 20 20 20 B3 B4 20 B6 B7 20 20 20 20 20 BD 20 20
+ 20 20 20 B3 B4 20 B6 B7 20 20 20 20 20 BD 20 20
+ E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
+ F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
+ E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
+ F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
+
+# sort_order array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F
+ 20 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 7B 7C 7D 7E 7F
+ A5 A6 A7 A8 A9 AA AB AC AD AE AF B0 B1 B2 B3 B4
+ B5 B6 B7 B8 B9 BA BB BC BD BE BF C0 C1 C2 C3 C4
+ C5 C6 C7 88 87 C8 8C 8D C9 CA CB CC CD 84 CE CF
+ D0 D1 D2 88 87 D3 8C 8D D4 D5 D6 D7 D8 84 D9 DA
+ A3 80 81 9B 85 86 99 83 9A 8B 8E 8F 90 91 92 93
+ 94 A4 95 96 97 98 89 82 A1 A0 8A 9D A2 9E 9C 9F
+ A3 80 81 9B 85 86 99 83 9A 8B 8E 8F 90 91 92 93
+ 94 A4 95 96 97 98 89 82 A1 A0 8A 9D A2 9E 9C 9F
diff --git a/sql/share/charsets/latin1.conf b/sql/share/charsets/latin1.conf
new file mode 100644
index 00000000000..cf974aefa14
--- /dev/null
+++ b/sql/share/charsets/latin1.conf
@@ -0,0 +1,74 @@
+# Configuration file for the latin1 character set
+
+# ctype array (must have 257 elements)
+ 00
+ 20 20 20 20 20 20 20 20 20 28 28 28 28 28 20 20
+ 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
+ 48 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 84 84 84 84 84 84 84 84 84 84 10 10 10 10 10 10
+ 10 81 81 81 81 81 81 01 01 01 01 01 01 01 01 01
+ 01 01 01 01 01 01 01 01 01 01 01 10 10 10 10 10
+ 10 82 82 82 82 82 82 02 02 02 02 02 02 02 02 02
+ 02 02 02 02 02 02 02 02 02 02 02 10 10 10 10 20
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 48 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
+ 01 01 01 01 01 01 01 10 01 01 01 01 01 01 01 02
+ 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02
+ 02 02 02 02 02 02 02 10 02 02 02 02 02 02 02 02
+
+# to_lower array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
+ 70 71 72 73 74 75 76 77 78 79 7A 5B 5C 5D 5E 5F
+ 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
+ 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
+ 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
+ A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
+ B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF
+ E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
+ F0 F1 F2 F3 F4 F5 F6 D7 F8 F9 FA FB FC FD FE DF
+ E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
+ F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
+
+# to_upper array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F
+ 60 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
+ 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
+ A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
+ B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF
+ C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF
+ C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D0 D1 D2 D3 D4 D5 D6 F7 D8 D9 DA DB DC DD DE FF
+
+# sort_order array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F
+ 60 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
+ 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
+ A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
+ B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF
+ 41 41 41 41 5C 5B 5C 43 45 45 45 45 49 49 49 49
+ 44 4E 4F 4F 4F 4F 5D D7 D8 55 55 55 59 59 DE DF
+ 41 41 41 41 5C 5B 5C 43 45 45 45 45 49 49 49 49
+ 44 4E 4F 4F 4F 4F 5D F7 D8 55 55 55 59 59 DE FF
diff --git a/sql/share/charsets/latin2.conf b/sql/share/charsets/latin2.conf
new file mode 100644
index 00000000000..cc18c22c0a2
--- /dev/null
+++ b/sql/share/charsets/latin2.conf
@@ -0,0 +1,74 @@
+# Configuration file for the latin2 character set
+
+# ctype array (must have 257 elements)
+ 00
+ 20 20 20 20 20 20 20 20 20 28 28 28 28 28 20 20
+ 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
+ 48 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 84 84 84 84 84 84 84 84 84 84 10 10 10 10 10 10
+ 10 81 81 81 81 81 81 01 01 01 01 01 01 01 01 01
+ 01 01 01 01 01 01 01 01 01 01 01 10 10 10 10 10
+ 10 82 82 82 82 82 82 02 02 02 02 02 02 02 02 02
+ 02 02 02 02 02 02 02 02 02 02 02 10 10 10 10 00
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 48 01 10 01 10 01 01 10 10 01 01 01 01 10 01 01
+ 10 02 10 02 10 02 02 10 10 02 02 02 02 10 02 02
+ 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
+ 10 01 01 01 01 01 01 10 01 01 01 01 01 01 01 10
+ 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02
+ 02 02 02 02 02 02 02 10 02 02 02 02 02 02 02 10
+
+# to_lower array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
+ 70 71 72 73 74 75 76 77 78 79 7A 5B 5C 5D 5E 5F
+ 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
+ 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
+ 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
+ A0 B1 A2 B3 A4 B5 B6 A7 A8 B9 BA BB BC AD BE BF
+ B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF
+ E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
+ D0 F1 F2 F3 F4 F5 F6 D7 F8 F9 FA FB FC FD FE DF
+ E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
+ F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
+
+# to_upper array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F
+ 60 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
+ 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
+ A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
+ B0 A1 B2 A3 B4 A5 A6 B7 B8 A9 AA AB AC BD AE AF
+ C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF
+ C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ F0 D1 D2 D3 D4 D5 D6 F7 D8 D9 DA DB DC DD DE FF
+
+# sort_order array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 41 44 45 48 49 4B 4C 4D 4E 4F 50 51 53 54 56
+ 58 59 5A 5B 5E 5F 60 61 62 63 64 68 69 6A 6B 6C
+ 6D 41 44 45 48 49 4B 4C 4D 4E 4F 50 51 53 54 56
+ 58 59 5A 5B 5E 5F 60 61 62 63 64 6E 6F 70 71 FF
+ FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
+ FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
+ FF 42 FF 52 FF 51 5C FF FF 5D 5B 5E 65 FF 67 66
+ FF 42 FF 52 FF 51 5C FF FF 5D 5B 5E 65 FF 67 66
+ 5A 43 43 43 43 51 46 45 47 49 4A 49 49 4E 4E 48
+ FF 55 54 57 56 56 56 FF 5A 5F 5F 5F 5F 63 5E FF
+ 5A 43 43 43 43 51 46 45 47 49 4A 49 49 4E 4E 48
+ FF 55 54 57 56 56 56 FF 5A 5F 5F 5F 5F 63 5E FF
diff --git a/sql/share/charsets/swe7.conf b/sql/share/charsets/swe7.conf
new file mode 100644
index 00000000000..d2de48b4d1c
--- /dev/null
+++ b/sql/share/charsets/swe7.conf
@@ -0,0 +1,74 @@
+# Configuration file for the swe7 character set
+
+# ctype array (must have 257 elements)
+ 00
+ 20 20 20 20 20 20 20 20 20 28 28 28 28 28 20 20
+ 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
+ 48 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 84 84 84 84 84 84 84 84 84 84 10 10 10 10 10 10
+ 01 81 81 81 81 81 81 01 01 01 01 01 01 01 01 01
+ 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 10
+ 01 82 82 82 82 82 82 02 02 02 02 02 02 02 02 02
+ 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 20
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+
+# to_lower array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
+ 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 5F
+ 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
+ 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
+ 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
+ A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
+ B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF
+ C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF
+ E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
+ F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
+
+# to_upper array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F
+ 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 7F
+ 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
+ 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
+ A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
+ B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF
+ C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF
+ E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
+ F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
+
+# sort_order array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 45 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 5C 5D 5B 59 5F
+ 45 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 5C 5D 5B 59 7F
+ 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
+ 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
+ A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
+ B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF
+ C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF
+ E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
+ F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
diff --git a/sql/share/charsets/usa7.conf b/sql/share/charsets/usa7.conf
new file mode 100644
index 00000000000..b9e7a44c894
--- /dev/null
+++ b/sql/share/charsets/usa7.conf
@@ -0,0 +1,74 @@
+# Configuration file for the usa7 character set
+
+# ctype array (must have 257 elements)
+ 00
+ 20 20 20 20 20 20 20 20 20 28 28 28 28 28 20 20
+ 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
+ 48 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 84 84 84 84 84 84 84 84 84 84 10 10 10 10 10 10
+ 10 81 81 81 81 81 81 01 01 01 01 01 01 01 01 01
+ 01 01 01 01 01 01 01 01 01 01 01 10 10 10 10 10
+ 10 82 82 82 82 82 82 02 02 02 02 02 02 02 02 02
+ 02 02 02 02 02 02 02 02 02 02 02 10 10 10 10 20
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+
+# to_lower array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
+ 70 71 72 73 74 75 76 77 78 79 7A 5B 5C 5D 5E 5F
+ 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
+ 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
+ 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
+ A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
+ B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF
+ C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF
+ E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
+ F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
+
+# to_upper array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F
+ 60 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
+ 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
+ A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
+ B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF
+ C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF
+ E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
+ F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
+
+# sort_order array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 5C 5D 5B 5E 5F
+ 45 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 7B 7C 7D 59 7F
+ 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
+ 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
+ A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
+ B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF
+ C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF
+ E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
+ F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
diff --git a/sql/share/charsets/win1250.conf b/sql/share/charsets/win1250.conf
new file mode 100644
index 00000000000..31d253d7381
--- /dev/null
+++ b/sql/share/charsets/win1250.conf
@@ -0,0 +1,74 @@
+# Configuration file for the win1250 character set.
+
+# The ctype array must have 257 elements.
+ 00
+ 20 20 20 20 20 20 20 20 20 28 28 28 28 28 20 20
+ 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
+ 48 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 84 84 84 84 84 84 84 84 84 84 10 10 10 10 10 10
+ 10 81 81 81 81 81 81 01 01 01 01 01 01 01 01 01
+ 01 01 01 01 01 01 01 01 01 01 01 10 10 10 10 10
+ 10 82 82 82 82 82 82 02 02 02 02 02 02 02 02 02
+ 02 02 02 02 02 02 02 02 02 02 02 10 10 10 10 20
+ 20 20 10 20 10 10 10 10 20 10 01 10 01 01 01 01
+ 20 10 10 10 10 10 10 10 20 10 02 10 02 02 02 02
+ 48 10 10 01 10 01 10 01 10 10 01 10 10 10 10 01
+ 10 10 10 02 10 10 10 10 10 02 02 10 01 10 02 02
+ 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
+ 01 01 01 01 01 01 01 10 01 01 01 01 01 01 01 02
+ 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02
+ 02 02 02 02 02 02 02 10 02 02 02 02 02 02 02 10
+
+# The to_lower array must have 256 elements.
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
+ 70 71 72 73 74 75 76 77 78 79 7A 5B 5C 5D 5E 5F
+ 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
+ 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 9A 8B 9C 9D 9E 9F
+ 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
+ A0 A1 A2 B3 A4 B9 A6 DF A8 A9 BA AB AC AD AE BF
+ B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BE BD BE BF
+ E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
+ F0 F1 F2 F3 F4 F5 F6 D7 F8 F9 FA FB FC FD FE DF
+ E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
+ F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
+
+# The to_upper array must have 256 elements.
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F
+ 60 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
+ 90 91 92 93 94 95 96 97 98 99 8A 9B 8C 8D 8E 8F
+ A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
+ B0 B1 B2 A3 B4 B5 B6 B7 B8 A5 AA BB BC BD BC AF
+ C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE A7
+ C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D0 D1 D2 D3 D4 D5 D6 F7 D8 D9 DA DB DC DD DE FF
+
+# The sort_order array must have 256 elements.
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 41 42 43 46 49 4A 4B 4C 4D 4E 4F 50 52 53 55
+ 56 57 58 59 5B 5C 5D 5E 5F 60 61 63 64 65 66 67
+ 68 41 42 43 46 49 4A 4B 4C 4D 4E 4F 50 52 53 55
+ 56 57 58 59 5B 5C 5D 5E 5F 60 61 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 5A 8B 5A 5B 62 62
+ 90 91 92 93 94 95 96 97 98 99 5A 9B 5A 5B 62 62
+ 20 A1 A2 50 A4 41 A6 59 A8 A9 59 AB AC AD AE 62
+ B0 B1 B2 50 B4 B5 B6 B7 B8 41 59 BB 50 BD 50 62
+ 58 41 41 41 41 50 45 43 44 49 49 49 49 4D 4D 46
+ 47 53 53 55 55 55 55 D7 58 5C 5C 5C 5C 60 5B 59
+ 58 41 41 41 41 50 45 43 44 49 49 49 49 4D 4D 46
+ 47 53 53 55 55 55 55 F7 58 5C 5C 5C 5C 60 5B FF
diff --git a/sql/share/charsets/win1251.conf b/sql/share/charsets/win1251.conf
new file mode 100644
index 00000000000..21f07361327
--- /dev/null
+++ b/sql/share/charsets/win1251.conf
@@ -0,0 +1,74 @@
+# Configuration file for the win1251 character set
+
+# ctype array (must have 257 elements)
+ 00
+ 20 20 20 20 20 20 20 20 20 28 28 28 28 28 20 20
+ 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
+ 48 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 84 84 84 84 84 84 84 84 84 84 10 10 10 10 10 10
+ 10 81 81 81 81 81 81 01 01 01 01 01 01 01 01 01
+ 01 01 01 01 01 01 01 01 01 01 01 10 10 10 10 10
+ 10 82 82 82 82 82 82 02 02 02 02 02 02 02 02 02
+ 02 02 02 02 02 02 02 02 02 02 02 10 10 10 10 20
+ 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 10 10 10 10 10 10 10 10 01 10 10 10 10 10 10 10
+ 10 10 10 10 10 10 10 10 02 10 10 10 10 10 10 10
+ 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
+ 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
+ 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02
+ 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02
+
+# to_lower array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
+ 70 71 72 73 74 75 76 77 78 79 7A 5B 5C 5D 5E 5F
+ 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
+ 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
+ 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
+ A0 A1 A2 A3 A4 A5 A6 A7 B8 A9 AA AB AC AD AE AF
+ B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF
+ E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
+ F0 F1 F2 F3 F4 F5 F5 F7 F8 F9 FA FB FC FD FE FF
+ E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
+ F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
+
+# to_upper array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F
+ 60 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
+ 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
+ A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
+ B0 B1 B2 B3 B4 B5 B6 B7 A8 B9 BA BB BC BD BE BF
+ C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D0 D1 D2 D3 D4 D5 D5 D7 D8 D9 DA DB DC DD DE DF
+ C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF
+
+# sort_order array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F
+ 60 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 7B 7C 7D 7E 7F
+ 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
+ 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
+ A0 A1 A2 A3 A4 A5 A6 A7 C6 A9 AA AB AC AD AE AF
+ B0 B1 B2 B3 B4 B5 B6 B7 C6 B9 BA BB BC BD BE BF
+ C0 C1 C2 C3 C4 C5 C7 C8 C9 CA CB CC CD CE CF D0
+ D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF E0
+ C0 C1 C2 C3 C4 C5 C7 C8 C9 CA CB CC CD CE CF D0
+ D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF E0
diff --git a/sql/share/charsets/win1251ukr.conf b/sql/share/charsets/win1251ukr.conf
new file mode 100644
index 00000000000..3f3e29ad0b4
--- /dev/null
+++ b/sql/share/charsets/win1251ukr.conf
@@ -0,0 +1,74 @@
+# Configuration file for the win1251ukr character set
+
+# ctype array (must have 257 elements)
+ 00
+ 20 20 20 20 20 20 20 20 20 28 28 28 28 28 20 20
+ 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
+ 48 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 84 84 84 84 84 84 84 84 84 84 10 10 10 10 10 10
+ 10 81 81 81 81 81 81 01 01 01 01 01 01 01 01 01
+ 01 01 01 01 01 01 01 01 01 01 01 10 10 10 10 10
+ 10 82 82 82 82 82 82 02 02 02 02 02 02 02 02 02
+ 02 02 02 02 02 02 02 02 02 02 02 10 10 10 10 20
+ 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
+ 10 10 10 10 10 01 10 10 01 10 01 10 10 10 10 01
+ 10 10 01 02 02 10 10 10 02 10 02 10 10 10 10 02
+ 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
+ 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01
+ 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02
+ 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02
+
+# to_lower array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
+ 70 71 72 73 74 75 76 77 78 79 7A 5B 5C 5D 5E 5F
+ 20 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
+ 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F
+ 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
+ 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
+ 20 20 20 20 20 A5 20 20 A8 20 AA 20 20 20 20 AF
+ 20 20 B2 B2 A5 20 20 20 A8 20 AA 20 20 20 20 AF
+ C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF
+ C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
+ D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF
+
+# to_upper array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F
+ 20 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 7B 7C 7D 7E 7F
+ 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
+ 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
+ 20 20 20 20 20 B4 20 20 B8 20 BA 20 20 20 20 BF
+ 20 20 B3 B3 B4 20 20 20 B8 20 BA 20 20 20 20 BF
+ E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
+ F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
+ E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
+ F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
+
+# sort_order array (must have 256 elements)
+ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
+ 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
+ 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
+ 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
+ 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F
+ 20 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
+ 50 51 52 53 54 55 56 57 58 59 5A 7B 7C 7D 7E 7F
+ A5 A6 A7 A8 A9 AA AB AC AD AE AF B0 B1 B2 B3 B4
+ B5 B6 B7 B8 B9 BA BB BC BD BE BF C0 C1 C2 C3 C4
+ C5 C6 C7 C8 C9 84 CA CB 88 CC 87 CD CE CF D0 8D
+ D1 D2 8C 8C 84 D3 D4 D5 88 D6 87 D7 D8 D9 DA 8D
+ 80 81 82 83 85 86 89 8A 8B 8E 8F 90 91 92 93 94
+ 95 96 97 98 99 9A 9B 9C 9D 9E 9F A0 A1 A2 A3 A4
+ 80 81 82 83 85 86 89 8A 8B 8E 8F 90 91 92 93 94
+ 95 96 97 98 99 9A 9B 9C 9D 9E 9F A0 A1 A2 A3 A4
diff --git a/sql/share/czech/errmsg.sys b/sql/share/czech/errmsg.sys
new file mode 100644
index 00000000000..e509b71a857
--- /dev/null
+++ b/sql/share/czech/errmsg.sys
Binary files differ
diff --git a/sql/share/czech/errmsg.txt b/sql/share/czech/errmsg.txt
new file mode 100644
index 00000000000..7403c915bac
--- /dev/null
+++ b/sql/share/czech/errmsg.txt
@@ -0,0 +1,209 @@
+/* Copyright Abandoned 1997 TCX DataKonsult AB & Monty Program KB & Detron HB
+ This file is public domain and comes with NO WARRANTY of any kind */
+
+/* Modifikoval Petr ¹najdr, snajdr@pvt.net, snajdr@cpress.cz v.0.01
+ ISO LATIN-8852-2
+ Upravil Jan Pazdziora, adelton@fi.muni.cz
+ Tue Nov 18 17:53:55 MET 1997 verze 0.02
+ Roz¹íøeno podle 3.21.15c Jan Pazdziora, adelton@fi.muni.cz
+ Tue Dec 2 19:08:54 MET 1997 verze 0.03
+ Roz¹íøeno podle 3.21.29 Jan Pazdziora, adelton@fi.muni.cz
+ Thu May 7 17:40:49 MET DST 1998 verze 0.04
+ Podle verze 3.22.20 upravil Jan Pazdziora, adelton@fi.muni.cz
+ Thu Apr 1 20:49:57 CEST 1999
+ Podle verze 3.23.2 upravil Jan Pazdziora, adelton@fi.muni.cz
+ Mon Aug 9 13:30:09 MET DST 1999
+*/
+
+"hashchk",
+"isamchk",
+"NE",
+"ANO",
+"Nemohu vytvoøit soubor '%-.64s' (chybový kód: %d)",
+"Nemohu vytvoøit tabulku '%-.64s' (chybový kód: %d)",
+"Nemohu vytvoøit databázi '%-.64s', chyba %d",
+"Nemohu vytvoøit databázi '%-.64s', databáze ji¾ existuje",
+"Nemohu zru¹it databázi '%-.64s', databáze neexistuje",
+"Chyba pøi ru¹ení databáze (nemohu vymazat '%-.64s', chyba %d)",
+"Chyba pøi ru¹ení databáze (nemohu vymazat adresáø '%-.64s', chyba %d)",
+"Chyba pøi výmazu '%-.64s' (chybový kód: %d)",
+"Nemohu èíst záznam v systémové tabulce",
+"Nemohu získat stav '%-.64s' (chybový kód: %d)",
+"Chyba pøi zji¹»ování pracovní adresáø (chybový kód: %d)",
+"Nemohu uzamknout soubor (chybový kód: %d)",
+"Nemohu otevøít soubor '%-.64s' (chybový kód: %d)",
+"Nemohu najít soubor '%-.64s' (chybový kód: %d)",
+"Nemohu èíst adresáø '%-.64s' (chybový kód: %d)",
+"Nemohu zmìnit adresáø na '%-.64s' (chybový kód: %d)",
+"Záznam byl zmìnìn od posledního ètení v tabulce '%-.64s'",
+"Disk je plný (%s), èekám na uvolnìní nìjakého místa ...",
+"Nemohu zapsat, zdvojený klíè v tabulce '%-.64s'",
+"Chyba pøi zavírání '%-.64s' (chybový kód: %d)",
+"Chyba pøi ètení souboru '%-.64s' (chybový kód: %d)",
+"Chyba pøi pøejmenování '%-.64s' na '%-.64s' (chybový kód: %d)",
+"Chyba pøi zápisu do souboru '%-.64s' (chybový kód: %d)",
+"'%-.64s' je zamèen proti zmìnám",
+"Tøídìní pøeru¹eno",
+"Pohled '%-.64s' pro '%-.64s' neexistuje",
+"Obsluha tabulky vrátila chybu %d",
+"Obsluha tabulky '%-.64s' nemá tento parametr",
+"Nemohu najít záznam v '%-.64s'",
+"Nesprávná informace v souboru '%-.64s'",
+"Nesprávný klíè pro tabulku '%-.64s'. Pokuste se ho opravit",
+"Starý klíèový soubor pro '%-.64s'. Opravte ho.",
+"'%-.64s' je jen pro ètení",
+"Málo pamìti. Pøestartujte daemona a zkuste znovu (je potøeba %d bytù)",
+"Málo pamìti pro tøídìní. Zvy¹te velikost tøídícího bufferu",
+"Neoèekávaný konec souboru pøi ètení '%-.64s' (chybový kód: %d)",
+"Pøíli¹ mnoho spojení",
+"Málo prostoru/pamìti pro thread",
+"Nemohu zjistit jméno stroje pro Va¹i adresu",
+"Chyba pøi ustavování spojení",
+"Pøístup pro u¾ivatele '%-.32s@%-.64s' k databázi '%-.64s' není povolen",
+"Pøístup pro u¾ivatele '%-.32s@%-.64s' (s heslem %s)",
+"Nebyla vybrána ¾ádná databáze",
+"Neznámý pøíkaz",
+"Sloupec '%-.64s' nemù¾e být null",
+"Neznámá databáze '%-.64s'",
+"Tabulka '%-.64s' ji¾ existuje",
+"Neznámá tabulka '%-.64s'",
+"Sloupec '%-.64s' v %s není zcela jasný",
+"Probíhá ukonèování práce serveru",
+"Neznámý sloupec '%-.64s' v %s",
+"Pou¾ité '%-.64s' nebylo v group by",
+"Nemohu pou¾ít group na '%-.64s'",
+"Pøíkaz obsahuje zároveò funkci sum a sloupce",
+"Poèet sloupcù neodpovídá zadané hodnotì",
+"Jméno identifikátoru '%-.64s' je pøíli¹ dlouhé",
+"Zdvojené jméno sloupce '%-.64s'",
+"Zdvojené jméno klíèe '%-.64s'",
+"Zvojený klíè '%-.64s' (èíslo klíèe %d)",
+"Chybná specifikace sloupce '%-.64s'",
+"%s blízko '%-.64s' na øádku %d",
+"Výsledek dotazu je prázdný",
+"Nejednoznaèná tabulka/alias: '%-.64s'",
+"Chybná defaultní hodnota pro '%-.64s'",
+"Definováno více primárních klíèù",
+"Zadáno pøíli¹ mnoho klíèù, je povoleno nejvíce %d klíèù",
+"Zadáno pøíli¹ mnoho èást klíèù, je povoleno nejvíce %d èástí",
+"Zadaný klíè byl pøíli¹ dlouhý, nejvìt¹í délka klíèe je %d",
+"Klíèový sloupec '%-.64s' v tabulce neexistuje",
+"Blob sloupec '%-.64s' nemù¾e být pou¾it jako klíè",
+"Pøíli¹ velká délka sloupce '%-.64s' (nejvíce %d). Pou¾ijte BLOB",
+"Mù¾ete mít pouze jedno AUTO pole a to musí být definováno jako klíè",
+"%s: pøipraven na spojení\n",
+"%s: normální ukonèení\n",
+"%s: pøijat signal %d, konèím\n",
+"%s: ukonèení práce hotovo\n",
+"%s: násilné uzavøení threadu %ld u¾ivatele '%-.64s'\n",
+"Nemohu vytvoøit IP socket",
+"Tabulka '%-.64s' nemá index odpovídající CREATE INDEX. Vytvoøte tabulku znovu",
+"Argument separátoru polo¾ek nebyl oèekáván. Pøeètìte si manuál",
+"Není mo¾né pou¾ít pevný rowlength s BLOBem. Pou¾ijte 'fields terminated by'.",
+"Soubor '%-.64s' musí být v adresáøi databáze nebo èitelný pro v¹echny",
+"Soubor '%-.64s' ji¾ existuje",
+"Záznamù: %ld Vymazáno: %ld Pøeskoèeno: %ld Varování: %ld",
+"Záznamù: %ld Zdvojených: %ld",
+"Chybná podèást klíèe -- není to øetìzec nebo je del¹í ne¾ délka èásti klíèe",
+"Není mo¾né vymazat v¹echny polo¾ky s ALTER TABLE. Pou¾ijte DROP TABLE",
+"Nemohu zru¹it '%-.64s' (provést DROP). Zkontrolujte, zda neexistují záznamy/klíèe",
+"Záznamù: %ld Zdvojených: %ld Varování: %ld",
+"INSERT TABLE '%-.64s' není dovoleno v seznamu tabulek FROM",
+"Neznámá identifikace threadu: %lu",
+"Nejste vlastníkem threadu %lu",
+"Nejsou pou¾ity ¾ádné tabulky",
+"Pøíli¹ mnoho øetìzcù pro sloupec %s a SET",
+"Nemohu vytvoøit jednoznaèné jméno logovacího souboru %s.(1-999)\n",
+"Tabulka '%-.64s' byla zamèena s READ a nemù¾e být zmìnìna",
+"Tabulka '%-.64s' nebyla zamèena s LOCK TABLES",
+"Blob polo¾ka '%-.64s' nemù¾e mít defaultní hodnotu",
+"Nepøípustné jméno databáze '%-.64s'",
+"Nepøípustné jméno tabulky '%-.64s'",
+"Zadaný SELECT by procházel pøíli¹ mnoho záznamù a trval velmi dlouho. Zkontrolujte tvar WHERE a je-li SELECT v poøádku, pou¾ijte SET OPTION SQL_BIG_SELECTS=1",
+"Neznámá chyba",
+"Neznámá procedura %s",
+"Chybný poèet parametrù procedury %s",
+"Chybné parametry procedury %s",
+"Neznámá tabulka '%-.64s' v %s",
+"Polo¾ka '%-.64s' je zadána dvakrát",
+"Nesprávné pou¾ití funkce group",
+"Tabulka '%-.64s' pou¾ívá roz¹íøení, které v této verzi MySQL není",
+"Tabulka musí mít alespoò jeden sloupec",
+"Tabulka '%-.64s' je plná",
+"Neznámá znaková sada: '%-.64s'",
+"Pøíli¹ mnoho tabulek, MySQL jich mù¾e mít v joinu jen %d",
+"Pøíli¹ mnoho polo¾ek",
+"Øádek je pøíli¹ velký. Maximální velikost øádku, nepoèítaje polo¾ky blob, je %d. Musíte zmìnit nìkteré polo¾ky na blob",
+"Pøeteèení zásobníku threadu: pou¾ito %ld z %ld. Pou¾ijte 'mysqld -O thread_stack=#' k zadání vìt¹ího zásobníku",
+"V OUTER JOIN byl nalezen køí¾ový odkaz. Provìøte ON podmínky",
+"Sloupec '%-.32s' je pou¾it s UNIQUE nebo INDEX, ale není definován jako NOT NULL",
+"Nemohu naèíst funkci '%-.64s'",
+"Nemohu inicializovat funkci '%-.64s'; %-.80s",
+"Pro sdílenou knihovnu nejsou povoleny cesty",
+"Funkce '%-.64s' ji¾ existuje",
+"Nemohu otevøít sdílenou knihovnu '%-.64s' (errno: %d %s)",
+"Nemohu najít funkci '%-.64s' v knihovnì'",
+"Funkce '%-.64s' není definována",
+"Stroj '%-.64s' je zablokován kvùli mnoha chybám pøi pøipojování. Odblokujete pou¾itím 'mysqladmin flush-hosts'",
+"Stroj '%-.64s' nemá povoleno se k tomuto MySQL serveru pøipojit",
+"Pou¾íváte MySQL jako anonymní u¾ivatel a anonymní u¾ivatelé nemají povoleno mìnit hesla",
+"Na zmìnu hesel ostatním musíte mít právo provést update tabulek v databázi mysql",
+"V tabulce user není ¾ádný odpovídající øádek",
+"Nalezených øádkù: %ld Zmìnìno: %ld Varování: %ld",
+"Nemohu vytvoøit nový thread (errno %d). Pokud je je¹tì nìjaká volná pamì», podívejte se do manuálu na èást o chybách specifických pro jednotlivé operaèní systémy",
+"Poèet sloupcù neodpovídá poètu hodnot na øádku %ld",
+"Nemohu znovuotevøít tabulku: '%-.64s',
+"Neplatné u¾ití hodnoty NULL",
+"Regulární výraz vrátil chybu '%-.64s'",
+"Pokud není ¾ádná GROUP BY klauzule, není dovoleno souèasné pou¾ití GROUP polo¾ek (MIN(),MAX(),COUNT()...) s ne GROUP polo¾kami",
+"Neexistuje odpovídající grant pro u¾ivatele '%-.32s' na stroji '%-.64s'",
+"%-.16s pøíkaz nepøístupný pro u¾ivatele: '%-.32s@%-.64s' pro tabulku '%-.64s'",
+"%-.16s pøíkaz nepøístupný pro u¾ivatele: '%-.32s@%-.64s' pro sloupec '%-.64s' v tabulce '%-.64s'",
+"Neplatný pøíkaz GRANT/REVOKE. Prosím, pøeètìte si v manuálu, jaká privilegia je mo¾né pou¾ít.",
+"Argument pøíkazu GRANT u¾ivatel nebo stroj je pøíli¹ dlouhý",
+"Tabulka '%-64s.%s' neexistuje",
+"Neexistuje odpovídající grant pro u¾ivatele '%-.32s' na stroji '%-.64s' pro tabulku '%-.64s'",
+"Pou¾itý pøíkaz není v této verzi MySQL povolen",
+"Va¹e syntaxe je nìjaká divná",
+"Zpo¾dìný insert threadu nebyl schopen získat po¾adovaný zámek pro tabulku %-.64s",
+"Pøíli¹ mnoho zpo¾dìných threadù",
+"Zru¹eno spojení %ld do databáze: '%-.64s' u¾ivatel: '%-.64s' (%s)",
+"Zji¹tìn pøíchozí packet del¹í ne¾ 'max_allowed_packet'",
+"Zji¹tìna chyba pøi ètení z roury spojení",
+"Zji¹tìna chyba fcntl()",
+"Pøíchozí packety v chybném poøadí",
+"Nemohu rozkomprimovat komunikaèní packet",
+"Zji¹tìna chyba pøi ètení komunikaèního packetu",
+"Zji¹tìn timeout pøi ètení komunikaèního packetu",
+"Zji¹tìna chyba pøi zápisu komunikaèního packetu",
+"Zji¹tìn timeout pøi zápisu komunikaèního packetu",
+"Výsledný øetìzec je del¹í ne¾ max_allowed_packet",
+"Typ pou¾ité tabulky nepodporuje BLOB/TEXT sloupce",
+"Typ pou¾ité tabulky nepodporuje AUTO_INCREMENT sloupce",
+"INSERT DELAYED není mo¾no s tabulkou '%-.64s' pou¾ít, proto¾e je zamèená pomocí LOCK TABLES",
+"Nesprávné jméno sloupce '%-.100s'",
+"Handler pou¾ité tabulky neumí indexovat sloupce '%-.64s'",
+"V¹echny tabulky v MERGE tabulce nejsou definovány stejnì",
+"Kvùli unique constraintu nemozu zapsat do tabulky '%-.64s'",
+"BLOB sloupec '%-.64s' je pou¾it ve specifikaci klíèe bez délky",
++"V-B¹echny èásti primárního klíèe musejí být NOT NULL; pokud potøebujete NULL, pou¾ijte UNIQUE",-A
++"V-Býsledek obsahuje více ne¾ jeden øádek",-A
++"Tento typ tabulky vy-B¾aduje primární klíè",-A
++"Tato verze MySQL nen-Bí zkompilována s podporou RAID",-A
++"Update tabulky bez WHERE s kl-Bíèem není v módu bezpeèných update dovoleno",-A
++"Kl-Bíè '%-.64s' v tabulce '%-.64s' neexistuje",-A
++"Nemohu otev-Bøít tabulku",-A
++"Handler tabulky nepodporuje check/repair",
+"You are not allowed to execute this command in a transaction",
+"Got error %d during COMMIT",
+"Got error %d during ROLLBACK",
+"Got error %d during FLUSH_LOGS",
+"Got error %d during CHECKPOINT",
+"Aborted connection %ld to db: '%-.64s' user: '%-.32s' host: `%-.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",
diff --git a/sql/share/danish/errmsg.sys b/sql/share/danish/errmsg.sys
new file mode 100644
index 00000000000..e59cb624472
--- /dev/null
+++ b/sql/share/danish/errmsg.sys
Binary files differ
diff --git a/sql/share/danish/errmsg.txt b/sql/share/danish/errmsg.txt
new file mode 100644
index 00000000000..18dd8ce09b8
--- /dev/null
+++ b/sql/share/danish/errmsg.txt
@@ -0,0 +1,198 @@
+/* Copyright Abandoned 1997 TCX DataKonsult AB & Monty Program KB & Detron HB
+ This file is public domain and comes with NO WARRANTY of any kind */
+
+/* Knud Riishøjgård knudriis@post.tele.dk 99 &&
+ Carsten H. Pedersen, carsten.pedersen@bitbybit.dk oct. 1999 */
+
+"hashchk",
+"isamchk",
+"NEJ",
+"JA",
+"Kan ikke oprette filen '%-.64s' (Fejlkode: %d)",
+"Kan ikke opprette tabellen '%-.64s' (Fejlkode: %d)",
+"Kan ikke oprette databasen '%-.64s'. Fejl %d",
+"Kan ikke oprette databasen '%-.64s'. Databasen eksisterer",
+"Kan ikke slette (droppe) '%-.64s'. Databasen eksisterer ikke",
+"Fejl ved sletning (drop) af databasen (kan ikke slette '%-.64s', Fejl %d)",
+"Fejl ved sletting af database (kan ikke slette biblioteket '%-.64s', Fejl %d)",
+"Fejl ved sletning af '%-.64s' (Fejlkode: %d)",
+"Kan ikke læse posten i systembiblioteket",
+"Kan ikke læse status af '%-.64s' (Fejlkode: %d)",
+"Kan ikke læse aktive bibliotek (Fejlkode: %d)",
+"Kan ikke låse fil (Fejlkode: %d)",
+"Kan ikke åbne fil: '%-.64s'. (Fejlkode: %d)",
+"Kan ikke finde fila: '%-.64s' (Fejlkode: %d)",
+"Kan ikke læse bibliotek '%-.64s' (Fejlkode: %d)",
+"Kan ikke skifte bibliotek til '%-.64s' (Fejlkode: %d)",
+"Posten erændret siden sidst læst '%-.64s'",
+"Ikke mere diskplads (%s). Venter på at få frigjort plads....",
+"Kan ikke skrive, flere ens nøgler i tabellen '%-.64s'",
+"Fejl ved lukning af '%-.64s' (Fejlkode: %d)",
+"Fejl ved læsning af '%-.64s' (Fejlkode: %d)",
+"Fejl ved omdøbning af '%-.64s' til '%-.64s' (Fejlkode: %d)",
+"Fejl ved skriving av filen '%-.64s' (Fejlkode: %d)",
+"'%-.64s' er låst mod opdateringer",
+"Sortering afbrutt",
+"View '%-.64s' eksisterer ikke for '%-.64s'",
+"Modtog fejl %d fra tabel håndterer",
+"Tabel håndtereren for '%-.64s' har ikke denne mulighed",
+"Kan ikke finde posten i '%-.64s'",
+"Forkert indhold i: '%-.64s'",
+"Fejl i indeksfilen til tabellen '%-.64s', prøv at reparere den",
+"Gammel indeksfil for tabellen '%-.64s'; Reparer den",
+"'%-.64s' er skrivebeskyttet",
+"Ikke mere hukommelse. Genstart serveren og prøv igen (mangler %d bytes)",
+"Ikke mere sorteringshukommelse. Øg sorteringshukommelse (sort buffer size) for serveren",
+"Uventet sluttning af fil (eof) ved læsning af filen '%-.64s' (Fejlkode: %d)",
+"For mange tilkoblinger (connections)",
+"Udgået for tråde/hukommelse",
+"Kan ikke få værtsnavn for din adresse",
+"Forkert håndtryk (handshake)",
+"Adgang nægtet bruger: '%-.32s@%-.64s' til databasen '%-.64s'",
+"Adgang nægtet bruger: '%-.32s@%-.64s' (Bruger password: %s)",
+"Ingen database valgt",
+"Ukendt kommando",
+"Kolonne '%-.64s' kan ikke være nul",
+"Ukendt database '%-.64s'",
+"Tabellen '%-.64s' eksisterer allerede",
+"Ukendt tabel '%-.64s'",
+"Felt: '%-.64s' i tabel %s er ikke entydigt",
+"Database nedkobling er i gang",
+"Ukendt kolonne '%-.64s' i tabel %s",
+"Grugte '%-.64s' som ikke var i group by",
+"Kan ikke gruppere på '%-.64s'",
+"Udtrykket har summer (sum) funktioner og kolonner i samme udtryk",
+"Kolonne tæller stemmer ikke med værditæller",
+"Identifikationen '%-.64s' er for lang",
+"Feltnavnet '%-.64s' eksisterer allerede",
+"Indeksnavnet '%-.64s' eksisterer allerede",
+"Ens værdier '%-.64s' for indeks %d",
+"Forkert kolonnespecifikaton for felt '%-.64s'",
+"%s nær '%-.64s' på linje %d",
+"Forespørgsel var tom",
+"Ikke unikt tabel/alias: '%-.64s'",
+"Ugyldig standardværdi for '%-.64s'",
+"Flere primærindekser specificeret",
+"For mange indekser specificeret. Maks %d indekser tillatt",
+"For mange indeksdele specificeret. Maks %d dele tillatt",
+"Specificeret indeks var for langt. Maks indekslængde er %d",
+"Indeks felt '%-.64s' eksiterer ikke i tabellen",
+"Blob felt '%-.64s' kan ikke bruges ved specifikation af indeks",
+"For stor feltlængde for kolonne '%-.64s' (maks = %d). Brug BLOB i stedet",
+"Der kan kun bruges eet AUTO-felt og det skal være indekseret",
+"%s: klar for tilslutninger\n",
+"%s: Normal nedlukning\n",
+"%s: Opdaget signal %d. Afslutter!!\n",
+"%s: Server lukket\n",
+"%s: Forceret nedlukning af tråd: %ld bruger: '%-.64s'\n",
+"Kan ikke oprette IP socket",
+"Tabellen '%-.64s' har intet indeks som det der er brugt i CREATE INDEX. Genopret tabellen",
+"Felt adskiller er ikke som forventet, se dokumentationen",
+"Man kan ikke bruge faste feltlængder med BLOB. Brug i stedet 'fields terminated by'.",
+"Filen '%-.64s' skal være i database-biblioteket for at kunne læses af alle",
+"Filen '%-.64s' eksisterer allerede",
+"Poster: %ld Fjernet: %ld Sprunget over: %ld Advarsler: %ld",
+"Poster: %ld Ens: %ld",
+"Forkert indeksdel. Den anvendte indeksdel er ikke en streng eller den længden er større end indekslængden",
+"Man kan ikke slette alle felter med ALTER TABLE. Brug DROP TABLE i stedet.",
+"Kan ikke DROP '%-.64s'. Undersøg om felt/indeks eksisterer.",
+"Poster: %ld Ens: %ld Advarsler: %ld",
+"INSERT TABLE '%-.64s' er ikke tilladt i FROM tabel liste",
+"Ukendt tråd id: %lu",
+"Du er ikke ejer av tråden %lu",
+"Ingen tabeller i brug",
+"For mange tekststrenge kolonne %s og SET",
+"Kan ikke lave unikt loggfilnavn %s.(1-999)\n",
+"Tabellen '%-.64s' var låst med READ lås og kan ikke opdateres",
+"Tabellen '%-.64s' var ikke låst med LOCK TABLES",
+"Blob feltet '%-.64s' kan ikke have en standard værdi",
+"Ugyldigt database navn '%-.64s'",
+"Ugyldigt tabel navn '%-.64s'",
+"SELECT ville undersøge for mange poster og ville sannsynligvis tage meget lang tid. Undersøg WHERE delen og brug SET OPTION SQL_BIG_SELECTS=1 hvis SELECTen er korrekt"
+"Ukendt fejl",
+"Ukendt procedure %s",
+"Forkert antal parametre til proceduren %s",
+"Forkert(e) parametre til proceduren %s",
+"Ukendt tabel '%-.64s' i %s",
+"Feltet '%-.64s' er anvendt to ganger",
+"Forkert brug af gruppe-funktion",
+"Tabellen '%-.64s' bruger et efternavn som ikke findes i denne MySQL version",
+"En tabel skal have mindst een kolonne",
+"Tabellen '%-.64s' er fuld",
+"Ukendt karaktersæt: '%-.64s'",
+"For mange tabeller. MySQL kan kun bruge %d tabeller i et join",
+"For mange felter",
+"For store poster. Max post størrelse, unde BOLB's, er %d. Du må lave nogle felter til BLOB's",
+"Thread stack brugt: Brugt: %ld af en %ld stak. Brug 'mysqld -O thread_stack=#' for at allokere en større stak om nødvendigt",
+"Krydsreferencer fundet i OUTER JOIN. Check dine ON conditions",
+"Kolonne '%-.32s' bruges som UNIQUE eller INDEX men er ikke defineret som NOT NULL",
+"Kan ikke læse funktionen '%-.64s'",
+"Kan ikke starte funktionen '%-.64s'; %-.80s",
+"Ingen sti tilladte for delt bibliotek",
+"Funktionen '%-.64s' findes allerede",
+"Kan ikke åbne delt bibliotek '%-.64s' (errno: %d %s)",
+"Kan ikke finde funktionen '%-.64s' in bibliotek'",
+"Funktionen '%-.64s' er ikke defineret",
+"Værten er blokeret på grund af mange fejlforespørgsler. Lås op med 'mysqladmin flush-hosts'",
+"Værten '%-.64s' kan ikke tilkoble denne MySQL-server",
+"Du bruger MySQL som anonym bruger. Anonyme brugere må ikke ændre adgangskoder",
+"Du skal have tilladelse til at opdatere tabeller i MySQL databasen for at ændre andres adgangskoder",
+"Kan ikke finde nogen tilsvarende poster i bruger tabellen",
+"Poster fundet: %ld Ændret: %ld Advarsler: %ld",
+"Kan ikke danne en ny tråd (thread) (errno %d). Hvis computeren ikke er løbet tør for hukommelse, kan du se i brugervejledningen for en mulig operativ-system - afhængig fejl",
+"Kolonne antallet stemmer ikke overens med antallet af værdier i post %ld",
+"Kan ikke genåbne tabel '%-.64s',
+"Forkert brug af nulværdi (NULL)",
+"Fik fejl '%-.64s' fra regexp",
+"Sammenblanding af GROUP kolonner (MIN(),MAX(),COUNT()...) uden GROUP kolonner er ikke tilladt, hvis der ikke er noget GROUP BY prædikat",
+"Denne tilladelse findes ikke for brugeren '%-.32s' på vært '%-.64s'",
+"%-.16s-kommandoen er ikke tilladt for brugeren '%-.32s@%-.64s' for tabellen '%-.64s'",
+"%-.16s-kommandoen er ikke tilladt for brugeren '%-.32s@%-.64s' for kolonne '%-.64s' in tabellen '%-.64s'",
+"Forkert GRANT/REVOKE kommando. Se i brugervejledningen hvilke privilegier der kan specificeres.",
+"Værts- eller brugernavn for langt til GRANT",
+"Tabellen '%-.64s.%-.64s' eksisterer ikke",
+"Denne tilladelse eksisterer ikke for brugeren '%-.32s' på vært '%-.64s' for tabellen '%-.64s'",
+"Den brugte kommando er ikke tilladt med denne udgave af MySQL",
+"Der er en fejl i SQL syntaksen",
+"Forsinket indsættelse tråden (delayed insert thread) kunne ikke opnå lås på tabellen %-.64s",
+"For mange slettede tråde (threads) i brug",
+"Afbrudt forbindelse %ld til database: '%-.64s' bruger: '%-.64s' (%-.64s)",
+"Modtog en datapakke som var større end 'max_allowed_packet'",
+"Fik læsefejl fra forbindelse (connection pipe)",
+"Fik fejlmeddelelse fra fcntl()",
+"Modtog ikke datapakker i korrekt rækkefølge",
+"Kunne ikke dekomprimere kommunikations-pakke (communication packet)",
+"Fik fejlmeddelelse ved læsning af kommunikations-pakker (communication packets)"
+"Timeout-fejl ved læsning af kommunukations-pakker (communication packets)",
+"Fik fejlmeddelelse ved skrivning af kommunukations-pakker (communication packets)",
+"Timeout-fejl ved skrivning af kommunukations-pakker (communication packets)",
+"Strengen med resultater er større end max_allowed_packet",
+"Denne tabeltype understøtter ikke brug af BLOB og TEXT kolonner",
+"Denne tabeltype understøtter ikke brug af AUTO_INCREMENT kolonner",
+"INSERT DELAYED kan ikke bruges med tabellen '%-.64s', fordi tabellen er låst med LOCK TABLES",
+"Forkert kolonnenavn '%-.100s'",
+"Den brugte tabel styrer kan ikke indeksere kolonnen '%-.64s'",
+"Tabellerne i MERGE er ikke defineret ens",
+"Kan ikke skrive til tabellen '%-.64s' fordi det vil bryde CONSTRAINT regler",
+"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",
+"Result consisted of more than one row",
+"This table type requires a primary key",
+"This version of MySQL is not compiled with RAID support",
+"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",
+"You are not allowed to execute this command in a transaction",
+"Got error %d during COMMIT",
+"Got error %d during ROLLBACK",
+"Got error %d during FLUSH_LOGS",
+"Got error %d during CHECKPOINT",
+"Aborted connection %ld to db: '%-.64s' user: '%-.32s' host: `%-.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",
diff --git a/sql/share/dutch/errmsg.sys b/sql/share/dutch/errmsg.sys
new file mode 100644
index 00000000000..0950a8d9bc4
--- /dev/null
+++ b/sql/share/dutch/errmsg.sys
Binary files differ
diff --git a/sql/share/dutch/errmsg.txt b/sql/share/dutch/errmsg.txt
new file mode 100644
index 00000000000..960200aa182
--- /dev/null
+++ b/sql/share/dutch/errmsg.txt
@@ -0,0 +1,195 @@
+/* Copyright Abandoned 1997 TCX DataKonsult AB & Monty Program KB & Detron HB
+ This file is public domain and comes with NO WARRANTY of any kind */
+
+"hashchk",
+"isamchk",
+"NEE",
+"JA",
+"Kan file '%-.64s' niet aanmaken. (Errcode: %d)",
+"Kan tabel '%-.64s' niet aanmaken. (Errcode: %d)",
+"Kan database '%-.64s' niet aanmaken. Error %d",
+"Kan database '%-.64s' niet aanmaken. Database bestaat al",
+"Kan database '%-.64s' niet verwijderen. Database bestaat niet",
+"Error verwijderen database (kan '%-.64s' niet verwijderen, error %d)",
+"Error verwijderen database (kan rmdir '%-.64s' niet uitvoeren, error %d)",
+"Error 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)",
+"Kan de file niet blokeren (Errcode: %d)",
+"Kan de file '%-.64s' niet openen. (Errcode: %d)",
+"Kan de file: '%-.64s' niet vinden. (Errcode: %d)",
+"Kan de directory niet lezen van '%-.64s' (Errcode: %d)",
+"Kan de directory niet veranderen naar '%-.64s' (Errcode: %d)",
+"Record is veranderd sinds de laatste lees activiteit in de tabel '%-.64s'",
+"Schijf vol (%s). Aan het wachten totdat er ruimte vrij wordt gemaakt...",
+"Kan niet schrijven, dubbele key in tabel '%-.64s'",
+"Fout bij het sluiten van '%-.64s' (Errcode: %d)",
+"Fout bij het lezen van file '%-.64s' (Errcode: %d)",
+"Fout bij het hernoemen van '%-.64s' naar '%-.64s' (Errcode: %d)",
+"Fout bij het wegschrijven van file '%-.64s' (Errcode: %d)",
+"'%-.64s' is geblokeerd tegen veranderingen",
+"Sorteren afgebroken",
+"View '%-.64s' bestaat niet voorr '%-.64s'",
+"Fout %d van tabel handler",
+"Tabel handler voor '%-.64s' heeft deze optie niet",
+"Kan record niet vinden in '%-.64s'",
+"Verkeerde info in file: '%-.64s'",
+"Verkeerde key file voor tabel: '%-.64s'. Probeer het te repareren",
+"Oude key file voor tabel '%-.64s'; Repareer het!",
+"'%-.64s' is alleen leesbaar",
+"Geen geheugen meer. Herstart server en probeer opnieuw (%d bytes nodig)",
+"Geen geheugen om te sorteren. Verhoog de server sort buffer size",
+"Onverwachte eof gevonden tijdens het lezen van file '%-.64s' (Errcode: %d)",
+"Te veel verbindingen",
+"Geen thread geheugen meer",
+"Kan de hostname niet krijgen van jou adres",
+"Verkeerde handshake",
+"Toegang geweigerd voor gebruiker: '%-.32s@%-.64s' naar database '%-.64s'",
+"Toegang geweigerd voor gebruiker: '%-.32s@%-.64s' (Wachtwoord gebruikt: %s)",
+"Geen database geselecteerd",
+"Onbekend commando",
+"Kolom '%-.64s' kan niet null zijn",
+"Onbekende database '%-.64s'",
+"Tabel '%-.64s' bestaat al",
+"Onbekende tabel '%-.64s'",
+"Kolom: '%-.64s' in %s is ambiguous",
+"Bezig met het stoppen van de server",
+"Onbekende kolom '%-.64s' in %s",
+"Opdracht gebruikt '%-.64s' dat niet in de group by voorkomt",
+"Kan '%-.64s' niet groeperen",
+"Opdracht heeft totaliseer functies en kolommen in dezelfde opdracht",
+"Het aantal kolommen komt niet overeen met het aantal opgegeven waardes",
+"Naam voor herkenning '%-.64s' is te lang",
+"Dubbele kolom naam '%-.64s'",
+"Dubbele sleutel naam '%-.64s'",
+"Dubbele ingang '%-.64s' voor sleutel %d",
+"Verkeerde kolom specificatie voor kolom '%-.64s'",
+"%s bij '%-.64s' in regel %d",
+"Query was leeg",
+"Niet unieke waarde tabel/alias: '%-.64s'",
+"Foutieve standaard waarde voor '%-.64s'",
+"Meerdere primaire sleutels gedefinieerd",
+"Teveel sleutels gedifinieerd. Maximaal zijn %d sleutels toegestaan",
+"Teveel sleutel onderdelen gespecificeerd. Maximaal %d onderdelen toegestaan",
+"Gespecificeerde sleutel was te lang. De maximale lengte is %d",
+"Sleutel kolom '%-.64s' bestaat niet in tabel",
+"Blob kolom '%-.64s' kan niet gebruikt worden bij key specificatie",
+"Te grote kolomlengte voor '%-.64s' (max = %d). Maak hiervoor gebruik van het type BLOB",
+"Er kan slechts 1 autofield zijn en deze moet als sleutel worden gedefinieerd.",
+"%s: klaar voor verbindingen\n",
+"%s: Normaal afgesloten \n",
+"%s: Signaal %d. Systeem breekt af!\n",
+"%s: Afsluiten afgerond\n",
+"%s: Afsluiten afgedwongen van thread %ld gebruiker: '%-.64s'\n",
+"Kan IP-socket niet openen",
+"Tabel '%-.64s' heeft geen INDEX zoals deze gemaakt worden met CREATE INDEX. Maak de tabel opnieuw",
+"De argumenten om velden te scheiden zijn anders dan verwacht. Controleer de handleiding","
+"Bij het gebruik van BLOBs is het niet mogelijk om vaste rijlengte te gebruiken. Maak s.v.p. gebruik van 'fields terminated by'.",
+"Het bestand '%-.64s' dient in de database directory voor the komen of leesbaar voor iedereen te zijn.",
+"Het bestand '%-.64s' bestaat reeds",
+"Records: %ld Verwijderd: %ld Overgeslagen: %ld Waarschuwingen: %ld",
+"Records: %ld Dubbel: %ld",
+"Foutief sub-gedeelte van de sleutel. De gebruikte sleutel is geen onderdeel van een string of of de gebruikte lengte is langer dan de sleutel",
+"Het is niet mogelijk alle velden te verwijderen met ALTER TABLE. Gebruik a.u.b. DROP TABLE hiervoor!",
+"Kan '%-.64s' niet weggooien. Controleer of het veld of de sleutel daadwerkelijk bestaat.",
+"Records: %ld Dubbel: %ld Waarschuwing: %ld",
+"INSERT TABLE '%-.64s' is niet toegestaan in de FROM tabel-lijst",
+"Onbekend thread id: %lu",
+"U bent geen bezitter van thread %lu",
+"Geen tabellen gebruikt.",
+"Teveel strings voor kolom %s en SET",
+"Het is niet mogelijk een unieke naam te maken voor de logfile %s.(1-999)\n",
+"Tabel '%-.64s' was gelocked met een lock om te lezen. Derhalve kunnen geen wijzigingen worden opgeslagen.",
+"Table '%-.64s' was niet gelocked met LOCK TABLES",
+"Blob veld '%-.64s' can geen standaardwaarde bevatten",
+"Databasenaam '%-.64s' is niet getoegestaan",
+"Niet toegestane tabelnaam '%-.64s'",
+"Het SELECT-statement zou te veel records analyseren en dus veel tijd in beslagnemen. Kijk het WHERE-gedeelte van de query na en kies SET OPTION SQL_BIG_SELECTS=1 als het stament in orde is.",
+"Onbekende Fout",
+"Onbekende procedure %s",
+"Foutief aantal parameters doorgegeven aan procedure %s",
+"Foutieve parameters voor procedure %s",
+"Onbekende tabel '%-.64s' in %s",
+"Veld '%-.64s' is dubbel gespecificeerd",
+"Ongeldig gebruik van GROUP-functie",
+"Tabel '%-.64s' gebruikt een extensie, die niet in deze MySQL-versie voorkomt.",
+"Een tabel moet minstens 1 kolom bevatten",
+"De tabel '%-.64s' is vol",
+"Onbekende character set: '%-.64s'",
+"Teveel tabellen. MySQL kan slechts %d tabellen in een join bevatten",
+"Te veel velden",
+"Rij-grootte is groter dan toegestaan. Maximale rij grootte, blobs niet meegeteld, is %d. U dient sommige velden in blobs te veranderen.",
+"Thread stapel overrun: Gebruikte: %ld van een %ld stack. Gebruik 'mysqld -O thread_stack=#' om een grotere stapel te definieren (indien noodzakelijk).",
+"Gekruiste afhankelijkheid gevonden in OUTER JOIN. Controleer uw ON-conditions",
+"Column '%-.64s' is used with UNIQUE or INDEX but is not defined as NOT NULL",
+"Can't load function '%-.64s'",
+"Can't initialize function '%-.64s'; %-.80s",
+"No paths allowed for shared library",
+"Function '%-.64s' already exist",
+"Can't open shared library '%-.64s' (errno: %d %s)",
+"Can't find function '%-.64s' in library'",
+"Function '%-.64s' is not defined",
+"Host '%-.64s' is blocked because of many connection errors. Unblock with 'mysqladmin flush-hosts'",
+"Host '%-.64s' is not allowed to connect to this MySQL server",
+"You are using MySQL as an anonymous users and anonymous users are not allowed to change passwords",
+"You must have privileges to update tables in the mysql database to be able to change passwords for others",
+"Can't find any matching row in the user table",
+"Rows matched: %ld Changed: %ld Warnings: %ld",
+"Can't create a new thread (errno %d). If you are not out of available memory you can consult the manual for any possible OS dependent bug",
+"Column count doesn't match value count at row %ld",
+"Can't reopen table: '%-.64s',
+"Invalid use of NULL value",
+"Got error '%-.64s' from regexp",
+"Mixing of GROUP columns (MIN(),MAX(),COUNT()...) with no GROUP columns is illegal if there is no GROUP BY clause",
+"There is no such grant defined for user '%-.32s' on host '%-.64s'",
+"%-.16s command denied to user: '%-.32s@%-.64s' for table '%-.64s'",
+"%-.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",
+"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",
+"Delayed insert thread couldn't get requested lock for table %-.64s",
+"Too many delayed threads in use",
+"Aborted connection %ld to db: '%-.64s' user: '%-.64s' (%s)",
+"Got a packet bigger than 'max_allowed_packet'",
+"Got a read error from the connection pipe",
+"Got an error from fcntl()",
+"Got packets out of order",
+"Couldn't uncompress communication packet",
+"Got an error reading communication packets"
+"Got timeout reading communication packets",
+"Got an error writing communication packets",
+"Got timeout writing communication packets",
+"Result string is longer than max_allowed_packet",
+"The used table type doesn't support BLOB/TEXT columns",
+"The used table type doesn't support AUTO_INCREMENT columns",
+"INSERT DELAYED can't be used with table '%-.64s', because it is locked with LOCK TABLES",
+"Incorrect column name '%-.100s'",
+"The used table handler can't index column '%-.64s'",
+"All tables in the MERGE table are not defined identically",
+"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",
+"Result consisted of more than one row",
+"This table type requires a primary key",
+"This version of MySQL is not compiled with RAID support",
+"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",
+"You are not allowed to execute this command in a transaction",
+"Got error %d during COMMIT",
+"Got error %d during ROLLBACK",
+"Got error %d during FLUSH_LOGS",
+"Got error %d during CHECKPOINT",
+"Aborted connection %ld to db: '%-.64s' user: '%-.32s' host: `%-.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",
diff --git a/sql/share/english/errmsg.sys b/sql/share/english/errmsg.sys
new file mode 100644
index 00000000000..7451111fcce
--- /dev/null
+++ b/sql/share/english/errmsg.sys
Binary files differ
diff --git a/sql/share/english/errmsg.txt b/sql/share/english/errmsg.txt
new file mode 100644
index 00000000000..7c456a6f592
--- /dev/null
+++ b/sql/share/english/errmsg.txt
@@ -0,0 +1,195 @@
+/* Copyright Abandoned 1997 TCX DataKonsult AB & Monty Program KB & Detron HB
+ This file is public domain and comes with NO WARRANTY of any kind */
+
+"hashchk",
+"isamchk",
+"NO",
+"YES",
+"Can't create file '%-.64s' (errno: %d)",
+"Can't create table '%-.64s' (errno: %d)",
+"Can't create database '%-.64s'. (errno: %d)",
+"Can't create database '%-.64s'. Database exists",
+"Can't drop database '%-.64s'. Database doesn't exist",
+"Error dropping database (can't delete '%-.64s', errno: %d)",
+"Error dropping database (can't rmdir '%-.64s', errno: %d)",
+"Error on delete of '%-.64s' (errno: %d)",
+"Can't read record in system table",
+"Can't get status of '%-.64s' (errno: %d)",
+"Can't get working directory (errno: %d)",
+"Can't lock file (errno: %d)",
+"Can't open file: '%-.64s'. (errno: %d)",
+"Can't find file: '%-.64s' (errno: %d)",
+"Can't read dir of '%-.64s' (errno: %d)",
+"Can't change dir to '%-.64s' (errno: %d)",
+"Record has changed since last read in table '%-.64s'",
+"Disk full (%s). Waiting for someone to free some space....",
+"Can't write, duplicate key in table '%-.64s'",
+"Error on close of '%-.64s' (errno: %d)",
+"Error reading file '%-.64s' (errno: %d)",
+"Error on rename of '%-.64s' to '%-.64s' (errno: %d)",
+"Error writing file '%-.64s' (errno: %d)",
+"'%-.64s' is locked against change",
+"Sort aborted",
+"View '%-.64s' doesn't exist for '%-.64s'",
+"Got error %d from table handler",
+"Table handler for '%-.64s' doesn't have this option",
+"Can't find record in '%-.64s'",
+"Incorrect information in file: '%-.64s'",
+"Incorrect key file for table: '%-.64s'. Try to repair it",
+"Old key file for table '%-.64s'; Repair it!",
+"Table '%-.64s' is read only",
+"Out of memory. Restart daemon and try again (needed %d bytes)",
+"Out of sort memory. Increase daemon sort buffer size",
+"Unexpected eof found when reading file '%-.64s' (errno: %d)",
+"Too many connections",
+"Out of memory; Check if mysqld or some other process uses all available memory. If not you may have to use 'ulimit' to allow mysqld to use more memory or you can add more swap space",
+"Can't get hostname for your address",
+"Bad handshake",
+"Access denied for user: '%-.32s@%-.64s' to database '%-.64s'",
+"Access denied for user: '%-.32s@%-.64s' (Using password: %s)",
+"No Database Selected",
+"Unknown command",
+"Column '%-.64s' cannot be null",
+"Unknown database '%-.64s'",
+"Table '%-.64s' already exists",
+"Unknown table '%-.64s'",
+"Column: '%-.64s' in %-.64s is ambiguous",
+"Server shutdown in progress",
+"Unknown column '%-.64s' in '%-.64s'",
+"'%-.64s' isn't in GROUP BY",
+"Can't group on '%-.64s'",
+"Statement has sum functions and columns in same statement",
+"Column count doesn't match value count",
+"Identifier name '%-.100s' is too long",
+"Duplicate column name '%-.64s'",
+"Duplicate key name '%-.64s'",
+"Duplicate entry '%-.64s' for key %d",
+"Incorrect column specifier for column '%-.64s'",
+"%s near '%-.80s' at line %d",
+"Query was empty",
+"Not unique table/alias: '%-.64s'",
+"Invalid default value for '%-.64s'",
+"Multiple primary key defined",
+"Too many keys specified. Max %d keys allowed",
+"Too many key parts specified. Max %d parts allowed",
+"Specified key was too long. Max key length is %d",
+"Key column '%-.64s' doesn't exist in table",
+"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: Normal shutdown\n",
+"%s: Got signal %d. Aborting!\n",
+"%s: Shutdown Complete\n",
+"%s: Forcing close of thread %ld user: '%-.32s'\n",
+"Can't create IP socket",
+"Table '%-.64s' has no index like the one used in CREATE INDEX. Recreate the table",
+"Field separator argument is not what is expected. Check the manual","
+"You can't use fixed rowlength with BLOBs. Please use 'fields terminated by'.",
+"The file '%-.64s' must be in the database directory or be readable by all",
+"File '%-.80s' already exists",
+"Records: %ld Deleted: %ld Skipped: %ld Warnings: %ld",
+"Records: %ld Duplicates: %ld",
+"Incorrect sub part key. The used key part isn't a string or the used length is longer than the key part",
+"You can't delete all columns with ALTER TABLE. Use DROP TABLE instead",
+"Can't DROP '%-.64s'. Check that column/key exists",
+"Records: %ld Duplicates: %ld Warnings: %ld",
+"INSERT TABLE '%-.64s' isn't allowed in FROM table list",
+"Unknown thread id: %lu",
+"You are not owner of thread %lu",
+"No tables used",
+"Too many strings for column %-.64s and SET",
+"Can't generate a unique log-filename %-.64s.(1-999)\n",
+"Table '%-.64s' was locked with a READ lock and can't be updated",
+"Table '%-.64s' was not locked with LOCK TABLES",
+"BLOB column '%-.64s' can't have a default value",
+"Incorrect database name '%-.100s'",
+"Incorrect table name '%-.100s'",
+"The SELECT would examine too many records and probably take a very long time. Check your WHERE and use SET OPTION SQL_BIG_SELECTS=1 if the SELECT is ok",
+"Unknown error",
+"Unknown procedure '%-.64s'",
+"Incorrect parameter count to procedure '%-.64s'",
+"Incorrect parameters to procedure '%-.64s'",
+"Unknown table '%-.64s' in %-.32s",
+"Column '%-.64s' specified twice",
+"Invalid use of group function",
+"Table '%-.64s' uses an extension that doesn't exist in this MySQL version",
+"A table must have at least 1 column",
+"The table '%-.64s' is full",
+"Unknown character set: '%-.64s'",
+"Too many tables. MySQL can only use %d tables in a join",
+"Too many columns",
+"Too big row size. The maximum row size, not counting BLOBs, is %d. You have to change some fields to BLOBs",
+"Thread stack overrun: Used: %ld of a %ld stack. Use 'mysqld -O thread_stack=#' to specify a bigger stack if needed",
+"Cross dependency found in OUTER JOIN. Examine your ON conditions",
+"Column '%-.64s' is used with UNIQUE or INDEX but is not defined as NOT NULL",
+"Can't load function '%-.64s'",
+"Can't initialize function '%-.64s'; %-.80s",
+"No paths allowed for shared library",
+"Function '%-.64s' already exist",
+"Can't open shared library '%-.64s' (errno: %d %-.64s)",
+"Can't find function '%-.64s' in library'",
+"Function '%-.64s' is not defined",
+"Host '%-.64s' is blocked because of many connection errors. Unblock with 'mysqladmin flush-hosts'",
+"Host '%-.64s' is not allowed to connect to this MySQL server",
+"You are using MySQL as an anonymous users and anonymous users are not allowed to change passwords",
+"You must have privileges to update tables in the mysql database to be able to change passwords for others",
+"Can't find any matching row in the user table",
+"Rows matched: %ld Changed: %ld Warnings: %ld",
+"Can't create a new thread (errno %d). If you are not out of available memory, you can consult the manual for a possible OS-dependent bug",
+"Column count doesn't match value count at row %ld",
+"Can't reopen table: '%-.64s'",
+"Invalid use of NULL value",
+"Got error '%-.64s' from regexp",
+"Mixing of GROUP columns (MIN(),MAX(),COUNT()...) with no GROUP columns is illegal if there is no GROUP BY clause",
+"There is no such grant defined for user '%-.32s' on host '%-.64s'",
+"%-.16s command denied to user: '%-.32s@%-.64s' for table '%-.64s'",
+"%-.16s command denied to user: '%-.32s@%-.64s' for column '%-.64s' in table '%-.64s'",
+"Illegal GRANT/REVOKE command. Please consult the manual which privileges can be used.",
+"The host or user argument to GRANT is too long",
+"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",
+"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)",
+"Got a packet bigger than 'max_allowed_packet'",
+"Got a read error from the connection pipe",
+"Got an error from fcntl()",
+"Got packets out of order",
+"Couldn't uncompress communication packet",
+"Got an error reading communication packets",
+"Got timeout reading communication packets",
+"Got an error writing communication packets",
+"Got timeout writing communication packets",
+"Result string is longer than max_allowed_packet",
+"The used table type doesn't support BLOB/TEXT columns",
+"The used table type doesn't support AUTO_INCREMENT columns",
+"INSERT DELAYED can't be used with table '%-.64s', because it is locked with LOCK TABLES",
+"Incorrect column name '%-.100s'",
+"The used table handler can't index column '%-.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",
+"Result consisted of more than one row",
+"This table type requires a primary key",
+"This version of MySQL is not compiled with RAID support",
+"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",
+"You are not allowed to execute this command in a transaction",
+"Got error %d during COMMIT",
+"Got error %d during ROLLBACK",
+"Got error %d during FLUSH_LOGS",
+"Got error %d during CHECKPOINT",
+"Aborted connection %ld to db: '%-.64s' user: '%-.32s' host: `%-.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",
diff --git a/sql/share/estonia/errmsg.sys b/sql/share/estonia/errmsg.sys
new file mode 100644
index 00000000000..bd7927bdb5e
--- /dev/null
+++ b/sql/share/estonia/errmsg.sys
Binary files differ
diff --git a/sql/share/estonia/errmsg.txt b/sql/share/estonia/errmsg.txt
new file mode 100644
index 00000000000..a1614fbd3e8
--- /dev/null
+++ b/sql/share/estonia/errmsg.txt
@@ -0,0 +1,199 @@
+/* Copyright Abandoned 1997 TCX DataKonsult AB & Monty Program KB & Detron HB
+ This file is public domain and comes with NO WARRANTY of any kind
+
+ 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)",
+"'%-.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",
+"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)",
+"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.",
+"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",
+"Tundmatu käsk",
+"Tulp '%-.64s' ei saa olla null",
+"Tundmatu andmebaas '%-.64s'",
+"Tabel '%-.64s' on juba olemas",
+"Tundmatu tabel '%-.64s'",
+"Tulp: '%-.64s' in %-.64s on väär",
+"Serveri seiskamine käib",
+"Tundmatu tulp '%-.64s' in '%-.64s'",
+"'%-.64s' puudub GROUP BY-s",
+"Ei saa grupeerida '%-.64s' järgi",
+"Lauses on korraga nii tulbad kui summad",
+"Tuplade arv tabelis erineb antud 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",
+"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",
+"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",
+"%s: ootab ühendusi\n",
+"%s: MySQL lõpetas\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",
+"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",
+"Tundmatu viga",
+"Tundmatu protseduur '%-.64s'",
+"Väär parameetrite hulk protseduurile '%-.64s'",
+"Valed parameetrid protseduurile '%-.64s'",
+"Tundmatu tabel '%-.64s' %s-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",
+"Tabel '%-.64s' on täis",
+"Tundmatu kooditabel: '%-.64s'",
+"Liiga palju tabeleid. MySQL oskab kasutada kuni %d tabelit JOINi puhul",
+"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",
+"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",
+"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',
+"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",
+"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",
+"Result consisted of more than one row",
+"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'",
+"Can't open table",
+"The handler for the table doesn't support check/repair",
+"You are not allowed to execute this command in a transaction",
+"Got error %d during COMMIT",
+"Got error %d during ROLLBACK",
+"Got error %d during FLUSH_LOGS",
+"Got error %d during CHECKPOINT",
+"Aborted connection %ld to db: '%-.64s' user: '%-.32s' host: `%-.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",
diff --git a/sql/share/french/errmsg.sys b/sql/share/french/errmsg.sys
new file mode 100644
index 00000000000..6947930f875
--- /dev/null
+++ b/sql/share/french/errmsg.sys
Binary files differ
diff --git a/sql/share/french/errmsg.txt b/sql/share/french/errmsg.txt
new file mode 100644
index 00000000000..34c488b7129
--- /dev/null
+++ b/sql/share/french/errmsg.txt
@@ -0,0 +1,195 @@
+/* Copyright Abandoned 1997 TCX DataKonsult AB & Monty Program KB & Detron HB
+ This file is public domain and comes with NO WARRANTY of any kind */
+
+"hashchk",
+"isamchk",
+"NON",
+"OUI",
+"Ne peut créer le fichier '%-.64s' (Errcode: %d)",
+"Ne peut créer la table '%-.64s' (Errcode: %d)",
+"Ne peut créer la base '%-.64s'. Erreur %d",
+"Ne peut créer la base '%-.64s'. Elle existe déjà",
+"Ne peut effacer la base '%-.64s'. Elle n'existe pas",
+"Ne peut effacer la base '%-.64s' (erreur %d)",
+"Erreur en effaçant la base (rmdir '%-.64s', erreur %d)",
+"Erreur en effaçant '%-.64s' (Errcode: %d)",
+"Ne peut lire un enregistrement de la table 'system'",
+"Ne peut obtenir le status de '%-.64s' (Errcode: %d)",
+"Ne peut obtenir le répertoire de travail (Errcode: %d)",
+"Ne peut verrouiller le fichier (Errcode: %d)",
+"Ne peut ouvrir le fichier: '%-.64s'. (Errcode: %d)",
+"Ne peut trouver le fichier: '%-.64s' (Errcode: %d)",
+"Ne peut lire le répertoire de '%-.64s' (Errcode: %d)",
+"Ne peut changer le répertoire pour '%-.64s' (Errcode: %d)",
+"Enregistrement modifié depuis sa dernière lecture dans la table '%-.64s'",
+"Disque plein (%s). J'attend que quelqu'un libère de l'espace...",
+"Ecriture impossible, doublon dans une clé de la table '%-.64s'",
+"Erreur a la fermeture de '%-.64s' (Errcode: %d)",
+"Erreur en lecture du fichier '%-.64s' (Errcode: %d)",
+"Erreur en renommant '%-.64s' en '%-.64s' (Errcode: %d)",
+"Erreur d'écriture du fichier '%-.64s' (Errcode: %d)",
+"'%-.64s' est verrouillé contre les modifications",
+"Tri alphabétique abandonné",
+"La vue (View) '%-.64s' n'existe pas pour '%-.64s'",
+"Reçu l'erreur %d du handler de la table",
+"Le handler de la table '%-.64s' n'a pas cette option",
+"Ne peut trouver l'enregistrement dans '%-.64s'",
+"Information erronnée dans le fichier: '%-.64s'",
+"Index corrompu dans la table: '%-.64s'. Essayez de le réparer",
+"Vieux fichier d'index pour la table '%-.64s'; Réparez le!",
+"'%-.64s' est en lecture seulement",
+"Manque de mémoire. Redémarrez le démon et ré-essayez (%d octets nécessaires)",
+"Manque de mémoire pour le tri. Augmentez-la.",
+"Fin de fichier inattendue en lisant '%-.64s' (Errcode: %d)",
+"Trop de connections",
+"Manque de 'threads'/mémoire",
+"Ne peut obtenir de hostname pour votre adresse",
+"Mauvais 'handshake'",
+"Accès refusé pour l'utilisateur: '%-.32s@%-.64s'. Base '%-.64s'",
+"Accès refusé pour l'utilisateur: '%-.32s@%-.64s' (mot de passe: %s)",
+"Aucune base n'a été sélectionnée",
+"Commande inconnue",
+"Le champ '%-.64s' ne peut être vide (null)",
+"Base '%-.64s' inconnue",
+"La table '%-.64s' existe déjà",
+"Table '%-.64s' inconnue",
+"Champ: '%-.64s' dans %s est ambigu",
+"Arrêt du serveur en cours",
+"Champ '%-.64s' inconnu dans %s",
+"'%-.64s' n'est pas dans 'group by'",
+"Ne peut regrouper '%-.64s'",
+"Vous demandez la fonction sum() et des champs dans la même commande",
+"Column count doesn't match value count",
+"Le nom de l'identificateur '%-.64s' est trop long",
+"Nom du champ '%-.64s' déjà utilisé",
+"Nom de clef '%-.64s' déjà utilisé",
+"Duplicata du champ '%-.64s' pour la clef %d",
+"Mauvais paramètre de champ pour le champ '%-.64s'",
+"%s près de '%-.64s' à la ligne %d",
+"Query est vide",
+"Table/alias: '%-.64s' non unique",
+"Valeur par défaut invalide pour '%-.64s'",
+"Plusieurs clefs primaires définies",
+"Trop de clefs sont définies. Maximum de %d clefs alloué",
+"Trop de parties specifiées dans la clef. Maximum de %d parties",
+"La clé est trop longue. Longueur maximale: %d",
+"La clé '%-.64s' n'existe pas dans la table",
+"Champ BLOB '%-.64s' ne peut être utilisé dans une clé",
+"Champ '%-.64s' trop long (max = %d). Utilisez un BLOB",
+"Un seul champ automatique est permis et il doit être indexé",
+"%s: Prêt pour des connections\n",
+"%s: Arrêt normal du serveur\n",
+"%s: Reçu le signal %d. Abandonne!\n",
+"%s: Arrêt du serveur terminé\n",
+"%s: Arrêt forcé de la tâche (thread) %ld utilisateur: '%-.64s'\n",
+"Ne peut créer la connection IP (socket)",
+"La table '%-.64s' n'a pas d'index comme celle utilisée dans CREATE INDEX. Recréez la table",
+"Séparateur de champs inconnu. Vérifiez dans le manuel",
+"Vous ne pouvez utiliser des lignes de longueur fixe avec des BLOBs. Utiliser 'fields terminated by'.",
+"Le fichier '%-.64s' doit être dans le répertoire de la base et lisible par tous",
+"Le fichier '%-.64s' existe déjà",
+"Enregistrements: %ld Effacés: %ld Non traités: %ld Avertissements: %ld",
+"Enregistrements: %ld Doublons: %ld",
+"Mauvaise sous-clef. Ce n'est pas un 'string' ou la longueur dépasse celle définie dans la clef",
+"Vous ne pouvez effacer tous les champs avec ALTER TABLE. Utilisez DROP TABLE",
+"Ne peut effacer (DROP) '%-.64s'. Vérifiez s'il existe",
+"Enregistrements: %ld Doublons: %ld Avertissements: %ld",
+"INSERT TABLE '%-.64s' n'est pas permis dans FROM liste des tables",
+"Numéro de tâche inconnu: %lu",
+"Vous n'êtes pas propriétaire de la tâche no: %lu",
+"Aucune table utilisée",
+"Trop de chaînes dans la colonne %s avec SET",
+"Ne peut générer un unique nom de journal %s.(1-999)\n",
+"Table '%-.64s' verrouillée lecture (READ): modification impossible",
+"Table '%-.64s' non verrouillée: utilisez LOCK TABLES",
+"BLOB '%-.64s' ne peut avoir de valeur par défaut",
+"Nom de base de donnée illégal: '%-.64s'",
+"Nom de table illégal: '%-.64s'",
+"SELECT va devoir examiner beaucoup d'enregistrements ce qui va prendre du temps. Vérifiez la clause WHERE et utilisez SET OPTION SQL_BIG_SELECTS=1 si SELECT se passe bien",
+"Erreur inconnue",
+"Procédure %s inconnue",
+"Mauvais nombre de paramètres pour la procedure %s",
+"Paramètre erroné pour la procedure %s",
+"Table inconnue '%-.64s' dans %s",
+"Champ '%-.64s' spécifié deux fois",
+"Utilisation invalide de la clause GROUP",
+"Table '%-.64s' : utilise une extension invalide pour cette version de MySQL",
+"Une table doit comporter au moins une colonne",
+"La table '%-.64s' est pleine",
+"Jeu de caractères inconnu: '%-.64s'",
+"Trop de tables. MySQL ne peut utiliser que %d tables dans un JOIN",
+"Trop de champs",
+"Ligne trop grande. Le taille maximale d'une ligne, sauf les BLOBs, est %d. Changez le type de quelques colonnes en BLOB",
+"Débordement de la pile des tâches (Thread stack). Utilisées: %ld pour une pile de %ld. Essayez 'mysqld -O thread_stack=#' pour indiquer une plus grande valeur",
+"Dépendance croisée dans une clause OUTER JOIN. Vérifiez la condition ON",
+"Column '%-.32s' is used with UNIQUE or INDEX but is not defined as NOT NULL",
+"Can't load function '%-.64s'",
+"Can't initialize function '%-.64s'; %-.80s",
+"No paths allowed for shared library",
+"Function '%-.64s' already exist",
+"Can't open shared library '%-.64s' (errno: %d %s)",
+"Can't find function '%-.64s' in library'",
+"Function '%-.64s' is not defined",
+"Host '%-.64s' is blocked because of many connection errors. Unblock with 'mysqladmin flush-hosts'",
+"Host '%-.64s' is not allowed to connect to this MySQL server",
+"You are using MySQL as an anonymous users and anonymous users are not allowed to change passwords",
+"You must have privileges to update tables in the mysql database to be able to change passwords for others",
+"Can't find any matching row in the user table",
+"Rows matched: %ld Changed: %ld Warnings: %ld",
+"Can't create a new thread (errno %d). If you are not out of available memory you can consult the manual for any possible OS dependent bug",
+"Column count doesn't match value count at row %ld",
+"Can't reopen table: '%-.64s',
+"Invalid use of NULL value",
+"Got error '%-.64s' from regexp",
+"Mixing of GROUP columns (MIN(),MAX(),COUNT()...) with no GROUP columns is illegal if there is no GROUP BY clause",
+"There is no such grant defined for user '%-.32s' on host '%-.64s'",
+"%-.16s command denied to user: '%-.32s@%-.64s' for table '%-.64s'",
+"%-.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",
+"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",
+"Delayed insert thread couldn't get requested lock for table %-.64s",
+"Too many delayed threads in use",
+"Aborted connection %ld to db: '%-.64s' user: '%-.64s' (%s)",
+"Got a packet bigger than 'max_allowed_packet'",
+"Got a read error from the connection pipe",
+"Got an error from fcntl()",
+"Got packets out of order",
+"Couldn't uncompress communication packet",
+"Got an error reading communication packets"
+"Got timeout reading communication packets",
+"Got an error writing communication packets",
+"Got timeout writing communication packets",
+"Result string is longer than max_allowed_packet",
+"The used table type doesn't support BLOB/TEXT columns",
+"The used table type doesn't support AUTO_INCREMENT columns",
+"INSERT DELAYED can't be used with table '%-.64s', because it is locked with LOCK TABLES",
+"Incorrect column name '%-.100s'",
+"The used table handler can't index column '%-.64s'",
+"All tables in the MERGE table are not defined identically",
+"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",
+"Result consisted of more than one row",
+"This table type requires a primary key",
+"This version of MySQL is not compiled with RAID support",
+"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",
+"You are not allowed to execute this command in a transaction",
+"Got error %d during COMMIT",
+"Got error %d during ROLLBACK",
+"Got error %d during FLUSH_LOGS",
+"Got error %d during CHECKPOINT",
+"Aborted connection %ld to db: '%-.64s' user: '%-.32s' host: `%-.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",
diff --git a/sql/share/german/errmsg.sys b/sql/share/german/errmsg.sys
new file mode 100644
index 00000000000..94c99a9262d
--- /dev/null
+++ b/sql/share/german/errmsg.sys
Binary files differ
diff --git a/sql/share/german/errmsg.txt b/sql/share/german/errmsg.txt
new file mode 100644
index 00000000000..adc69b823b7
--- /dev/null
+++ b/sql/share/german/errmsg.txt
@@ -0,0 +1,198 @@
+/* Copyright Abandoned 1997 TCX DataKonsult AB & Monty Program KB & Detron HB
+ This file is public domain and comes with NO WARRANTY of any kind
+
+ Dirk Munzinger (dmun@4t2.com)
+ Version: 17.03.1999 */
+
+"hashchk",
+"isamchk",
+"Nein",
+"Ja",
+"Kann Datei '%-.64s' nicht erzeugen. (Fehler: %d)",
+"Kann Tabelle '%-.64s' nicht erzeugen. (Fehler: %d)",
+"Kann Datenbank '%-.64s' nicht erzeugen. (Fehler: %d)",
+"Kann Datenbank '%-.64s' nicht erzeugen. Datenbank '%-.64s' existiert bereits.",
+"Kann Datenbank '%-.64s' nicht löschen. Keine Datenbank '%-.64s' vorhanden.",
+"Fehler beim Löschen der Datenbank. ('%-.64s' kann nicht gelöscht werden, Fehler %d)",
+"Fehler beim Löschen der Datenbank. (Verzeichnis '%-.64s' kann nicht gelöscht werden, Fehler %d)",
+"Fehler beim Löschen von '%-.64s'. (Fehler: %d)",
+"Datensatz in der Systemtabelle nicht lesbar.",
+"Kann Status von '%-.64s' nicht erhalten. (Fehler: %d)",
+"Kann Arbeitsverzeichnis nicht erhalten. (Fehler: %d)",
+"File nicht sperrbar. (Fehler: %d)",
+"Kann Datei '%-.64s' nicht öffnen. (Fehler: %d)",
+"Kann Datei '%-.64s' nicht finden. (Fehler: %d)",
+"Verzeichnis von '%-.64s' nicht lesbar. (Fehler: %d)",
+"Verzeichnis kann nicht nach '%-.64s' gewechselt werden. (Fehler: %d)",
+"Datensatz hat sich seit dem letzten Zugriff auf Tabelle '%-.64s' geändert.",
+"Festplatte voll (%-.64s). Warte bis jemand Platz schafft ...",
+"Kann nicht speichern, doppelter Schlüssel in Tabelle '%-.64s'.",
+"Fehler beim Schließen von '%-.64s'. (Fehler: %d)",
+"Fehler beim Lesen der Datei '%-.64s'. (Fehler: %d)",
+"Fehler beim Umbennenen von '%-.64s' nach '%-.64s'. (Fehler: %d)",
+"Fehler beim Speichern der Datei '%-.64s'. (Fehler: %d)",
+"'%-.64s' ist für Veränderungen gesperrt.",
+"Sortieren abgebrochen.",
+"View '%-.64s' existiert für '%-.64s' nicht.",
+"Fehler %d. (table handler)",
+"Diese Option gibt es nicht. (table handler)",
+"Kann Datensatz nicht finden.",
+"Falsche Information in Datei: '%-.64s'",
+"Falsche Schlüssel-Datei für Tabelle '%-.64s'. Versuche zu reparieren!",
+"Alte Schlüssel-Datei für Tabelle '%-.64s'. Repariere!
+"'%-.64s' ist nur lesbar.",
+"Kein Speicher (benötigt %d bytes). Server neu starten.",
+"Kein Speicher zum Sortieren. Server Sortier-Buffer erhöhen.",
+"Unerwartetes EOF beim Lesen der Datei '%-.64s'. (Fehler: %d)",
+"Zu viele Verbindungen.",
+"Zuwenig Speicher.",
+"Kann Hostname für diese Adresse nicht erhalten.",
+"Schlechter handshake.",
+"Keine Zugriffsberechtigung für Benutzer: '%-.32s@%-.64s' für Datenbank '%-.64s'.",
+"Keine Zugriffsberechtigung für Benutzer: '%-.32s@%-.64s'. (Verwendetes Passwort: %-.64s)",
+"Keine Datenbank ausgewählt.",
+"Unbekannter Befehl.",
+"Feld '%-.64s' kann nicht NULL sein.",
+"Unbekannte Datenbank '%-.64s'.",
+"Tabelle '%-.64s' bereits vorhanden.",
+"Unbekannte Tabelle '%-.64s'.",
+"Spalte: '%-.64s' in %-.64s ist mißverständlich.",
+"Der Server wird herunter gefahren...",
+"Unbekanntes Tabellenfeld '%-.64s' in %-.64s.",
+"'%-.64s' ist nicht in GROUP BY.",
+"Gruppierung nicht möglich bei '%-.64s'.",
+"Im Statement wurden sowohl sum-Funktionen als auch Spalten verwendet. Nicht möglich.",
+"Spaltenzähler entspricht nicht dem Wertzähler.",
+"Name des Identifizierers '%-.64s' ist zu lang.",
+"Doppelter Spaltenname vorhanden: '%-.64s'",
+"Doppelter Name für Schlüssel (Key) vorhanden: '%-.64s'",
+"Doppelter Eintrag '%-.64s' für Schlüssel %d.",
+"Falsche Spalten-Spezifizierung für Spalte '%-.64s'.",
+"%-.64s bei '%-.64s' in Zeile %d.",
+"Leere Abfrage.",
+"Keine eindeutige(n) Tabelle/Alias: '%-.64s'",
+"Fehlerhafter Vorgabewert (Default-Wert): '%-.64s'",
+"Mehrfacher Primärschlüssel (Primary Key) definiert.",
+"Zuviele Schlüssel definiert. Maximal %d Schlüssel erlaubt.",
+"Zuviele Teilschlüssel definiert. Maximal sind %d Teilschlüssel erlaubt.",
+"Schlüssel ist zu lang. Die maximale Schlüssellänge beträgt %d.",
+"In der Tabelle gibt es keine Schlüsselspalte '%-.64s'.",
+"BLOB-Feld '%-.64s' kann nicht als Schlüssel verwendet werden.",
+"Feldlänge für Feld '%-.64s' zu groß (max = %d). BLOB-Feld verwenden!",
+"Nur ein Auto-Feld möglich, welches als Schlüssel definiert werden muß.",
+"%-.64s: Warten auf Verbindungen.\n",
+"%-.64s: Normal beendet.\n",
+"%-.64s: Signal %d erhalten. Abbruch!\n",
+"%-.64s: Shutdown ausgeführt.\n",
+"%-.64s: Beendigung des Thread %ld veranlaßt. Benutzer: '%-.64s'\n",
+"Kann IP-Socket nicht erstellen.",
+"Tabelle '%-.64s' hat keinen solchen Index wie in CREATE INDEX verwendet. Index neu anlegen.",
+"Feld-Separator Argument ist nicht in der Form wie erwartet. Bitte im Manual nachlesen.",
+"Eine feste Reihenlänge kann für BLOBs nicht verwendet werden. Verwende 'fields terminated by' stattdessen.",
+"Feld '%-.64s' muß im Datenbank-Directory vorhanden und lesbar für alle sein.",
+"File '%-.64s' bereits vorhanden.",
+"Datensätze: %ld Gelöscht: %ld Ausgelassen: %ld Warnungen: %ld",
+"Datensätze: %ld Duplikate: %ld",
+"Falscher Subteilschlüssel. Der verwendete Schlüsselteil ist entweder kein String oder die verwendete Länge ist länger als der Teilschlüssel.",
+"Mit ALTER TABLE können nicht alle Felder auf einmal gelöscht werden. Verwende DROP TABLE stattdessen.",
+"Kann '%-.64s' nicht löschen (DROP). Existiert das Feld/der Schlüssel?",
+"Datensätze: %ld Duplikate: %ld Warnungen: %ld",
+"INSERT TABLE '%-.64s' nicht erlaubt im FROM Abschnitt.",
+"Unbekannte Thread-ID: %lu",
+"Nicht Besitzer des Threads %lu.",
+"Keine Tabellen in Verwendung.",
+"Zuviele Strings für Spalte %-.64s und SET.",
+"Kann keinen eindeutigen Log-Filenamen erstellen %-.64s.(1-999)\n",
+"Tabelle '%-.64s' mit Lese-Sperre versehen und kann nicht upgedated werden.",
+"Tabelle '%-.64s' wurde nicht mittels LOCK TABLES gesperrt.",
+"BLOB-Feld '%-.64s' kann keinen Vorgabewert (Default-Value) besitzen.",
+"Unerlaubter Datenbankname '%-.64s'.",
+"Unerlaubter Tabellenname '%-.64s'.",
+"Die Ausführung des SELECT würde zu viele Datensätze untersuchen und wahrscheinlich sehr lange daueren. Bitte WHERE überprüfen und SET OPTION SQL_BIG_SELECTS=1 verwenden, sofern SELECT ok ist.",
+"Unbekannter Fehler.",
+"Unbekannte Procedure %-.64s.",
+"Falsche Parameterzahl für Procedure %-.64s.",
+"Falsche Parameter in Procedure %-.64s.",
+"Unbekannte Tabelle '%-.64s' in %-.64s.",
+"Feld '%-.64s' wurde zweimal spezifiziert.",
+"Falsche Verwendung der GROUP-Funktion.",
+"Tabelle '%-.64s' verwendet eine Extension, die in dieser MySQL Version nicht verfügbar ist.",
+"Eine Tabelle muß mindestens eine Spalte besitzen.",
+"Tabelle '%-.64s' voll.",
+"Unbekannter Zeichensatz: '%-.64s'.",
+"Zuviele Tabellen. MySQL kann maximal %d Tabellen in einem Join verwenden.",
+"Zuviele Felder.",
+"Zuviele Spalten. Maximal sind %d Spalten erlaubt (Ohne BLOBs). Einige Felder sollten in BLOBs umgewandelt werden.",
+"Thread Stack Überlauf : Verwendet: %ld von %ld Stack. Verwende 'mysqld -O thread_stack=#' um ggf. einen größeren Stack anzulegen.",
+"OUTER JOIN enthält fehlerhafte Abhängigkeiten. Prüfen Sie Ihre ON Bedingungen.",
+"Spalte '%-.64s' wurde mit UNIQUE oder INDEX benutzt ohne als NOT NULL definiert zu sein.",
+"Kann Funktion '%-.64s' nicht laden.",
+"Kann Funktion '%-.64s' nicht initialisieren; %-.80s.",
+"Keine Pfade gestattet für Shared Library.",
+"Funktion '%-.64s' existiert schon.",
+"Kann Shared Library '%-.64s' nicht öffnen. (Fehler: %d %-.64s)",
+"Kann Funktion '%-.64s' in der Library nicht finden.",
+"Funktion '%-.64s' ist nicht definiert.",
+"Host blockiert wegen zu vieler Verbindungsfehler. Aufheben der Blockierung mit 'mysqladmin flush-hosts'.",
+"Host hat keine Berechtigung, eine Verbindung zu diesem MySQL Server herzustellen.",
+"Sie benutzen MySQL als anonymer User; diese User dürfen keine Passwörter ändern.",
+"Sie müssen autorisiert sein zum UPDATE von Tabellen in der mysql Datenbank, um für andere Benutzer Passwörter ändern zu können.",
+"Kann keinen passenden Datensatz in der User-Tabelle finden.",
+"Datensätze gefunden: %ld Geändert: %ld Warnungen: %ld",
+"Kann keinen neuen Thread erzeugen (errno %d). Sollte nicht die Speichergrenze erreicht sein, bitte im Manual nach vorhanden OS-Abhängigen Fehlern nachschauen.",
+"Spaltenzahl stimmt nicht mit der Anzahl der Werte überein in Reihe%ld",
+"Kann Tabelle'%-.64s' nicht wieder öffnen",
+"Unerlaubte Verwendung eines NULL-Wertes",
+"Fehler '%-.64s' von regexp",
+"Das Vermischen von GROUP Spalten (MIN(),MAX(),COUNT()...) mit Nicht-GROUP Spalten ist nicht erlaubt, sofern keine GROUP BY Klausel vorhanden ist.",
+"Keine solche Berechtigung für User '%-.32s' auf Host '%-.64s'",
+"%-.16s Kommando abgelehnt für User: '%-.32s@%-.64s' für Tabelle '%-.64s'",
+"%-.16s Kommando abgelehnt für User: '%-.32s@%-.64s' in Spalte '%-.64s' in Tabelle '%-.64s'",
+"Unzulässiges GRANT/REVOKE Kommando. Weiteres zum Thema Berechtigungen im Manual.",
+"Das Host oder User Argument für GRANT ist zu lang",
+"Tabelle '%-.64s.%-.64s' existiert nicht",
+"Keine solche Berechtigung für User '%-.32s' auf Host '%-.64s' an Tabelle '%-.64s'",
+"Das used Kommando ist mit dieser MySQL Version nicht erlaubt",
+"Fehler in der Syntax",
+"Verzögerter Einfüge-Thread konnte den angeforderten Lock für Tabelle %-.64s nicht bekommen",
+"Zu viele Delayed Threads in Verwendung",
+"Abbruch der Verbindung %ld zur Datenbank: '%-.64s' User: '%-.64s' (%-.64s)",
+"Empfangenes Paket ist größer als 'max_allowed_packet'",
+"Lese-Fehler bei einer Kommunikations-Pipe",
+"Fehler von fcntl()",
+"Empfangenes Paket ist nicht in Reihenfolge",
+"Communikation-Packet läßt sich nicht entpacken",
+"Fehler beim Lesen eines Communication-Packets"
+"Timeout beim Lesen eines Communication-Packets",
+"Fehler beim Schreiben eines Communication-Packets",
+"Timeout beim Schreiben eines Communication-Packets",
+"Result string is longer than max_allowed_packet",
+"The used table type doesn't support BLOB/TEXT columns",
+"The used table type doesn't support AUTO_INCREMENT columns",
+"INSERT DELAYED can't be used with table '%-.64s', because it is locked with LOCK TABLES",
+"Incorrect column name '%-.100s'",
+"The used table handler can't index column '%-.64s'",
+"All tables in the MERGE table are not defined identically",
+"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",
+"Result consisted of more than one row",
+"This table type requires a primary key",
+"This version of MySQL is not compiled with RAID support",
+"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",
+"You are not allowed to execute this command in a transaction",
+"Got error %d during COMMIT",
+"Got error %d during ROLLBACK",
+"Got error %d during FLUSH_LOGS",
+"Got error %d during CHECKPOINT",
+"Aborted connection %ld to db: '%-.64s' user: '%-.32s' host: `%-.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",
diff --git a/sql/share/greek/errmsg.sys b/sql/share/greek/errmsg.sys
new file mode 100644
index 00000000000..8fc053c381d
--- /dev/null
+++ b/sql/share/greek/errmsg.sys
Binary files differ
diff --git a/sql/share/greek/errmsg.txt b/sql/share/greek/errmsg.txt
new file mode 100644
index 00000000000..b3a17af8ec0
--- /dev/null
+++ b/sql/share/greek/errmsg.txt
@@ -0,0 +1,195 @@
+/* Copyright Abandoned 1997 TCX DataKonsult AB & Monty Program KB & Detron HB
+ This file is public domain and comes with NO WARRANTY of any kind */
+
+"hashchk",
+"isamchk",
+"Ï×É",
+"ÍÁÉ",
+"Áäýíáôç ç äçìéïõñãßá ôïõ áñ÷åßïõ '%-.64s' (êùäéêüò ëÜèïõò: %d)",
+"Áäýíáôç ç äçìéïõñãßá ôïõ ðßíáêá '%-.64s' (êùäéêüò ëÜèïõò: %d)",
+"Áäýíáôç ç äçìéïõñãßá ôçò âÜóçò äåäïìÝíùí '%-.64s'. (êùäéêüò ëÜèïõò: %d)",
+"Áäýíáôç ç äçìéïõñãßá ôçò âÜóçò äåäïìÝíùí '%-.64s'. Ç âÜóç äåäïìÝíùí õðÜñ÷åé Þäç",
+"Áäýíáôç ç äéáãñáöÞ ôçò âÜóçò äåäïìÝíùí '%-.64s'. Ç âÜóç äåäïìÝíùí äåí õðÜñ÷åé",
+"ÐáñïõóéÜóôçêå ðñüâëçìá êáôÜ ôç äéáãñáöÞ ôçò âÜóçò äåäïìÝíùí (áäýíáôç ç äéáãñáöÞ '%-.64s', êùäéêüò ëÜèïõò: %d)",
+"ÐáñïõóéÜóôçêå ðñüâëçìá êáôÜ ôç äéáãñáöÞ ôçò âÜóçò äåäïìÝíùí (áäýíáôç ç äéáãñáöÞ ôïõ öáêÝëëïõ '%-.64s', êùäéêüò ëÜèïõò: %d)",
+"ÐáñïõóéÜóôçêå ðñüâëçìá êáôÜ ôç äéáãñáöÞ '%-.64s' (êùäéêüò ëÜèïõò: %d)",
+"Áäýíáôç ç áíÜãíùóç åããñáöÞò áðü ðßíáêá ôïõ óõóôÞìáôïò",
+"Áäýíáôç ç ëÞøç ðëçñïöïñéþí ãéá ôçí êáôÜóôáóç ôïõ '%-.64s' (êùäéêüò ëÜèïõò: %d)",
+"Ï öÜêåëëïò åñãáóßáò äåí âñÝèçêå (êùäéêüò ëÜèïõò: %d)",
+"Ôï áñ÷åßï äåí ìðïñåß íá êëåéäùèåß (êùäéêüò ëÜèïõò: %d)",
+"Äåí åßíáé äõíáôü íá áíïé÷ôåß ôï áñ÷åßï: '%-.64s'. (êùäéêüò ëÜèïõò: %d)",
+"Äåí âñÝèçêå ôï áñ÷åßï: '%-.64s' (êùäéêüò ëÜèïõò: %d)",
+"Äåí åßíáé äõíáôü íá äéáâáóôåß ï öÜêåëëïò ôïõ '%-.64s' (êùäéêüò ëÜèïõò: %d)",
+"Áäýíáôç ç áëëáãÞ ôïõ ôñÝ÷ïíôïò êáôáëüãïõ óå '%-.64s' (êùäéêüò ëÜèïõò: %d)",
+"Ç åããñáöÞ Ý÷åé áëëÜîåé áðü ôçí ôåëåõôáßá öïñÜ ðïõ áíáóýñèçêå áðü ôïí ðßíáêá '%-.64s'",
+"Äåí õðÜñ÷åé ÷þñïò óôï äßóêï (%s). Ðáñáêáëþ, ðåñéìÝíåôå íá åëåõèåñùèåß ÷þñïò....",
+"Äåí åßíáé äõíáôÞ ç êáôá÷þñçóç, ç ôéìÞ õðÜñ÷åé Þäç óôïí ðßíáêá '%-.64s'",
+"ÐáñïõóéÜóôçêå ðñüâëçìá êëåßíïíôáò ôï '%-.64s' (êùäéêüò ëÜèïõò: %d)",
+"Ðñüâëçìá êáôÜ ôçí áíÜãíùóç ôïõ áñ÷åßïõ '%-.64s' (êùäéêüò ëÜèïõò: %d)",
+"Ðñüâëçìá êáôÜ ôçí ìåôïíïìáóßá ôïõ áñ÷åßïõ '%-.64s' to '%-.64s' (êùäéêüò ëÜèïõò: %d)",
+"Ðñüâëçìá êáôÜ ôçí áðïèÞêåõóç ôïõ áñ÷åßïõ '%-.64s' (êùäéêüò ëÜèïõò: %d)",
+"'%-.64s' äåí åðéôñÝðïíôáé áëëáãÝò",
+"Ç äéáäéêáóßá ôáîéíüìéóçò áêõñþèçêå",
+"Ôï View '%-.64s' äåí õðÜñ÷åé ãéá '%-.64s'",
+"ÅëÞöèç ìÞíõìá ëÜèïõò %d áðü ôïí ÷åéñéóôÞ ðßíáêá (table handler)",
+"Ï ÷åéñéóôÞò ðßíáêá (table handler) ãéá '%-.64s' äåí äéáèÝôåé áõôÞ ôçí åðéëïãÞ",
+"Áäýíáôç ç áíåýñåóç åããñáöÞò óôï '%-.64s'",
+"ËÜèïò ðëçñïöïñßåò óôï áñ÷åßï: '%-.64s'",
+"ËÜèïò áñ÷åßï ôáîéíüìéóçò (key file) ãéá ôïí ðßíáêá: '%-.64s'. Ðáñáêáëþ, äéïñèþóôå ôï!",
+"Ðáëáéü áñ÷åßï ôáîéíüìéóçò (key file) ãéá ôïí ðßíáêá '%-.64s'; Ðáñáêáëþ, äéïñèþóôå ôï!",
+"'%-.64s' åðéôñÝðåôáé ìüíï ç áíÜãíùóç",
+"Äåí õðÜñ÷åé äéáèÝóéìç ìíÞìç. ÐñïóðáèÞóôå ðÜëé, åðáíåêéíþíôáò ôç äéáäéêáóßá (demon) (÷ñåéÜæïíôáé %d bytes)",
+"Äåí õðÜñ÷åé äéáèÝóéìç ìíÞìç ãéá ôáîéíüìéóç. ÁõîÞóôå ôï sort buffer size ãéá ôç äéáäéêáóßá (demon)",
+"ÊáôÜ ôç äéÜñêåéá ôçò áíÜãíùóçò, âñÝèçêå áðñïóäüêçôá ôï ôÝëïò ôïõ áñ÷åßïõ '%-.64s' (êùäéêüò ëÜèïõò: %d)",
+"ÕðÜñ÷ïõí ðïëëÝò óõíäÝóåéò...",
+"Ðñüâëçìá ìå ôç äéáèÝóéìç ìíÞìç (Out of thread space/memory)",
+"Äåí Ýãéíå ãíùóôü ôï hostname ãéá ôçí address óáò",
+"Ç áíáãíþñéóç (handshake) äåí Ýãéíå óùóôÜ",
+"Äåí åðéôÝñåôáé ç ðñüóâáóç óôï ÷ñÞóôç: '%-.32s@%-.64s' óôç âÜóç äåäïìÝíùí '%-.64s'",
+"Äåí åðéôÝñåôáé ç ðñüóâáóç óôï ÷ñÞóôç: '%-.32s@%-.64s' (÷ñÞóç password: %s)",
+"Äåí åðéëÝ÷èçêå âÜóç äåäïìÝíùí",
+"Áãíùóôç åíôïëÞ",
+"Ôï ðåäßï '%-.64s' äåí ìðïñåß íá åßíáé êåíü (null)",
+"Áãíùóôç âÜóç äåäïìÝíùí '%-.64s'",
+"Ï ðßíáêáò '%-.64s' õðÜñ÷åé Þäç",
+"Áãíùóôïò ðßíáêáò '%-.64s'",
+"Ôï ðåäßï: '%-.64s' óå %-.64s äåí Ý÷åé êáèïñéóôåß",
+"Åíáñîç äéáäéêáóßáò áðïóýíäåóçò ôïõ åîõðçñåôçôÞ (server shutdown)",
+"Áãíùóôï ðåäßï '%-.64s' óå '%-.64s'",
+"×ñçóéìïðïéÞèçêå '%-.64s' ðïõ äåí õðÞñ÷å óôï group by",
+"Áäýíáôç ç ïìáäïðïßçóç (group on) '%-.64s'",
+"Ç äéáôýðùóç ðåñéÝ÷åé sum functions êáé columns óôçí ßäéá äéáôýðùóç",
+"Ôï Column count äåí ôáéñéÜæåé ìå ôï value count",
+"Ôï identifier name '%-.100s' åßíáé ðïëý ìåãÜëï",
+"ÅðáíÜëçøç column name '%-.64s'",
+"ÅðáíÜëçøç key name '%-.64s'",
+"ÄéðëÞ åããñáöÞ '%-.64s' ãéá ôï êëåéäß %d",
+"ÅóöáëìÝíï column specifier ãéá ôï ðåäßï '%-.64s'",
+"%s ðëçóßïí '%-.80s' óôç ãñáììÞ %d",
+"Ôï åñþôçìá (query) ðïõ èÝóáôå Þôáí êåíü",
+"Áäýíáôç ç áíåýñåóç unique table/alias: '%-.64s'",
+"ÅóöáëìÝíç ðñïêáèïñéóìÝíç ôéìÞ (default value) ãéá '%-.64s'",
+"Ðåñéóóüôåñá áðü Ýíá primary key ïñßóôçêáí",
+"ÐÜñá ðïëëÜ key ïñßóèçêáí. Ôï ðïëý %d åðéôñÝðïíôáé",
+"ÐÜñá ðïëëÜ key parts ïñßóèçêáí. Ôï ðïëý %d åðéôñÝðïíôáé",
+"Ôï êëåéäß ðïõ ïñßóèçêå åßíáé ðïëý ìåãÜëï. Ôï ìÝãéóôï ìÞêïò åßíáé %d",
+"Ôï ðåäßï êëåéäß '%-.64s' äåí õðÜñ÷åé óôïí ðßíáêá",
+"Ðåäßï ôýðïõ Blob '%-.64s' äåí ìðïñåß íá ÷ñçóéìïðïéçèåß óôïí ïñéóìü åíüò êëåéäéïý (key specification)",
+"Ðïëý ìåãÜëï ìÞêïò ãéá ôï ðåäßï '%-.64s' (max = %d). Ðáñáêáëþ ÷ñçóéìïðïéåßóôå ôïí ôýðï BLOB",
+"Ìðïñåß íá õðÜñ÷åé ìüíï Ýíá auto field êáé ðñÝðåé íá Ý÷åé ïñéóèåß óáí key",
+"%s: óå áíáìïíÞ óõíäÝóåùí\n",
+"%s: ÖõóéïëïãéêÞ äéáäéêáóßá shutdown\n",
+"%s: ÅëÞöèç ôï ìÞíõìá %d. Ç äéáäéêáóßá åãêáôáëåßðåôáé!\n",
+"%s: Ç äéáäéêáóßá Shutdown ïëïêëçñþèçêå\n",
+"%s: Ôï thread èá êëåßóåé %ld user: '%-.64s'\n",
+"Äåí åßíáé äõíáôÞ ç äçìéïõñãßá IP socket",
+"Ï ðßíáêáò '%-.64s' äåí Ý÷åé åõñåôÞñéï (index) óáí áõôü ðïõ ÷ñçóéìïðïéåßôå óôçí CREATE INDEX. Ðáñáêáëþ, îáíáäçìéïõñãÞóôå ôïí ðßíáêá",
+"Ï äéá÷ùñéóôÞò ðåäßùí äåí åßíáé áõôüò ðïõ áíáìåíüôáí. Ðáñáêáëþ áíáôñÝîôå óôï manual","
+"Äåí ìðïñåßôå íá ÷ñçóéìïðïéÞóåôå fixed rowlength óå BLOBs. Ðáñáêáëþ ÷ñçóéìïðïéåßóôå 'fields terminated by'.",
+"Ôï áñ÷åßï '%-.64s' ðñÝðåé íá õðÜñ÷åé óôï database directory Þ íá ìðïñåß íá äéáâáóôåß áðü üëïõò",
+"Ôï áñ÷åßï '%-.64s' õðÜñ÷åé Þäç",
+"ÅããñáöÝò: %ld ÄéáãñáöÝò: %ld ÐáñåêÜìöèçóáí: %ld ÐñïåéäïðïéÞóåéò: %ld",
+"ÅããñáöÝò: %ld ÅðáíáëÞøåéò: %ld",
+"ÅóöáëìÝíï sub part key. Ôï ÷ñçóéìïðïéïýìåíï key part äåí åßíáé string Þ ôï ìÞêïò ôïõ åßíáé ìåãáëýôåñï",
+"Äåí åßíáé äõíáôÞ ç äéáãñáöÞ üëùí ôùí ðåäßùí ìå ALTER TABLE. Ðáñáêáëþ ÷ñçóéìïðïéåßóôå DROP TABLE",
+"Áäýíáôç ç äéáãñáöÞ (DROP) '%-.64s'. Ðáñáêáëþ åëÝãîôå áí ôï ðåäßï/êëåéäß õðÜñ÷åé",
+"ÅããñáöÝò: %ld ÅðáíáëÞøåéò: %ld ÐñïåéäïðïéÞóåéò: %ld",
+"INSERT TABLE '%-.64s' äåí åðéôñÝðåôáé óôï FROM table list",
+"Áãíùóôï thread id: %lu",
+"Äåí åßóèå owner ôïõ thread %lu",
+"Äåí ÷ñçóéìïðïéÞèçêáí ðßíáêåò",
+"ÐÜñá ðïëëÜ strings ãéá ôï ðåäßï %-.64s êáé SET",
+"Áäýíáôç ç äçìéïõñãßá unique log-filename %-.64s.(1-999)\n",
+"Ï ðßíáêáò '%-.64s' Ý÷åé êëåéäùèåß ìå READ lock êáé äåí åðéôñÝðïíôáé áëëáãÝò",
+"Ï ðßíáêáò '%-.64s' äåí Ý÷åé êëåéäùèåß ìå LOCK TABLES",
+"Ôá Blob ðåäßá '%-.64s' äåí ìðïñïýí íá Ý÷ïõí ðñïêáèïñéóìÝíåò ôéìÝò (default value)",
+"ËÜèïò üíïìá âÜóçò äåäïìÝíùí '%-.100s'",
+"ËÜèïò üíïìá ðßíáêá '%-.100s'",
+"Ôï SELECT èá åîåôÜóåé ìåãÜëï áñéèìü åããñáöþí êáé ðéèáíþò èá êáèõóôåñÞóåé. Ðáñáêáëþ åîåôÜóôå ôéò ðáñáìÝôñïõò ôïõ WHERE êáé ÷ñçóéìïðïéåßóôå SET OPTION SQL_BIG_SELECTS=1 áí ôï SELECT åßíáé óùóôü",
+"ÐñïÝêõøå Üãíùóôï ëÜèïò",
+"Áãíùóôç äéáäéêáóßá '%-.64s'",
+"ËÜèïò áñéèìüò ðáñáìÝôñùí óôç äéáäéêáóßá '%-.64s'",
+"ËÜèïò ðáñÜìåôñïé óôçí äéáäéêáóßá '%-.64s'",
+"Áãíùóôïò ðßíáêáò '%-.64s' óå %s",
+"Ôï ðåäßï '%-.64s' Ý÷åé ïñéóèåß äýï öïñÝò",
+"ÅóöáëìÝíç ÷ñÞóç ôçò group function",
+"Ï ðßíáêò '%-.64s' ÷ñçóéìïðïéåß êÜðïéï extension ðïõ äåí õðÜñ÷åé óôçí Ýêäïóç áõôÞ ôçò MySQL",
+"Åíáò ðßíáêáò ðñÝðåé íá Ý÷åé ôïõëÜ÷éóôïí Ýíá ðåäßï",
+"Ï ðßíáêáò '%-.64s' åßíáé ãåìÜôïò",
+"Áãíùóôï character set: '%-.64s'",
+"Ðïëý ìåãÜëïò áñéèìüò ðéíÜêùí. Ç MySQL ìðïñåß íá ÷ñçóéìïðïéÞóåé %d ðßíáêåò óå äéáäéêáóßá join",
+"Ðïëý ìåãÜëïò áñéèìüò ðåäßùí",
+"Ðïëý ìåãÜëï ìÝãåèïò åããñáöÞò. Ôï ìÝãéóôï ìÝãåèïò åããñáöÞò, ÷ùñßò íá õðïëïãßæïíôáé ôá blobs, åßíáé %d. ÐñÝðåé íá ïñßóåôå êÜðïéá ðåäßá óáí blobs",
+"Stack overrun óôï thread: Used: %ld of a %ld stack. Ðáñáêáëþ ÷ñçóéìïðïéåßóôå 'mysqld -O thread_stack=#' ãéá íá ïñßóåôå Ýíá ìåãáëýôåñï stack áí ÷ñåéÜæåôáé",
+"Cross dependency âñÝèçêå óå OUTER JOIN. Ðáñáêáëþ åîåôÜóôå ôéò óõíèÞêåò ðïõ èÝóáôå óôï ON",
+"Ôï ðåäßï '%-.64s' ÷ñçóéìïðïéåßôáé óáí UNIQUE Þ INDEX áëëÜ äåí Ý÷åé ïñéóèåß óáí NOT NULL",
+"Äåí åßíáé äõíáôÞ ç äéáäéêáóßá load ãéá ôç óõíÜñôçóç '%-.64s'",
+"Äåí åßíáé äõíáôÞ ç Ýíáñîç ôçò óõíÜñôçóçò '%-.64s'; %-.80s",
+"Äåí âñÝèçêáí paths ãéá ôçí shared library",
+"Ç óõíÜñôçóç '%-.64s' õðÜñ÷åé Þäç",
+"Äåí åßíáé äõíáôÞ ç áíÜãíùóç ôçò shared library '%-.64s' (êùäéêüò ëÜèïõò: %d %s)",
+"Äåí åßíáé äõíáôÞ ç áíåýñåóç ôçò óõíÜñôçóçò '%-.64s' óôçí âéâëéïèÞêç'",
+"Ç óõíÜñôçóç '%-.64s' äåí Ý÷åé ïñéóèåß",
+"Ï õðïëïãéóôÞò Ý÷åé áðïêëåéóèåß ëüãù ðïëëáðëþí ëáèþí óýíäåóçò. ÐñïóðáèÞóôå íá äéïñþóåôå ìå 'mysqladmin flush-hosts'",
+"Ï õðïëïãéóôÞò äåí Ý÷åé äéêáßùìá óýíäåóçò ìå ôïí MySQL server",
+"×ñçóéìïðïéåßôå ôçí MySQL óáí anonymous user êáé Ýôóé äåí ìðïñåßôå íá áëëÜîåôå ôá passwords Üëëùí ÷ñçóôþí",
+"ÐñÝðåé íá Ý÷åôå äéêáßùìá äéüñèùóçò ðéíÜêùí (update) óôç âÜóç äåäïìÝíùí mysql ãéá íá ìðïñåßôå íá áëëÜîåôå ôá passwords Üëëùí ÷ñçóôþí",
+"Äåí åßíáé äõíáôÞ ç áíåýñåóç ôçò áíôßóôïé÷çò åããñáöÞò óôïí ðßíáêá ôùí ÷ñçóôþí",
+"Rows matched: %ld Changed: %ld Warnings: %ld",
+"Can't create a new thread (errno %d). If you are not out of available memory, you can consult the manual for a possible OS-dependent bug",
+"Column count doesn't match value count at row %ld",
+"Can't reopen table: '%-.64s'",
+"Invalid use of NULL value",
+"Got error '%-.64s' from regexp",
+"Mixing of GROUP columns (MIN(),MAX(),COUNT()...) with no GROUP columns is illegal if there is no GROUP BY clause",
+"There is no such grant defined for user '%-.32s' on host '%-.64s'",
+"%-.16s command denied to user: '%-.32s@%-.64s' for table '%-.64s'",
+"%-.16s command denied to user: '%-.32s@%-.64s' for column '%-.64s' in table '%-.64s'",
+"Illegal GRANT/REVOKE command. Please consult the manual which privileges can be used.",
+"The host or user argument to GRANT is too long",
+"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",
+"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)",
+"Got a packet bigger than 'max_allowed_packet'",
+"Got a read error from the connection pipe",
+"Got an error from fcntl()",
+"Got packets out of order",
+"Couldn't uncompress communication packet",
+"Got an error reading communication packets"
+"Got timeout reading communication packets",
+"Got an error writing communication packets",
+"Got timeout writing communication packets",
+"Result string is longer than max_allowed_packet",
+"The used table type doesn't support BLOB/TEXT columns",
+"The used table type doesn't support AUTO_INCREMENT columns",
+"INSERT DELAYED can't be used with table '%-.64s', because it is locked with LOCK TABLES",
+"Incorrect column name '%-.100s'",
+"The used table handler can't index column '%-.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",
+"Result consisted of more than one row",
+"This table type requires a primary key",
+"This version of MySQL is not compiled with RAID support",
+"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",
+"You are not allowed to execute this command in a transaction",
+"Got error %d during COMMIT",
+"Got error %d during ROLLBACK",
+"Got error %d during FLUSH_LOGS",
+"Got error %d during CHECKPOINT",
+"Aborted connection %ld to db: '%-.64s' user: '%-.32s' host: `%-.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",
diff --git a/sql/share/hungarian/errmsg.sys b/sql/share/hungarian/errmsg.sys
new file mode 100644
index 00000000000..df039b66ec3
--- /dev/null
+++ b/sql/share/hungarian/errmsg.sys
Binary files differ
diff --git a/sql/share/hungarian/errmsg.txt b/sql/share/hungarian/errmsg.txt
new file mode 100644
index 00000000000..8e9e04b79b7
--- /dev/null
+++ b/sql/share/hungarian/errmsg.txt
@@ -0,0 +1,197 @@
+/* Copyright Abandoned 1997 TCX DataKonsult AB & Monty Program KB & Detron HB
+ Translated by Feher Peter. Forditotta Feher Peter (feherp@mail.matav.hu) 1998
+ Updated May, 2000
+ This file is public domain and comes with NO WARRANTY of any kind */
+
+"hashchk",
+"isamchk",
+"NEM",
+"IGEN",
+"A '%-.64s' file nem hozhato letre (hibakod: %d)",
+"A '%-.64s' tabla nem hozhato letre (hibakod: %d)",
+"Az '%-.64s' adatbazis nem hozhato letre (hibakod: %d)",
+"Az '%-.64s' adatbazis nem hozhato letre Az adatbazis mar letezik",
+"A(z) '%-.64s' adatbazis nem szuntetheto meg. Az adatbazis nem letezik",
+"Adatbazis megszuntetesi hiba ('%-.64s' nem torolheto, hibakod: %d)",
+"Adatbazis megszuntetesi hiba ('%-.64s' nem szuntetheto meg, hibakod: %d)",
+"Torlesi hiba: '%-.64s' (hibakod: %d)",
+"Nem olvashato rekord a rendszertablaban",
+"A(z) '%-.64s' statusza nem allapithato meg (hibakod: %d)",
+"A munkakonyvtar nem allapithato meg (hibakod: %d)",
+"A file nem zarolhato. (hibakod: %d)",
+"A '%-.64s' file nem nyithato meg. (hibakod: %d)",
+"A(z) '%-.64s' file nem talalhato. (hibakod: %d)",
+"A(z) '%-.64s' konyvtar nem olvashato. (hibakod: %d)",
+"Konyvtarvaltas nem lehetseges a(z) '%-.64s'-ba. (hibakod: %d)",
+"A(z) '%-.64s' tablaban talalhato rekord megvaltozott az utolso olvasas ota",
+"A lemez megtelt (%s).",
+"Irasi hiba, duplikalt kulcs a '%-.64s' tablaban.",
+"Hiba a(z) '%-.64s' zarasakor. (hibakod: %d)",
+"Hiba a '%-.64s'file olvasasakor. (hibakod: %d)",
+"Hiba a '%-.64s' file atnevezesekor. (hibakod: %d)",
+"Hiba a '%-.64s' file irasakor. (hibakod: %d)",
+"'%-.64s' a valtoztatas ellen zarolva",
+"Sikertelen rendezes",
+"A(z) '%-.64s' nezet nem letezik a(z) '%-.64s'-hoz",
+"%d hibajelzes a tablakezelotol",
+"A(z) '%-.64s' tablakezelonek nincs ilyen opcioja",
+"Nem talalhato a rekord '%-.64s'-ben",
+"Ervenytelen info a file-ban: '%-.64s'",
+"Ervenytelen kulcsfile a tablahoz: '%-.64s'. Probalja kijavitani!",
+"Regi kulcsfile a '%-.64s'tablahoz; Probalja kijavitani!",
+"'%-.64s' irasvedett",
+"Nincs eleg memoria. Inditsa ujra a demont, es probalja ismet. (%d byte szukseges.)",
+"Nincs eleg memoria a rendezeshez. Novelje a rendezo demon puffermeretet",
+"Varatlan filevege-jel a '%-.64s'olvasasakor. (hibakod: %d)",
+"Tul sok kapcsolat",
+"Elfogyott a thread-memoria",
+"A gepnev nem allapithato meg a cimbol",
+"A kapcsolatfelvetel nem sikerult (Bad handshake)",
+"A(z) '%-.32s@%-.64s' felhasznalo szamara tiltott eleres az '%-.64s' adabazishoz.",
+"A(z) '%-.32s@%-.64s' felhasznalo szamara tiltott eleres. (Hasznalja a jelszot: %s)",
+"Nincs kivalasztott adatbazis",
+"Ervenytelen parancs",
+"A(z) '%-.64s' oszlop erteke nem lehet nulla",
+"Ervenytelen adatbazis: '%-.64s'",
+"A(z) '%-.64s' tabla mar letezik",
+"Ervenytelen tabla: '%-.64s'",
+"A(z) '%-.64s' oszlop %-.64s-ben ketertelmu",
+"A szerver leallitasa folyamatban",
+"A(z) '%-.64s' oszlop ervenytelen '%-.64s'-ben",
+"Used '%-.64s' with wasn't in group by",
+"A group nem hasznalhato: '%-.64s'",
+"Statement has sum functions and columns in same statement",
+"Az oszlopban levo ertek nem egyezik meg a szamitott ertekkel",
+"A(z) '%-.100s' azonositonev tul hosszu.",
+"Duplikalt oszlopazonosito: '%-.64s'",
+"Duplikalt kulcsazonosito: '%-.64s'",
+"Duplikalt bejegyzes '%-.64s' a %d kulcs szerint.",
+"Rossz oszlopazonosito: '%-.64s'",
+"A %s a '%-.80s'-hez kozeli a %d sorban",
+"Ures lekerdezes.",
+"Nem egyedi tabla/alias: '%-.64s'",
+"Ervenytelen ertek: '%-.64s'",
+"Tobbszoros elsodleges kulcs definialas.",
+"Tul sok kulcs. Maximum %d kulcs engedelyezett.",
+"Tul sok kulcsdarabot definialt. Maximum %d resz engedelyezett",
+"A megadott kulcs tul hosszu. Maximalis kulcshosszusag: %d",
+"A(z) '%-.64s'kulcsoszlop nem letezik a tablaban",
+"Blob objektum '%-.64s' nem hasznalhato kulcskent",
+"A(z) '%-.64s' oszlop tul hosszu. (maximum = %d). Hasznaljon BLOB tipust inkabb.",
+"Csak egy auto mezo lehetseges, es azt kulcskent kell definialni.",
+"%s: kapcsolatra kesz\n",
+"%s: Normal leallitas\n",
+"%s: %d jelzes. Megszakitva!\n",
+"%s: A leallitas kesz\n",
+"%s: A(z) %ld thread kenyszeritett zarasa. Felhasznalo: '%-.64s'\n",
+"Az IP socket nem hozhato letre",
+"A(z) '%-.64s' tablahoz nincs meg a CREATE INDEX altal hasznalt index. Alakitsa at a tablat",
+"A mezoelvalaszto argumentumok nem egyeznek meg a varttal. Nezze meg a kezikonyvben!","
+"Fix hosszusagu BLOB-ok nem hasznalhatok. Hasznalja a 'mezoelvalaszto jelet' .",
+"A(z) '%-.64s'-nak az adatbazis konyvtarban kell lennie, vagy mindenki szamara olvashatonak",
+"A '%-.64s' file mar letezik.",
+"Rekordok: %ld Torolve: %ld Skipped: %ld Warnings: %ld",
+"Rekordok: %ld Duplikalva: %ld",
+"Rossz alkulcs. A hasznalt kulcsresz nem karaktersorozat vagy hosszabb, mint a kulcsresz",
+"Az osszes mezo nem torolheto az ALTER TABLE-lel. Hasznalja a DROP TABLE-t helyette",
+"A DROP '%-.64s' nem lehetseges. Ellenorizze, hogy a mezo/kulcs letezik-e",
+"Rekordok: %ld Duplikalva: %ld Warnings: %ld",
+"INSERT TABLE '%-.64s' nem engedelyezett a FROM table listabol",
+"Ervenytelen szal (thread) id: %lu",
+"A %lu thread-nek mas a tulajdonosa",
+"Nincs hasznalt tabla",
+"Tul sok karakter: %-.64s es SET",
+"Egyedi log-filenev nem generalhato: %-.64s.(1-999)\n",
+"A(z) '%-.64s' tabla zarolva lett (READ lock) es nem lehet frissiteni",
+"A(z) '%-.64s' tabla nincs zarolva a LOCK TABLES-szel",
+"A(z) '%-.64s' blob objektumnak nem lehet alapertelmezett erteke",
+"Hibas adatbazisnev: '%-.100s'",
+"Hibas tablanev: '%-.100s'",
+"A SELECT tul sok rekordot fog megvizsgalni es nagyon sokaig fog tartani. Ellenorizze a WHERE-t es hasznalja a SET OPTION SQL_BIG_SELECTS=1 beallitast, ha a SELECT ok",
+"Ismeretlen hiba",
+"Ismeretlen eljaras: '%-.64s'",
+"Rossz parameter a(z) '%-.64s'eljaras szamitasanal",
+"Rossz parameter a(z) '%-.64s' eljarasban",
+"Ismeretlen tabla: '%-.64s' %s-ban",
+"A(z) '%-.64s' mezot ketszer definialta",
+"A group funkcio ervenytelen hasznalata",
+"A(z) '%-.64s' tabla olyan bovitest hasznal, amely nem letezik ebben a MySQL versioban.",
+"A tablanak legalabb egy oszlopot tartalmazni kell",
+"A '%-.64s' tabla megtelt",
+"Ervenytelen karakterkeszlet: '%-.64s'",
+"Tul sok tabla. A MySQL csak %d tablat tud kezelni osszefuzeskor",
+"Tul sok mezo",
+"Tul nagy sormeret. A maximalis sormeret (nem szamolva a blob objektumokat) %d. Nehany mezot meg kell valtoztatnia",
+"Thread verem tullepes: Used: %ld of a %ld stack. Hasznalja a 'mysqld -O thread_stack=#' nagyobb verem definialasahoz",
+"Keresztfuggoseg van az OUTER JOIN-ban. Ellenorizze az ON felteteleket",
+"A(z) '%-.64s' oszlop INDEX vagy UNIQUE (egyedi), de a definicioja szerint nem NOT NULL",
+"A(z) '%-.64s' fuggveny nem toltheto be",
+"A(z) '%-.64s' fuggveny nem inicializalhato; %-.80s",
+"Nincs ut a megosztott konyvtarakhoz (shared library)",
+"A '%-.64s' fuggveny mar letezik",
+"A(z) '%-.64s' megosztott konyvtar nem hasznalhato (hibakod: %d %s)",
+"A(z) '%-.64s' fuggveny nem talalhato a konyvtarban",
+"A '%-.64s' fuggveny nem definialt",
+"A '%-.64s' host blokkolodott, tul sok kapcsolodasi hiba miatt. Hasznalja a 'mysqladmin flush-hosts' parancsot",
+"A '%-.64s' host szamara nem engedelyezett a kapcsolodas ehhez a MySQL szerverhez",
+"Nevtelen (anonymous) felhasznalokent nem negedelyezett a jelszovaltoztatas",
+"Onnek tabla-update joggal kell rendelkeznie a mysql adatbazisban masok jelszavanak megvaltoztatasahoz",
+"Nincs megegyezo sor a user tablaban",
+"Megegyezo sorok szama: %ld Valtozott: %ld Warnings: %ld",
+"Uj thread letrehozasa nem lehetseges (Hibakod: %d). Amenyiben van meg szabad memoria, olvassa el a kezikonyv operacios rendszerfuggo hibalehetosegekrol szolo reszet",
+"Az oszlopban talalhato ertek nem egyezik meg a %ld sorban szamitott ertekkel",
+"Nem lehet ujra-megnyitni a tablat: '%-.64s',
+"A NULL ervenytelen hasznalata",
+"'%-.64s' hiba a regularis kifejezes hasznalata soran (regexp)",
+"A GROUP mezok (MIN(),MAX(),COUNT()...) kevert hasznalata nem lehetseges GROUP BY hivatkozas nelkul",
+"A '%-.32s' felhasznalonak nincs ilyen joga a '%-.64s' host-on",
+"%-.16s parancs a '%-.32s@%-.64s' felhasznalo szamara nem engedelyezett a '%-.64s' tablaban",
+"%-.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 '%-.32s' felhasznalo szamara a '%-.64s' host '%-.64s' tablajaban ez a parancs nem engedelyezett",
+"A hasznalt parancs nem engedelyezett ebben a MySQL verzioban",
+"Szintaktikai hiba",
+"A kesleltetett beillesztes (delayed insert) thread nem kapott zatolast a %-.64s tablahoz",
+"Tul sok kesletetett thread (delayed)",
+"Megszakitott kapcsolat %ld db: '%-.64s' adatbazishoz, felhasznalo: '%-.64s' (%s)",
+"A kapott csomag nagyobb, mint a maximalisan engedelyezett: 'max_allowed_packet'",
+"Olvasasi hiba a kapcsolat soran",
+"Hiba a fcntl() fuggvenyben",
+"Helytelen sorrendben erkezett adatcsomagok",
+"A kommunikacios adatcsomagok nem tomorithetok ki",
+"HIba a kommunikacios adatcsomagok olvasasa soran"
+"Idotullepes a kommunikacios adatcsomagok olvasasa soran",
+"Hiba a kommunikacios csomagok irasa soran",
+"Idotullepes a kommunikacios csomagok irasa soran",
+"Ez eredmeny sztring nagyobb, mint a lehetseges maximum: max_allowed_packet",
+"A hasznalt tabla tipus nem tamogatja a BLOB/TEXT mezoket",
+"A hasznalt tabla tipus nem tamogatja az AUTO_INCREMENT tipusu mezoket",
+"Az INSERT DELAYED nem hasznalhato a '%-.64s' tablahoz, mert a tabla zarolt (LOCK TABLES)",
+"Ervenytelen mezonev: '%-.100s'",
+"A hasznalt tablakezelo nem tudja a '%-.64s' mezot indexelni",
+"A MERGE tablaban talalhato tablak definicioja nem azonos",
+"A '%-.64s' nem irhato, az egyedi mezok miatt",
+"BLOB mezo '%-.64s' hasznalt a mezo specifikacioban, a mezohossz megadasa nelkul",
+"Az elsodleges kulcs teljes egeszeben csak NOT NULL tipusu lehet; Ha NULL mezot szeretne a kulcskent, hasznalja inkabb a UNIQUE-ot",
+"Az eredmeny tobb, mint egy sort tartalmaz",
+"Az adott tablatipushoz elsodleges kulcs hasznalata kotelezo",
+"Ezen leforditott MySQL verzio nem tartalmaz RAID support-ot",
+"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",
+"Az On szamara nem engedelyezett a parancs vegrehajtasa a tranzakcioban",
+"%d hiba a COMMIT vegrehajtasa soran",
+"%d hiba a ROLLBACK vegrehajtasa soran",
+"%d hiba a FLUSH_LOGS vegrehajtasa soran",
+"%d hiba a CHECKPOINT vegrehajtasa soran",
+"Aborted connection %ld to db: '%-.64s' user: '%-.32s' host: `%-.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",
diff --git a/sql/share/italian/errmsg.sys b/sql/share/italian/errmsg.sys
new file mode 100644
index 00000000000..f1300819c3c
--- /dev/null
+++ b/sql/share/italian/errmsg.sys
Binary files differ
diff --git a/sql/share/italian/errmsg.txt b/sql/share/italian/errmsg.txt
new file mode 100644
index 00000000000..0aa12667b5e
--- /dev/null
+++ b/sql/share/italian/errmsg.txt
@@ -0,0 +1,195 @@
+/* Copyright Abandoned 1997 TCX DataKonsult AB & Monty Program KB & Detron HB
+ This file is public domain and comes with NO WARRANTY of any kind */
+
+"hashchk",
+"isamchk",
+"NO",
+"SI",
+"Impossibile creare il file '%-.64s' (errno: %d)",
+"Impossibile creare la tabella '%-.64s' (errno: %d)",
+"Impossibile creare il database '%-.64s'. (errno: %d)",
+"Impossibile creare il database '%-.64s'. Il database esiste",
+"Impossibile cancellare '%-.64s'. Il database non esiste",
+"Errore durante la cancellazione del database (impossibile cancellare '%-.64s', errno: %d)",
+"Errore durante la cancellazione del database (impossibile rmdir '%-.64s', errno: %d)",
+"Errore durante la cancellazione di '%-.64s' (errno: %d)",
+"Impossibile leggere il record dalla tabella di sistema",
+"Impossibile leggere lo stato di '%-.64s' (errno: %d)",
+"Impossibile leggere la directory di lavoro (errno: %d)",
+"Impossibile il locking il file (errno: %d)",
+"Impossibile aprire il file: '%-.64s'. (errno: %d)",
+"Impossibile trovare il file: '%-.64s' (errno: %d)",
+"Impossibile leggere la directory di '%-.64s' (errno: %d)",
+"Impossibile cambiare la directory in '%-.64s' (errno: %d)",
+"Il record e` cambiato dall'ultima lettura della tabella '%-.64s'",
+"Disco pieno (%s). In attesa che qualcuno liberi un po' di spazio....",
+"Impossibile scrivere, chiave duplicata nella tabella '%-.64s'",
+"Errore durante la chiusura di '%-.64s' (errno: %d)",
+"Errore durante la lettura del file '%-.64s' (errno: %d)",
+"Errore durante la rinominazione da '%-.64s' a '%-.64s' (errno: %d)",
+"Errore durante la scrittura del file '%-.64s' (errno: %d)",
+"'%-.64s' e` soggetto a lock contro i cambiamenti",
+"Operazione di ordinamento abbandonata" ,
+"La view '%-.64s' non esiste per '%-.64s'",
+"Rilevato l'errore %d dal gestore delle tabelle",
+"Il gestore delle tabelle per '%-.64s' non ha questa opzione",
+"Impossibile trovare il record in '%-.64s'",
+"Informazione errata nel file: '%-.64s'",
+"File chiave errato per la tabella : '%-.64s'. Prova a riparalo",
+"File chiave vecchio per la tabella '%-.64s'; Riparalo!",
+"'%-.64s' e` di sola lettura",
+"Memoria esaurita. Fai ripartire il demone e riprova (richiesti %d bytes)",
+"Memoria per gli ordinamenti esaurita. Incrementare il 'sort_buffer' al demone",
+"Fine del file inaspettata durante la lettura del file '%-.64s' (errno: %d)",
+"Troppe connessioni",
+"Fine dello spazio/memoria per i thread",
+"Impossibile risalire al nome dell'host dal tuo indirizzo",
+"Negoziazione impossibile",
+"Accesso non consentito per l'utente: '%-.32s@%-.64s' al database '%-.64s'",
+"Accesso non consentito per l'utente: '%-.32s@%-.64s' (Password: %s)",
+"Nessun database selezionato",
+"Comando sconosciuto",
+"La colonna '%-.64s' non puo` essere nulla",
+"Database '%-.64s' sconosciuto",
+"La tabella '%-.64s' esiste gia`",
+"Tabella '%-.64s' sconosciuta",
+"Colonna: '%-.64s' di %-.64s e` ambigua",
+"Shutdown del server in corso",
+"Colonna sconosciuta '%-.64s' in '%-.64s'",
+"Usato '%-.64s' che non e` nel GROUP BY",
+"Impossibile raggruppare per '%-.64s'",
+"Il comando ha una funzione SUM e una colonna non specificata nella GROUP BY",
+"Il numero delle colonne non e` uguale al numero dei valori",
+"Il nome dell'identificatore '%-.100s' e` troppo lungo",
+"Nome colonna duplicato '%-.64s'",
+"Nome chiave duplicato '%-.64s'",
+"Valore duplicato '%-.64s' per la chiave %d",
+"Specifica errata per la colonna '%-.64s'",
+"%s vicino a '%-.80s' linea %d",
+"La query e` vuota",
+"Tabella/alias non unico: '%-.64s'",
+"Valore di default non valido per '%-.64s'",
+"Definite piu` chiave primarie",
+"Troppe chiavi. Sono ammesse max %d chiavi",
+"Troppe parti di chiave specificate. Sono ammesse max %d parti",
+"La chiave specificata e` troppo lunga. La max lunghezza della chiave e` %d",
+"La colonna chiave '%-.64s' non esiste nella tabella",
+"La colonna Blob '%-.64s' non puo` essere usata nella specifica della chiave",
+"La colonna '%-.64s' e` troppo grande (max=%d). Utilizza un BLOB.",
+"Puo` esserci solo un campo AUTO e deve essere definito come chiave",
+"%s: Pronto per le connessioni\n",
+"%s: Shutdown normale\n",
+"%s: Ricevuto segnale %d. Interruzione!\n",
+"%s: Shutdown completato\n",
+"%s: Forzata la chiusura del thread %ld utente: '%-.64s'\n",
+"Impossibile creare il socket IP",
+"La tabella '%-.64s' non ha nessun indice come quello specificatato dalla CREATE INDEX. Ricrea la tabella",
+"L'argomento 'Field separator' non e` quello atteso. Controlla il manuale","
+"Non possono essere usate righe a lunghezza fissa con i BLOB. Usa 'FIELDS TERMINATED BY'.",
+"Il file '%-.64s' deve essere nella directory del database e deve essere leggibile da tutti",
+"Il file '%-.64s' esiste gia`",
+"Records: %ld Cancellati: %ld Saltati: %ld Avvertimenti: %ld",
+"Records: %ld Duplicati: %ld",
+"Sotto-parte della chiave errata. La parte di chiave utilizzata non e` una stringa o la lunghezza e` maggiore della parte di chiave.",
+"Non si possono cancellare tutti i campi con una ALTER TABLE. Utilizzare DROP TABLE",
+"Impossibile cancellare '%-.64s'. Controllare che il campo chiave esista",
+"Records: %ld Duplicati: %ld Avvertimenti: %ld",
+"INSERT TABLE '%-.64s' non e` permesso nella FROM table list",
+"Thread id: %lu sconosciuto",
+"Utente non proprietario del thread %lu",
+"Nessuna tabella usata",
+"Troppe stringhe per la colonna %-.64s e la SET",
+"Impossibile generare un nome del file log unico %-.64s.(1-999)\n",
+"La tabella '%-.64s' e` soggetta a lock in lettura e non puo` essere aggiornata",
+"Non e` stato impostato il lock per la tabella '%-.64s' con LOCK TABLES",
+"Il campo Blob '%-.64s' non puo` avere un valore di default",
+"Nome database errato '%-.100s'",
+"Nome tabella errato '%-.100s'",
+"La SELECT dovrebbe esaminare troppi record e usare troppo tempo. Controllare la WHERE e usa SET OPTION SQL_BIG_SELECTS=1 se e` tutto a posto.",
+"Errore sconosciuto",
+"Procedura '%-.64s' sconosciuta",
+"Numero di parametri errato per la procedura '%-.64s'",
+"Parametri errati per la procedura '%-.64s'",
+"Tabella '%-.64s' sconosciuta in %s",
+"Campo '%-.64s' specificato 2 volte",
+"Uso non valido di una funzione di raggruppamento",
+"La tabella '%-.64s' usa un'estensione che non esiste in questa versione di MySQL",
+"Una tabella deve avere almeno 1 colonna",
+"La tabella '%-.64s' e` piena",
+"Set di caratteri '%-.64s' sconosciuto",
+"Troppe tabelle. MySQL puo` usare solo %d tabelle in una join",
+"Troppi campi",
+"Riga troppo grande. La massima grandezza di una riga, non contando i BLOB, e` %d. Devi cambiare alcuni campi in BLOB",
+"Thread stack overrun: Usati: %ld di uno stack di %ld. Usa 'mysqld -O thread_stack=#' per specificare uno stack piu` grande.",
+"Trovata una dipendenza incrociata nella OUTER JOIN. Controlla le condizioni ON",
+"La colonna '%-.64s' e` usata con UNIQUE o INDEX ma non e` definita come NOT NULL",
+"Impossibile caricare la funzione '%-.64s'",
+"Impossibile inizializzare la funzione '%-.64s'; %-.80s",
+"Non sono ammessi path per le librerie condivisa",
+"La funzione '%-.64s' esiste gia`",
+"Impossibile aprire la libreria condivisa '%-.64s' (errno: %d %s)",
+"Impossibile trovare la funzione '%-.64s' nella libreria",
+"La funzione '%-.64s' non e` definita",
+"Sistema '%-.64s' bloccato a causa di troppi errori di connessione. Per sbloccarlo: 'mysqladmin flush-hosts'",
+"Al sistema '%-.64s' non e` consentita la connessione a questo server MySQL",
+"Impossibile cambiare la password usando MySQL come utente anonimo",
+"E` necessario il privilegio di update sulle tabelle del database mysql per cambiare le password per gli altri utenti",
+"Impossibile trovare la riga corrispondente nella tabella user",
+"Rows riconosciute: %ld Cambiate: %ld Warnings: %ld",
+"Impossibile creare un nuovo thread (errno %d). Se non ci sono problemi di memoria disponibile puoi consultare il manuale per controllare possibili problemi dipendenti dal SO",
+"Il numero delle colonne non corrisponde al conteggio alla riga %ld",
+"Impossibile riaprire la tabella: '%-.64s'",
+"Uso scorretto del valore NULL",
+"Errore '%-.64s' da regexp",
+"Il mescolare funzioni di aggregazione (MIN(),MAX(),COUNT()...) e non e` illegale se non c'e` una clausula GROUP BY",
+"GRANT non definita per l'utente '%-.32s' dalla macchina '%-.64s'",
+"Comando %-.16s negato per l'utente: '%-.32s@%-.64s' sulla tabella '%-.64s'",
+"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",
+"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",
+"Il thread di inserimento ritardato non riesce ad avere il lock per la tabella %-.64s",
+"Troppi threads ritardati in uso",
+"Interrotta la connessione %ld al db: '%-.64s' utente: '%-.64s' (%s)",
+"Ricevuto un pacchetto piu` grande di 'max_allowed_packet'",
+"Rilevato un errore di lettura dalla pipe di connessione",
+"Rilevato un errore da fcntl()",
+"Ricevuti pacchetti non in ordine",
+"Impossibile scompattare i pacchetti di comunicazione",
+"Rilevato un errore ricevendo i pacchetti di comunicazione",
+"Rilevato un timeout ricevendo i pacchetti di comunicazione",
+"Rilevato un errore inviando i pacchetti di comunicazione",
+"Rilevato un timeout inviando i pacchetti di comunicazione",
+"La stringa di risposta e` piu` lunga di max_allowed_packet",
+"Il tipo di tabella usata non supporta colonne di tipo BLOB/TEXT",
+"Il tipo di tabella usata non supporta colonne di tipo AUTO_INCREMENT",
+"L'inserimento ritardato (INSERT DELAYED) non puo` essere usato con la tabella '%-.64s', perche` soggetta a lock da 'LOCK TABLES'",
+"Nome colonna '%-.100s' non corretto",
+"Il gestore delle tabelle non puo` indicizzare la colonna '%-.64s'",
+"Non tutte le tabelle nella tabella di MERGE sono definite in maniera identica",
+"Impossibile scrivere nella tabella '%-.64s' per limitazione di unicita`",
+"La colonna '%-.64s' di tipo BLOB e' usata in una chiave senza specificarne la lunghezza",
+"Tutte le parti di una chiave primaria devono essere dichiarate NOT NULL; se hai bisogno del valore NULL in una chiave usa UNIQUE",
+"Il risultato consiste di piu' di una riga",
+"Questo tipo di tabella richiede una chiave primaria",
+"Questa versione di MYSQL non e' compilata con il supporto RAID",
+"Stai il modo 'safe update' e hai provato ad aggiornare una tabella senza una WHERE che usi una chiave",
+"La chiave '%-.64s' non esiste nella tabella '%-.64s'",
+"Impossibile aprire la tabella",
+"Il gestore per la tabella non supporta il controllo/riparazione",
+"Non puoi eseguire questo comando in una transazione",
+"Rilevato l'errore %d durante la COMMIT",
+"Rilevato l'errore %d durante il ROLLBACK",
+"Rilevato l'errore %d durante il FLUSH_LOGS",
+"Rilevato l'errore %d durante il CHECKPOINT",
+"Abortita la connessione %ld al db: ''%-.64s' utente: '%-.32s' host: '%-.64s' (%-.64s)",
+"Il gestore per la tabella non supporta il dump binario",
+"Binlog e' stato chiuso mentre provavo ad eseguire FLUSH MASTER",
+"Fallita la ricostruzione dell'indice della tabella copiata '%-.64s'",
+"Errore dal master: '%-.64s",
+"Errore di rete ricevendo dal master",
+"Errore di rete inviando al master",
+"Impossibile trovare un indice FULLTEXT che corrisponda all'elenco delle colonne",
diff --git a/sql/share/japanese/errmsg.sys b/sql/share/japanese/errmsg.sys
new file mode 100644
index 00000000000..1b5547999a5
--- /dev/null
+++ b/sql/share/japanese/errmsg.sys
Binary files differ
diff --git a/sql/share/japanese/errmsg.txt b/sql/share/japanese/errmsg.txt
new file mode 100644
index 00000000000..da15c0a22f1
--- /dev/null
+++ b/sql/share/japanese/errmsg.txt
@@ -0,0 +1,197 @@
+/* Copyright Abandoned 1997 TCX DataKonsult AB & Monty Program KB & Detron HB
+ This file is public domain and comes with NO WARRANTY of any kind
+ 3.22.10-beta euc-japanese (ujis) text
+*/
+
+"hashchk",
+"isamchk",
+"NO",
+"YES",
+"'%-.64s' ¥Õ¥¡¥¤¥ë¤¬ºî¤ì¤Þ¤»¤ó (errno: %d)",
+"'%-.64s' ¥Æ¡¼¥Ö¥ë¤¬ºî¤ì¤Þ¤»¤ó.(errno: %d)",
+"'%-.64s' ¥Ç¡¼¥¿¥Ù¡¼¥¹¤¬ºî¤ì¤Þ¤»¤ó (errno: %d)",
+"'%-.64s' ¥Ç¡¼¥¿¥Ù¡¼¥¹¤¬ºî¤ì¤Þ¤»¤ó.´û¤Ë¤½¤Î¥Ç¡¼¥¿¥Ù¡¼¥¹¤¬Â¸ºß¤·¤Þ¤¹",
+"'%-.64s' ¥Ç¡¼¥¿¥Ù¡¼¥¹¤òÇË´þ¤Ç¤­¤Þ¤»¤ó. ¤½¤Î¥Ç¡¼¥¿¥Ù¡¼¥¹¤¬¤Ê¤¤¤Î¤Ç¤¹.",
+"¥Ç¡¼¥¿¥Ù¡¼¥¹ÇË´þ¥¨¥é¡¼ ('%-.64s' ¤òºï½ü¤Ç¤­¤Þ¤»¤ó, errno: %d)",
+"¥Ç¡¼¥¿¥Ù¡¼¥¹ÇË´þ¥¨¥é¡¼ ('%-.64s' ¤ò rmdir ¤Ç¤­¤Þ¤»¤ó, errno: %d)",
+"'%-.64s' ¤Îºï½ü¤¬¥¨¥é¡¼ (errno: %d)",
+"system table ¤Î¥ì¥³¡¼¥É¤òÆɤà»ö¤¬¤Ç¤­¤Þ¤»¤ó¤Ç¤·¤¿",
+"'%-.64s' ¤Î¥¹¥Æ¥¤¥¿¥¹¤¬ÆÀ¤é¤ì¤Þ¤»¤ó. (errno: %d)",
+"working directory ¤òÆÀ¤ë»ö¤¬¤Ç¤­¤Þ¤»¤ó¤Ç¤·¤¿. (errno: %d)",
+"¥Õ¥¡¥¤¥ë¤ò¥í¥Ã¥¯¤Ç¤­¤Þ¤»¤ó.(errno: %d)",
+"'%-.64s' ¥Õ¥¡¥¤¥ë¤ò³«¤¯»ö¤¬¤Ç¤­¤Þ¤»¤ó.(errno: %d)",
+"'%-.64s' ¥Õ¥¡¥¤¥ë¤ò¸«ÉÕ¤±¤ë»ö¤¬¤Ç¤­¤Þ¤»¤ó.(errno: %d)",
+"'%-.64s' ¥Ç¥£¥ì¥¯¥È¥ê¤¬Æɤá¤Þ¤»¤ó.(errno: %d)",
+"'%-.64s' ¥Ç¥£¥ì¥¯¥È¥ê¤Ë chdir ¤Ç¤­¤Þ¤»¤ó.(errno: %d)",
+"Record has changed since last read in table '%-.64s'",
+"Disk full (%s). 狼¤¬²¿¤«¤ò¸º¤é¤¹¤Þ¤Ç¤Þ¤Ã¤Æ¤¯¤À¤µ¤¤...",
+"table '%-.64s' ¤Ë key ¤¬½ÅÊ£¤·¤Æ¤¤¤Æ½ñ¤­¤³¤á¤Þ¤»¤ó",
+"Error on close of '%-.64s' (errno: %d)",
+"'%-.64s' ¥Õ¥¡¥¤¥ë¤ÎÆɤ߹þ¤ß¥¨¥é¡¼ (errno: %d)",
+"'%-.64s' ¤ò '%-.64s' ¤Ë rename ¤Ç¤­¤Þ¤»¤ó (errno: %d)",
+"'%-.64s' ¥Õ¥¡¥¤¥ë¤ò½ñ¤¯»ö¤¬¤Ç¤­¤Þ¤»¤ó (errno: %d)",
+"'%-.64s' ¤Ï¥í¥Ã¥¯¤µ¤ì¤Æ¤¤¤Þ¤¹",
+"Sort ̾̂",
+"View '%-.64s' ¤¬ '%-.64s' ¤ËÄêµÁ¤µ¤ì¤Æ¤¤¤Þ¤»¤ó",
+"Got error %d from table handler",
+"Table handler for '%-.64s' doesn't have this option",
+"'%-.64s'¤Î¤Ê¤«¤Ë¥ì¥³¡¼¥É¤¬¸«ÉÕ¤«¤ê¤Þ¤»¤ó",
+"¥Õ¥¡¥¤¥ë '%-.64s' ¤Î info ¤¬´Ö°ã¤Ã¤Æ¤¤¤ë¤è¤¦¤Ç¤¹",
+"'%-.64s' ¥Æ¡¼¥Ö¥ë¤Î key file ¤¬´Ö°ã¤Ã¤Æ¤¤¤ë¤è¤¦¤Ç¤¹. ½¤Éü¤ò¤·¤Æ¤¯¤À¤µ¤¤",
+"'%-.64s' ¥Æ¡¼¥Ö¥ë¤Ï¸Å¤¤·Á¼°¤Î key file ¤Î¤è¤¦¤Ç¤¹; ½¤Éü¤ò¤·¤Æ¤¯¤À¤µ¤¤",
+"'%-.64s' ¤ÏÆɤ߹þ¤ßÀìÍѤǤ¹",
+"Out of memory. ¥Ç¡¼¥â¥ó¤ò¥ê¥¹¥¿¡¼¥È¤·¤Æ¤ß¤Æ¤¯¤À¤µ¤¤ (%d bytes ɬÍ×)",
+"Out of sort memory. sort buffer size ¤¬Â­¤ê¤Ê¤¤¤è¤¦¤Ç¤¹.",
+"'%-.64s' ¥Õ¥¡¥¤¥ë¤òÆɤ߹þ¤ßÃæ¤Ë EOF ¤¬Í½´ü¤»¤Ì½ê¤Ç¸½¤ì¤Þ¤·¤¿. (errno: %d)",
+"Àܳ¤¬Â¿¤¹¤®¤Þ¤¹",
+"Out of memory; mysqld ¤«¤½¤Î¾¤Î¥×¥í¥»¥¹¤¬¥á¥â¥ê¡¼¤òÁ´¤Æ»È¤Ã¤Æ¤¤¤ë¤«³Îǧ¤·¤Æ¤¯¤À¤µ¤¤. ¥á¥â¥ê¡¼¤ò»È¤¤ÀڤäƤ¤¤Ê¤¤¾ì¹ç¡¢'ulimit' ¤òÀßÄꤷ¤Æ mysqld ¤Î¥á¥â¥ê¡¼»ÈÍѸ³¦Î̤ò¿¤¯¤¹¤ë¤«¡¢swap space ¤òÁý¤ä¤·¤Æ¤ß¤Æ¤¯¤À¤µ¤¤",
+"¤½¤Î address ¤Î hostname ¤¬°ú¤±¤Þ¤»¤ó.",
+"Bad handshake",
+"¥æ¡¼¥¶¡¼ '%-.32s@%-.64s' ¤Î '%-.64s' ¥Ç¡¼¥¿¥Ù¡¼¥¹¤Ø¤Î¥¢¥¯¥»¥¹¤òµñÈݤ·¤Þ¤¹",
+"¥æ¡¼¥¶¡¼ '%-.32s@%-.64s' ¤òµñÈݤ·¤Þ¤¹.(Using password: %s)",
+"¥Ç¡¼¥¿¥Ù¡¼¥¹¤¬ÁªÂò¤µ¤ì¤Æ¤¤¤Þ¤»¤ó.",
+"¤½¤Î¥³¥Þ¥ó¥É¤Ï²¿¡©",
+"Column '%-.64s' ¤Ï null ¤Ë¤Ï¤Ç¤­¤Ê¤¤¤Î¤Ç¤¹",
+"'%-.64s' ¤Ê¤ó¤Æ¥Ç¡¼¥¿¥Ù¡¼¥¹¤ÏÃΤê¤Þ¤»¤ó.",
+"Table '%-.64s' ¤Ï´û¤Ë¤¢¤ê¤Þ¤¹",
+"table '%-.64s' ¤Ï¤¢¤ê¤Þ¤»¤ó.",
+"Column: '%-.64s' in %-.64s is ambiguous",
+"Server ¤ò shutdown Ãæ...",
+"'%-.64s' column ¤Ï '%-.64s' ¤Ë¤Ï¤¢¤ê¤Þ¤»¤ó.",
+"'%-.64s' isn't in GROUP BY",
+"Can't group on '%-.64s'",
+"Statement has sum functions and columns in same statement",
+"Column count doesn't match value count",
+"Identifier name '%-.100s' ¤ÏŤ¹¤®¤Þ¤¹",
+"'%-.64s' ¤È¤¤¤¦ column ̾¤Ï½ÅÊ£¤·¤Æ¤Þ¤¹",
+"'%-.64s' ¤È¤¤¤¦ key ¤Î̾Á°¤Ï½ÅÊ£¤·¤Æ¤¤¤Þ¤¹",
+"'%-.64s' ¤Ï key %d ¤Ë¤ª¤¤¤Æ½ÅÊ£¤·¤Æ¤¤¤Þ¤¹",
+"Incorrect column specifier for column '%-.64s'",
+"%s : '%-.80s' ÉÕ¶á : %d ¹ÔÌÜ",
+"Query ¤¬¶õ¤Ç¤¹.",
+"'%-.64s' ¤Ï°ì°Õ¤Î table/alias ̾¤Ç¤Ï¤¢¤ê¤Þ¤»¤ó",
+"Invalid default value for '%-.64s'",
+"Ê£¿ô¤Î primary key ¤¬ÄêµÁ¤µ¤ì¤Þ¤·¤¿",
+"key ¤Î»ØÄ꤬¿¤¹¤®¤Þ¤¹. key ¤ÏºÇÂç %d ¤Þ¤Ç¤Ç¤¹",
+"Too many key parts specified. Max %d parts allowed",
+"key ¤¬Ä¹¤¹¤®¤Þ¤¹. key ¤ÎŤµ¤ÏºÇÂç %d ¤Ç¤¹",
+"Key column '%-.64s' ¤¬¥Æ¡¼¥Ö¥ë¤Ë¤¢¤ê¤Þ¤»¤ó.",
+"BLOB column '%-.64s' can't be used in key specification with the used table type",
+"column '%-.64s' ¤Ï,³ÎÊݤ¹¤ë column ¤ÎÂ礭¤µ¤¬Â¿¤¹¤®¤Þ¤¹. (ºÇÂç %d ¤Þ¤Ç). BLOB ¤ò¤«¤ï¤ê¤Ë»ÈÍѤ·¤Æ¤¯¤À¤µ¤¤.",
+"¥Æ¡¼¥Ö¥ë¤ÎÄêµÁ¤¬°ã¤¤¤Þ¤¹; There can only be one auto column and it must be defined as a key",
+"%s: ½àÈ÷´°Î»\n",
+"%s: Normal shutdown\n",
+"%s: Got signal %d. ̾̂!\n",
+"%s: Shutdown ´°Î»\n",
+"%s: ¥¹¥ì¥Ã¥É %ld ¶¯À©½ªÎ» user: '%-.64s'\n",
+"IP socket ¤¬ºî¤ì¤Þ¤»¤ó",
+"Table '%-.64s' ¤Ï¤½¤Î¤è¤¦¤Ê index ¤ò»ý¤Ã¤Æ¤¤¤Þ¤»¤ó(CREATE INDEX ¼Â¹Ô»þ¤Ë»ØÄꤵ¤ì¤Æ¤¤¤Þ¤»¤ó). ¥Æ¡¼¥Ö¥ë¤òºî¤êľ¤·¤Æ¤¯¤À¤µ¤¤",
+"Field separator argument is not what is expected. Check the manual",
+"You can't use fixed rowlength with BLOBs. Please use 'fields terminated by'.",
+"¥Õ¥¡¥¤¥ë '%-.64s' ¤Ï databse ¤Î directory ¤Ë¤¢¤ë¤«Á´¤Æ¤Î¥æ¡¼¥¶¡¼¤¬Æɤá¤ë¤è¤¦¤Ëµö²Ä¤µ¤ì¤Æ¤¤¤Ê¤±¤ì¤Ð¤Ê¤ê¤Þ¤»¤ó.",
+"File '%-.64s' ¤Ï´û¤Ë¸ºß¤·¤Þ¤¹",
+"¥ì¥³¡¼¥É¿ô: %ld ºï½ü: %ld Skipped: %ld Warnings: %ld",
+"¥ì¥³¡¼¥É¿ô: %ld ½ÅÊ£: %ld",
+"Incorrect sub part key. The used key part isn't a string or the used length is longer than the key part",
+"ALTER TABLE ¤ÇÁ´¤Æ¤Î column ¤Ïºï½ü¤Ç¤­¤Þ¤»¤ó. DROP TABLE ¤ò»ÈÍѤ·¤Æ¤¯¤À¤µ¤¤",
+"'%-.64s' ¤òÇË´þ¤Ç¤­¤Þ¤»¤ó¤Ç¤·¤¿. Check that column/key exists",
+"¥ì¥³¡¼¥É¿ô: %ld ½ÅÊ£¿ô: %ld Warnings: %ld",
+"INSERT TABLE '%-.64s' isn't allowed in FROM table list",
+"thread id: %lu ¤Ï¤¢¤ê¤Þ¤»¤ó",
+"thread %lu ¤Î¥ª¡¼¥Ê¡¼¤Ç¤Ï¤¢¤ê¤Þ¤»¤ó",
+"No tables used",
+"Too many strings for column %-.64s and SET",
+"Can't generate a unique log-filename %-.64s.(1-999)\n",
+"Table '%-.64s' ¤Ï READ lock ¤Ë¤Ê¤Ã¤Æ¤¤¤Æ¡¢¹¹¿·¤Ï¤Ç¤­¤Þ¤»¤ó",
+"Table '%-.64s' ¤Ï LOCK TABLES ¤Ë¤è¤Ã¤Æ¥í¥Ã¥¯¤µ¤ì¤Æ¤¤¤Þ¤»¤ó",
+"BLOB column '%-.64s' can't have a default value",
+"»ØÄꤷ¤¿ database ̾ '%-.100s' ¤¬´Ö°ã¤Ã¤Æ¤¤¤Þ¤¹",
+"»ØÄꤷ¤¿ table ̾ '%-.100s' ¤Ï¤Þ¤Á¤¬¤Ã¤Æ¤¤¤Þ¤¹",
+"The SELECT would examine too many records and probably take a very long time. Check your WHERE and use SET OPTION SQL_BIG_SELECTS=1 if the SELECT is ok",
+"Unknown error",
+"Unknown procedure '%-.64s'",
+"Incorrect parameter count to procedure '%-.64s'",
+"Incorrect parameters to procedure '%-.64s'",
+"Unknown table '%-.64s' in %s",
+"Column '%-.64s' specified twice",
+"Invalid use of group function",
+"Table '%-.64s' uses an extension that doesn't exist in this MySQL version",
+"¥Æ¡¼¥Ö¥ë¤ÏºÇÄã 1 ¸Ä¤Î column ¤¬É¬ÍפǤ¹",
+"table '%-.64s' ¤Ï¤¤¤Ã¤Ñ¤¤¤Ç¤¹",
+"character set '%-.64s' ¤Ï¥µ¥Ý¡¼¥È¤·¤Æ¤¤¤Þ¤»¤ó",
+"¥Æ¡¼¥Ö¥ë¤¬Â¿¤¹¤®¤Þ¤¹. MySQL can only use %d tables in a join",
+"column ¤¬Â¿¤¹¤®¤Þ¤¹",
+"row size ¤¬Â礭¤¹¤®¤Þ¤¹. BLOB ¤ò´Þ¤Þ¤Ê¤¤¾ì¹ç¤Î row size ¤ÎºÇÂç¤Ï %d ¤Ç¤¹. ¤¤¤¯¤Ä¤«¤Î field ¤ò BLOB ¤ËÊѤ¨¤Æ¤¯¤À¤µ¤¤.",
+"Thread stack overrun: Used: %ld of a %ld stack. ¥¹¥¿¥Ã¥¯Îΰè¤ò¿¤¯¤È¤ê¤¿¤¤¾ì¹ç¡¢'mysqld -O thread_stack=#' ¤È»ØÄꤷ¤Æ¤¯¤À¤µ¤¤",
+"Cross dependency found in OUTER JOIN. Examine your ON conditions",
+"Column '%-.64s' ¤¬ UNIQUE ¤« INDEX ¤Ç»ÈÍѤµ¤ì¤Þ¤·¤¿. ¤³¤Î¥«¥é¥à¤Ï NOT NULL ¤ÈÄêµÁ¤µ¤ì¤Æ¤¤¤Þ¤»¤ó.",
+"function '%-.64s' ¤ò ¥í¡¼¥É¤Ç¤­¤Þ¤»¤ó",
+"function '%-.64s' ¤ò½é´ü²½¤Ç¤­¤Þ¤»¤ó; %-.80s",
+"shared library ¤Ø¤Î¥Ñ¥¹¤¬Ä̤äƤ¤¤Þ¤»¤ó",
+"Function '%-.64s' ¤Ï´û¤ËÄêµÁ¤µ¤ì¤Æ¤¤¤Þ¤¹",
+"shared library '%-.64s' ¤ò³«¤¯»ö¤¬¤Ç¤­¤Þ¤»¤ó (errno: %d %s)",
+"function '%-.64s' ¤ò¥é¥¤¥Ö¥é¥ê¡¼Ãæ¤Ë¸«ÉÕ¤±¤ë»ö¤¬¤Ç¤­¤Þ¤»¤ó",
+"Function '%-.64s' ¤ÏÄêµÁ¤µ¤ì¤Æ¤¤¤Þ¤»¤ó",
+"Host '%-.64s' ¤Ï many connection error ¤Î¤¿¤á¡¢µñÈݤµ¤ì¤Þ¤·¤¿. 'mysqladmin flush-hosts' ¤Ç²ò½ü¤·¤Æ¤¯¤À¤µ¤¤",
+"Host '%-.64s' ¤Ï MySQL server ¤ËÀܳ¤òµö²Ä¤µ¤ì¤Æ¤¤¤Þ¤»¤ó",
+"MySQL ¤ò anonymous users ¤Ç»ÈÍѤ·¤Æ¤¤¤ë¾õÂ֤Ǥϡ¢¥Ñ¥¹¥ï¡¼¥É¤ÎÊѹ¹¤Ï¤Ç¤­¤Þ¤»¤ó",
+"¾¤Î¥æ¡¼¥¶¡¼¤Î¥Ñ¥¹¥ï¡¼¥É¤òÊѹ¹¤¹¤ë¤¿¤á¤Ë¤Ï, mysql ¥Ç¡¼¥¿¥Ù¡¼¥¹¤ËÂФ·¤Æ update ¤Îµö²Ä¤¬¤Ê¤±¤ì¤Ð¤Ê¤ê¤Þ¤»¤ó.",
+"Can't find any matching row in the user table",
+"°ìÃ׿ô(Rows matched): %ld Êѹ¹: %ld Warnings: %ld",
+"¿·µ¬¤Ë¥¹¥ì¥Ã¥É¤¬ºî¤ì¤Þ¤»¤ó¤Ç¤·¤¿ (errno %d). ¤â¤·ºÇÂç»ÈÍѵö²Ä¥á¥â¥ê¡¼¿ô¤ò±Û¤¨¤Æ¤¤¤Ê¤¤¤Î¤Ë¥¨¥é¡¼¤¬È¯À¸¤·¤Æ¤¤¤ë¤Ê¤é, ¥Þ¥Ë¥å¥¢¥ë¤ÎÃ椫¤é 'possible OS-dependent bug' ¤È¤¤¤¦Ê¸»ú¤òõ¤·¤Æ¤¯¤ß¤Æ¤À¤µ¤¤.",
+"Column count doesn't match value count at row %ld",
+"Can't reopen table: '%-.64s'",
+"NULL ÃͤλÈÍÑÊýË¡¤¬ÉÔŬÀڤǤ¹",
+"Got error '%-.64s' from regexp",
+"Mixing of GROUP columns (MIN(),MAX(),COUNT()...) with no GROUP columns is illegal if there is no GROUP BY clause",
+"¥æ¡¼¥¶¡¼ '%-.32s' (¥Û¥¹¥È '%-.64s' ¤Î¥æ¡¼¥¶¡¼) ¤Ïµö²Ä¤µ¤ì¤Æ¤¤¤Þ¤»¤ó",
+"¥³¥Þ¥ó¥É %-.16s ¤Ï ¥æ¡¼¥¶¡¼ '%-.32s@%-.64s' ,¥Æ¡¼¥Ö¥ë '%-.64s' ¤ËÂФ·¤Æµö²Ä¤µ¤ì¤Æ¤¤¤Þ¤»¤ó",
+"¥³¥Þ¥ó¥É %-.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",
+"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",
+"Delayed insert thread couldn't get requested lock for table %-.64s",
+"Too many delayed threads in use",
+"Aborted connection %ld to db: '%-.64s' user: '%-.64s' (%s)",
+"Got a packet bigger than 'max_allowed_packet'",
+"Got a read error from the connection pipe",
+"Got an error from fcntl()",
+"Got packets out of order",
+"Couldn't uncompress communication packet",
+"Got an error reading communication packets"
+"Got timeout reading communication packets",
+"Got an error writing communication packets",
+"Got timeout writing communication packets",
+"Result string is longer than max_allowed_packet",
+"The used table type doesn't support BLOB/TEXT columns",
+"The used table type doesn't support AUTO_INCREMENT columns",
+"INSERT DELAYED can't be used with table '%-.64s', because it is locked with LOCK TABLES",
+"Incorrect column name '%-.100s'",
+"The used table handler can't index column '%-.64s'",
+"All tables in the MERGE table are not defined identically",
+"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",
+"Result consisted of more than one row",
+"This table type requires a primary key",
+"This version of MySQL is not compiled with RAID support",
+"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",
+"You are not allowed to execute this command in a transaction",
+"Got error %d during COMMIT",
+"Got error %d during ROLLBACK",
+"Got error %d during FLUSH_LOGS",
+"Got error %d during CHECKPOINT",
+"Aborted connection %ld to db: '%-.64s' user: '%-.32s' host: `%-.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",
diff --git a/sql/share/korean/errmsg.sys b/sql/share/korean/errmsg.sys
new file mode 100644
index 00000000000..4bd754f1ff5
--- /dev/null
+++ b/sql/share/korean/errmsg.sys
Binary files differ
diff --git a/sql/share/korean/errmsg.txt b/sql/share/korean/errmsg.txt
new file mode 100644
index 00000000000..b5f231e1b70
--- /dev/null
+++ b/sql/share/korean/errmsg.txt
@@ -0,0 +1,195 @@
+/* Copyright Abandoned 1997 TCX DataKonsult AB & Monty Program KB & Detron HB
+ This È­ÀÏ is public domain and comes with NO WARRANTY of any kind */
+
+"hashchk",
+"isamchk",
+"¾Æ´Ï¿À",
+"¿¹",
+"È­ÀÏ '%-.64s'¸¦ ¸¸µéÁö ¸øÇß½À´Ï´Ù. (¿¡·¯¹øÈ£: %d)",
+"Å×À̺í '%-.64s'¸¦ ¸¸µéÁö ¸øÇß½À´Ï´Ù. (¿¡·¯¹øÈ£: %d)",
+"µ¥ÀÌŸº£À̽º '%-.64s'¸¦ ¸¸µéÁö ¸øÇß½À´Ï´Ù.. (¿¡·¯¹øÈ£: %d)",
+"µ¥ÀÌŸº£À̽º '%-.64s'¸¦ ¸¸µéÁö ¸øÇß½À´Ï´Ù.. µ¥ÀÌŸº£À̽º°¡ Á¸ÀçÇÔ",
+"µ¥ÀÌŸº£À̽º '%-.64s'¸¦ Á¦°ÅÇÏÁö ¸øÇß½À´Ï´Ù. µ¥ÀÌŸº£À̽º°¡ Á¸ÀçÇÏÁö ¾ÊÀ½ ",
+"µ¥ÀÌŸº£À̽º Á¦°Å ¿¡·¯('%-.64s'¸¦ »èÁ¦ÇÒ ¼ö ¾øÀ¾´Ï´Ù, ¿¡·¯¹øÈ£: %d)",
+"µ¥ÀÌŸº£À̽º Á¦°Å ¿¡·¯(rmdir '%-.64s'¸¦ ÇÒ ¼ö ¾øÀ¾´Ï´Ù, ¿¡·¯¹øÈ£: %d)",
+"'%-.64s' »èÁ¦ Áß ¿¡·¯ (¿¡·¯¹øÈ£: %d)",
+"system Å×ÀÌºí¿¡¼­ ·¹Äڵ带 ÀÐÀ» ¼ö ¾ø½À´Ï´Ù.",
+"'%-.64s'ÀÇ »óŸ¦ ¾òÁö ¸øÇß½À´Ï´Ù. (¿¡·¯¹øÈ£: %d)",
+"¼öÇà µð·ºÅ丮¸¦ ãÁö ¸øÇß½À´Ï´Ù. (¿¡·¯¹øÈ£: %d)",
+"È­ÀÏÀ» Àá±×Áö(lock) ¸øÇß½À´Ï´Ù. (¿¡·¯¹øÈ£: %d)",
+"È­ÀÏÀ» ¿­Áö ¸øÇß½À´Ï´Ù.: '%-.64s'. (¿¡·¯¹øÈ£: %d)",
+"È­ÀÏÀ» ãÁö ¸øÇß½À´Ï´Ù.: '%-.64s' (¿¡·¯¹øÈ£: %d)",
+"'%-.64s'µð·ºÅ丮¸¦ ÀÐÁö ¸øÇß½À´Ï´Ù. (¿¡·¯¹øÈ£: %d)",
+"'%-.64s'µð·ºÅ丮·Î À̵¿ÇÒ ¼ö ¾ø¾ú½À´Ï´Ù. (¿¡·¯¹øÈ£: %d)",
+"Å×À̺í '%-.64s'¿¡¼­ ¸¶Áö¸·À¸·Î ÀÐÀº ÈÄ Record°¡ º¯°æµÇ¾ú½À´Ï´Ù.",
+"Disk full (%s). ´Ù¸¥ »ç¶÷ÀÌ Áö¿ï¶§±îÁö ±â´Ù¸³´Ï´Ù.....",
+"±â·ÏÇÒ ¼ö ¾øÀ¾´Ï´Ù., Å×À̺í '%-.64s'¿¡¼­ Áߺ¹ Å°",
+"'%-.64s'´Ý´Â Áß ¿¡·¯ (¿¡·¯¹øÈ£: %d)",
+"'%-.64s'È­ÀÏ Àб⠿¡·¯ (¿¡·¯¹øÈ£: %d)",
+"'%-.64s'¸¦ '%-.64s'·Î À̸§ º¯°æÁß ¿¡·¯ (¿¡·¯¹øÈ£: %d)",
+"'%-.64s'È­ÀÏ ±â·Ï Áß ¿¡·¯ (¿¡·¯¹øÈ£: %d)",
+"'%-.64s'°¡ º¯°æÇÒ ¼ö ¾øµµ·Ï Àá°ÜÀÖÀ¾´Ï´Ù.",
+"¼ÒÆ®°¡ ÁߴܵǾú½À´Ï´Ù.",
+"ºä '%-.64s'°¡ '%-.64s'¿¡¼­´Â Á¸ÀçÇÏÁö ¾ÊÀ¾´Ï´Ù.",
+"Å×À̺í handler¿¡¼­ %d ¿¡·¯°¡ ¹ß»ý ÇÏ¿´½À´Ï´Ù.",
+"'%-.64s'ÀÇ Å×À̺í handler´Â ÀÌ·¯ÇÑ ¿É¼ÇÀ» Á¦°øÇÏÁö ¾ÊÀ¾´Ï´Ù.",
+"'%-.64s'¿¡¼­ ·¹Äڵ带 ãÀ» ¼ö ¾øÀ¾´Ï´Ù.",
+"È­ÀÏÀÇ ºÎÁ¤È®ÇÑ Á¤º¸: '%-.64s'",
+"'%-.64s' Å×À̺íÀÇ ºÎÁ¤È®ÇÑ Å° Á¸Àç. ¼öÁ¤ÇϽÿÀ!",
+"'%-.64s' Å×À̺íÀÇ ÀÌÀü¹öÁ¯ÀÇ Å° Á¸Àç. ¼öÁ¤ÇϽÿÀ!",
+"Å×À̺í '%-.64s'´Â ÀбâÀü¿ë ÀÔ´Ï´Ù.",
+"Out of memory. µ¥¸óÀ» Àç ½ÇÇà ÈÄ ´Ù½Ã ½ÃÀÛÇϽÿÀ (needed %d bytes)",
+"Out of sort memory. daemon sort bufferÀÇ Å©±â¸¦ Áõ°¡½ÃÅ°¼¼¿ä",
+"'%-.64s' È­ÀÏÀ» Àд µµÁß À߸øµÈ eofÀ» ¹ß°ß (¿¡·¯¹øÈ£: %d)",
+"³Ê¹« ¸¹Àº ¿¬°á... max_connectionÀ» Áõ°¡ ½ÃÅ°½Ã¿À...",
+"Out of memory; mysqld³ª ¶Ç´Ù¸¥ ÇÁ·Î¼¼¼­¿¡¼­ »ç¿ë°¡´ÉÇÑ ¸Þ¸ð¸®¸¦ »ç¿ëÇÑÁö äũÇϽÿÀ. ¸¸¾à ±×·¸Áö ¾Ê´Ù¸é ulimit ¸í·ÉÀ» ÀÌ¿¿ëÇÏ¿© ´õ¸¹Àº ¸Þ¸ð¸®¸¦ »ç¿ëÇÒ ¼ö ÀÖµµ·Ï Çϰųª ½º¿Ò ½ºÆÐÀ̽º¸¦ Áõ°¡½ÃÅ°½Ã¿À",
+"´ç½ÅÀÇ ÄÄÇ»ÅÍÀÇ È£½ºÆ®À̸§À» ¾òÀ» ¼ö ¾øÀ¾´Ï´Ù.",
+"Bad handshake",
+"'%-.32s@%-.64s' »ç¿ëÀÚ´Â '%-.64s' µ¥ÀÌŸº£À̽º¿¡ Á¢±ÙÀÌ °ÅºÎ µÇ¾ú½À´Ï´Ù.",
+"'%-.32s@%-.64s' »ç¿ëÀÚ´Â Á¢±ÙÀÌ °ÅºÎ µÇ¾ú½À´Ï´Ù. (Using password: %s)",
+"¼±ÅÃµÈ µ¥ÀÌŸº£À̽º°¡ ¾ø½À´Ï´Ù.",
+"¸í·É¾î°¡ ¹ºÁö ¸ð¸£°Ú¾î¿ä....",
+"Ä®·³ '%-.64s'´Â ³Î(Null)ÀÌ µÇ¸é ¾ÈµË´Ï´Ù. ",
+"µ¥ÀÌŸº£À̽º '%-.64s'´Â ¾Ë¼ö ¾øÀ½",
+"Å×À̺í '%-.64s'´Â ÀÌ¹Ì Á¸ÀçÇÔ",
+"Å×À̺í '%-.64s'´Â ¾Ë¼ö ¾øÀ½",
+"Ä®·³: '%-.64s' in '%-.64s' ÀÌ ¸ðÈ£ÇÔ",
+"Server°¡ ¼Ë´Ù¿î ÁßÀÔ´Ï´Ù.",
+"Unknown Ä®·³ '%-.64s' in '%-.64s'",
+"'%-.64s'Àº GROUP BY¼Ó¿¡ ¾øÀ½",
+"'%-.64s'¸¦ ±×·ìÇÒ ¼ö ¾øÀ½",
+"Statement °¡ sum±â´ÉÀ» µ¿ÀÛÁßÀÌ°í Ä®·³µµ µ¿ÀÏÇÑ statementÀÔ´Ï´Ù.",
+"Ä®·³ÀÇ Ä«¿îÆ®°¡ °ªÀÇ Ä«¿îÆ®¿Í ÀÏÄ¡ÇÏÁö ¾Ê½À´Ï´Ù.",
+"Identifier '%-.100s'´Â ³Ê¹« ±æ±º¿ä.",
+"Áߺ¹µÈ Ä®·³ À̸§: '%-.64s'",
+"Áߺ¹µÈ Å° À̸§ : '%-.64s'",
+"Áߺ¹µÈ ÀÔ·Â °ª '%-.64s': key %d",
+"Ä®·³ '%-.64s'ÀÇ ºÎÁ¤È®ÇÑ Ä®·³ Á¤ÀÇÀÚ",
+"'%-.64s' ¿¡·¯ °°À¾´Ï´Ù. ('%-.80s' ¸í·É¾î ¶óÀÎ %d)",
+"Äõ¸®°á°ú°¡ ¾ø½À´Ï´Ù.",
+"Unique ÇÏÁö ¾ÊÀº Å×À̺í/alias: '%-.64s'",
+"'%-.64s'ÀÇ À¯È¿ÇÏÁö ¸øÇÑ µðÆúÆ® °ªÀ» »ç¿ëÇϼ̽À´Ï´Ù.",
+"Multiple primary key°¡ Á¤ÀǵǾî ÀÖ½¿",
+"³Ê¹« ¸¹Àº Å°°¡ Á¤ÀǵǾî ÀÖÀ¾´Ï´Ù.. ÃÖ´ë %dÀÇ Å°°¡ °¡´ÉÇÔ",
+"³Ê¹« ¸¹Àº Å° ºÎºÐ(parts)µéÀÌ Á¤ÀǵǾî ÀÖÀ¾´Ï´Ù.. ÃÖ´ë %d ºÎºÐÀÌ °¡´ÉÇÔ",
+"Á¤ÀÇµÈ Å°°¡ ³Ê¹« ±é´Ï´Ù. ÃÖ´ë Å°ÀÇ ±æÀÌ´Â %dÀÔ´Ï´Ù.",
+"Key Ä®·³ '%-.64s'´Â Å×ÀÌºí¿¡ Á¸ÀçÇÏÁö ¾Ê½À´Ï´Ù.",
+"BLOB Ä®·³ '%-.64s'´Â Å° Á¤ÀÇ¿¡¼­ »ç¿ëµÉ ¼ö ¾ø½À´Ï´Ù.",
+"Ä®·³ '%-.64s'ÀÇ Ä®·³ ±æÀÌ°¡ ³Ê¹« ±é´Ï´Ù (ÃÖ´ë = %d). ´ë½Å¿¡ BLOB¸¦ »ç¿ëÇϼ¼¿ä.",
+"ºÎÁ¤È®ÇÑ Å×À̺í Á¤ÀÇ; Å×À̺íÀº ÇϳªÀÇ auto Ä®·³ÀÌ Á¸ÀçÇÏ°í Å°·Î Á¤ÀǵǾîÁ®¾ß ÇÕ´Ï´Ù.",
+"%s: ¿¬°á ÁغñÁßÀÔ´Ï´Ù.\n",
+"%s: Á¤»óÀûÀÎ shutdown\n",
+"%s: %d ½ÅÈ£°¡ µé¾î¿ÔÀ½. ÁßÁö!\n",
+"%s: Shutdown ÀÌ ¿Ï·áµÊ!\n",
+"%s: thread %ldÀÇ °­Á¦ Á¾·á user: '%-.64s'\n",
+"IP ¼ÒÄÏÀ» ¸¸µéÁö ¸øÇß½À´Ï´Ù.",
+"Å×À̺í '%-.64s'´Â À妽º¸¦ ¸¸µéÁö ¾Ê¾Ò½À´Ï´Ù. alter Å×À̺í¸í·ÉÀ» ÀÌ¿ëÇÏ¿© Å×À̺íÀ» ¼öÁ¤Çϼ¼¿ä...",
+"ÇÊµå ±¸ºÐÀÚ ÀμöµéÀÌ ¿ÏÀüÇÏÁö ¾Ê½À´Ï´Ù. ¸Þ´º¾óÀ» ã¾Æ º¸¼¼¿ä.",
+"BLOB·Î´Â °íÁ¤±æÀÌÀÇ lowlength¸¦ »ç¿ëÇÒ ¼ö ¾ø½À´Ï´Ù. 'fields terminated by'¸¦ »ç¿ëÇϼ¼¿ä.",
+"'%-.64s' È­ÀÏ´Â µ¥ÀÌŸº£À̽º µð·ºÅ丮¿¡ Á¸ÀçÇϰųª ¸ðµÎ¿¡°Ô Àб⠰¡´ÉÇÏ¿©¾ß ÇÕ´Ï´Ù.",
+"'%-.64s' È­ÀÏÀº ÀÌ¹Ì Á¸ÀçÇÕ´Ï´Ù.",
+"·¹ÄÚµå: %ld°³ »èÁ¦: %ld°³ ½ºÅµ: %ld°³ °æ°í: %ld°³",
+"·¹ÄÚµå: %ld°³ Áߺ¹: %ld°³",
+"ºÎÁ¤È®ÇÑ ¼­¹ö ÆÄÆ® Å°. »ç¿ëµÈ Å° ÆÄÆ®°¡ ½ºÆ®¸µÀÌ ¾Æ´Ï°Å³ª Å° ÆÄÆ®ÀÇ ±æÀÌ°¡ ³Ê¹« ±é´Ï´Ù.",
+"ALTER TABLE ¸í·ÉÀ¸·Î´Â ¸ðµç Ä®·³À» Áö¿ï ¼ö ¾ø½À´Ï´Ù. DROP TABLE ¸í·ÉÀ» ÀÌ¿ëÇϼ¼¿ä.",
+"'%-.64s'¸¦ DROPÇÒ ¼ö ¾ø½À´Ï´Ù. Ä®·³À̳ª Å°°¡ Á¸ÀçÇÏ´ÂÁö äũÇϼ¼¿ä.",
+"·¹ÄÚµå: %ld°³ Áߺ¹: %ld°³ °æ°í: %ld°³",
+"INSERT TABLE '%-.64s' ´Â FROM Å×À̺í list¿¡¼­ Çã°¡µÇÁö ¾Ê¾Ò½À´Ï´Ù.",
+"¾Ë¼ö ¾ø´Â ¾²·¹µå id: %lu",
+"¾²·¹µå(Thread) %luÀÇ ¼ÒÀ¯ÀÚ°¡ ¾Æ´Õ´Ï´Ù.",
+"¾î¶² Å×ÀÌºíµµ »ç¿ëµÇÁö ¾Ê¾Ò½À´Ï´Ù.",
+"Ä®·³ %-.64s¿Í SET¿¡¼­ ½ºÆ®¸µÀÌ ³Ê¹« ¸¹½À´Ï´Ù.",
+"Unique ·Î±×È­ÀÏ '%-.64s'¸¦ ¸¸µé¼ö ¾ø½À´Ï´Ù.(1-999)\n",
+"Å×À̺í '%-.64s'´Â READ ¶ôÀÌ Àá°ÜÀ־ °»½ÅÇÒ ¼ö ¾ø½À´Ï´Ù.",
+"Å×À̺í '%-.64s'´Â LOCK TABLES ¸í·ÉÀ¸·Î Àá±âÁö ¾Ê¾Ò½À´Ï´Ù.",
+"BLOB Ä®·³ '%-.64s' ´Â µðÆúÆ® °ªÀ» °¡Áú ¼ö ¾ø½À´Ï´Ù.",
+"'%-.100s' µ¥ÀÌŸº£À̽ºÀÇ À̸§ÀÌ ºÎÁ¤È®ÇÕ´Ï´Ù.",
+"'%-.100s' Å×À̺í À̸§ÀÌ ºÎÁ¤È®ÇÕ´Ï´Ù.",
+"SELECT ¸í·É¿¡¼­ ³Ê¹« ¸¹Àº ·¹Äڵ带 ã±â ¶§¹®¿¡ ¸¹Àº ½Ã°£ÀÌ ¼Ò¿äµË´Ï´Ù. µû¶ó¼­ WHERE ¹®À» Á¡°ËÇϰųª, ¸¸¾à SELECT°¡ okµÇ¸é SET OPTION SQL_BIG_SELECTS=1 ¿É¼ÇÀ» »ç¿ëÇϼ¼¿ä.",
+"¾Ë¼ö ¾ø´Â ¿¡·¯ÀÔ´Ï´Ù.",
+"¾Ë¼ö ¾ø´Â ¼öÇ๮ : '%-.64s'",
+"'%-.64s' ¼öÇ๮¿¡ ´ëÇÑ ºÎÁ¤È®ÇÑ ÆĶó¸ÞÅÍ",
+"'%-.64s' ¼öÇ๮¿¡ ´ëÇÑ ºÎÁ¤È®ÇÑ ÆĶó¸ÞÅÍ",
+"¾Ë¼ö ¾ø´Â Å×À̺í '%-.64s' (µ¥ÀÌŸº£À̽º %s)",
+"Ä®·³ '%-.64s'´Â µÎ¹ø Á¤ÀǵǾî ÀÖÀ¾´Ï´Ù.",
+"À߸øµÈ ±×·ì ÇÔ¼ö¸¦ »ç¿ëÇÏ¿´½À´Ï´Ù.",
+"Å×À̺í '%-.64s'´Â È®Àå¸í·ÉÀ» ÀÌ¿ëÇÏÁö¸¸ ÇöÀçÀÇ MySQL ¹öÁ¯¿¡¼­´Â Á¸ÀçÇÏÁö ¾Ê½À´Ï´Ù.",
+"ÇϳªÀÇ Å×ÀÌºí¿¡¼­´Â Àû¾îµµ ÇϳªÀÇ Ä®·³ÀÌ Á¸ÀçÇÏ¿©¾ß ÇÕ´Ï´Ù.",
+"Å×À̺í '%-.64s'°¡ full³µ½À´Ï´Ù. ",
+"¾Ë¼ö¾ø´Â ¾ð¾î Set: '%-.64s'",
+"³Ê¹« ¸¹Àº Å×À̺íÀÌ JoinµÇ¾ú½À´Ï´Ù. MySQL¿¡¼­´Â JOIN½Ã %d°³ÀÇ Å×ÀÌºí¸¸ »ç¿ëÇÒ ¼ö ÀÖ½À´Ï´Ù.",
+"Ä®·³ÀÌ ³Ê¹« ¸¹½À´Ï´Ù.",
+"³Ê¹« Å« row »çÀÌÁîÀÔ´Ï´Ù. BLOB¸¦ °è»êÇÏÁö ¾Ê°í ÃÖ´ë row »çÀÌÁî´Â %dÀÔ´Ï´Ù. ¾ó¸¶°£ÀÇ ÇʵåµéÀ» BLOB·Î ¹Ù²Ù¼Å¾ß °Ú±º¿ä..",
+"¾²·¹µå ½ºÅÃÀÌ ³ÑÃƽÀ´Ï´Ù. »ç¿ë: %ld°³ ½ºÅÃ: %ld°³. ¸¸¾à ÇÊ¿ä½Ã ´õÅ« ½ºÅÃÀ» ¿øÇÒ¶§¿¡´Â 'mysqld -O thread_stack=#' ¸¦ Á¤ÀÇÇϼ¼¿ä",
+"Cross dependency found in OUTER JOIN. Examine your ON conditions",
+"'%-.64s' Ä®·³ÀÌ UNIQUE³ª INDEX¸¦ »ç¿ëÇÏ¿´Áö¸¸ NOT NULLÀÌ Á¤ÀǵÇÁö ¾Ê¾Ò±º¿ä...",
+"'%-.64s' ÇÔ¼ö¸¦ ·ÎµåÇÏÁö ¸øÇß½À´Ï´Ù.",
+"'%-.64s' ÇÔ¼ö¸¦ ÃʱâÈ­ ÇÏÁö ¸øÇß½À´Ï´Ù.; %-.80s",
+"°øÀ¯ ¶óÀ̹ö·¯¸®¸¦ À§ÇÑ Æнº°¡ Á¤ÀǵǾî ÀÖÁö ¾Ê½À´Ï´Ù.",
+"'%-.64s' ÇÔ¼ö´Â ÀÌ¹Ì Á¸ÀçÇÕ´Ï´Ù.",
+"'%-.64s' °øÀ¯ ¶óÀ̹ö·¯¸®¸¦ ¿­¼ö ¾ø½À´Ï´Ù.(¿¡·¯¹øÈ£: %d %s)",
+"¶óÀ̹ö·¯¸®¿¡¼­ '%-.64s' ÇÔ¼ö¸¦ ãÀ» ¼ö ¾ø½À´Ï´Ù.",
+"'%-.64s' ÇÔ¼ö°¡ Á¤ÀǵǾî ÀÖÁö ¾Ê½À´Ï´Ù.",
+"³Ê¹« ¸¹Àº ¿¬°á¿À·ù·Î ÀÎÇÏ¿© È£½ºÆ® '%-.64s'´Â ºí¶ôµÇ¾ú½À´Ï´Ù. 'mysqladmin flush-hosts'¸¦ ÀÌ¿ëÇÏ¿© ºí¶ôÀ» ÇØÁ¦Çϼ¼¿ä",
+"'%-.64s' È£½ºÆ®´Â ÀÌ MySQL¼­¹ö¿¡ Á¢¼ÓÇÒ Çã°¡¸¦ ¹ÞÁö ¸øÇß½À´Ï´Ù.",
+"´ç½ÅÀº MySQL¼­¹ö¿¡ À͸íÀÇ »ç¿ëÀÚ·Î Á¢¼ÓÀ» Çϼ̽À´Ï´Ù.À͸íÀÇ »ç¿ëÀÚ´Â ¾ÏÈ£¸¦ º¯°æÇÒ ¼ö ¾ø½À´Ï´Ù.",
+"´ç½ÅÀº ´Ù¸¥»ç¿ëÀÚµéÀÇ ¾ÏÈ£¸¦ º¯°æÇÒ ¼ö ÀÖµµ·Ï µ¥ÀÌŸº£À̽º º¯°æ±ÇÇÑÀ» °¡Á®¾ß ÇÕ´Ï´Ù.",
+"»ç¿ëÀÚ Å×ÀÌºí¿¡¼­ ÀÏÄ¡ÇÏ´Â °ÍÀ» ãÀ» ¼ö ¾øÀ¾´Ï´Ù.",
+"ÀÏÄ¡ÇÏ´Â Rows : %ld°³ º¯°æµÊ: %ld°³ °æ°í: %ld°³",
+"»õ·Î¿î ¾²·¹µå¸¦ ¸¸µé ¼ö ¾ø½À´Ï´Ù.(¿¡·¯¹øÈ£ %d). ¸¸¾à ¿©À¯¸Þ¸ð¸®°¡ ÀÖ´Ù¸é OS-dependent¹ö±× ÀÇ ¸Þ´º¾ó ºÎºÐÀ» ã¾Æº¸½Ã¿À.",
+"Row %ld¿¡¼­ Ä®·³ Ä«¿îÆ®¿Í value Ä«¿îÅÍ¿Í ÀÏÄ¡ÇÏÁö ¾Ê½À´Ï´Ù.",
+"Å×À̺íÀ» ´Ù½Ã ¿­¼ö ¾ø±º¿ä: '%-.64s',
+"NULL °ªÀ» À߸ø »ç¿ëÇϼ̱º¿ä...",
+"regexp¿¡¼­ '%-.64s'°¡ ³µ½À´Ï´Ù.",
+"Mixing of GROUP Ä®·³s (MIN(),MAX(),COUNT()...) with no GROUP Ä®·³s is illegal if there is no GROUP BY clause",
+"»ç¿ëÀÚ '%-.32s' (È£½ºÆ® '%-.64s')¸¦ À§ÇÏ¿© Á¤ÀÇµÈ ±×·± ½ÂÀÎÀº ¾ø½À´Ï´Ù.",
+"'%-.16s' ¸í·ÉÀº ´ÙÀ½ »ç¿ëÀÚ¿¡°Ô °ÅºÎµÇ¾ú½À´Ï´Ù. : '%-.32s@%-.64s' for Å×À̺í '%-.64s'",
+"'%-.16s' ¸í·ÉÀº ´ÙÀ½ »ç¿ëÀÚ¿¡°Ô °ÅºÎµÇ¾ú½À´Ï´Ù. : '%-.32s@%-.64s' for Ä®·³ '%-.64s' in Å×À̺í '%-.64s'",
+"À߸øµÈ GRANT/REVOKE ¸í·É. ¾î¶² ±Ç¸®¿Í ½ÂÀÎÀÌ »ç¿ëµÇ¾î Áú ¼ö ÀÖ´ÂÁö ¸Þ´º¾óÀ» º¸½Ã¿À.",
+"½ÂÀÎ(GRANT)À» À§ÇÏ¿© »ç¿ëÇÑ »ç¿ëÀÚ³ª È£½ºÆ®ÀÇ °ªµéÀÌ ³Ê¹« ±é´Ï´Ù.",
+"Å×À̺í '%-64s.%s' ´Â Á¸ÀçÇÏÁö ¾Ê½À´Ï´Ù.",
+"»ç¿ëÀÚ '%-.32s'(È£½ºÆ® '%-.64s')´Â Å×À̺í '%-.64s'¸¦ »ç¿ëÇϱâ À§ÇÏ¿© Á¤ÀÇµÈ ½ÂÀÎÀº ¾ø½À´Ï´Ù. ",
+"»ç¿ëµÈ ¸í·ÉÀº ÇöÀçÀÇ MySQL ¹öÁ¯¿¡¼­´Â ÀÌ¿ëµÇÁö ¾Ê½À´Ï´Ù.",
+"SQL ±¸¹®¿¡ ¿À·ù°¡ ÀÖ½À´Ï´Ù.",
+"Áö¿¬µÈ insert ¾²·¹µå°¡ Å×À̺í %-.64sÀÇ ¿ä±¸µÈ ¶ôÅ·À» ó¸®ÇÒ ¼ö ¾ø¾ú½À´Ï´Ù.",
+"³Ê¹« ¸¹Àº Áö¿¬ ¾²·¹µå¸¦ »ç¿ëÇÏ°í ÀÖ½À´Ï´Ù.",
+"µ¥ÀÌŸº£À̽º Á¢¼ÓÀ» À§ÇÑ ¿¬°á %ld°¡ Áß´ÜµÊ : '%-.64s' »ç¿ëÀÚ: '%-.64s' (%s)",
+"'max_allowed_packet'º¸´Ù ´õÅ« ÆÐŶÀ» ¹Þ¾Ò½À´Ï´Ù.",
+"¿¬°á ÆÄÀÌÇÁ·ÎºÎÅÍ ¿¡·¯°¡ ¹ß»ýÇÏ¿´½À´Ï´Ù.",
+"fcntl() ÇÔ¼ö·ÎºÎÅÍ ¿¡·¯°¡ ¹ß»ýÇÏ¿´½À´Ï´Ù.",
+"¼ø¼­°¡ ¸ÂÁö¾Ê´Â ÆÐŶÀ» ¹Þ¾Ò½À´Ï´Ù.",
+"Åë½Å ÆÐŶÀÇ ¾ÐÃàÇØÁ¦¸¦ ÇÒ ¼ö ¾ø¾ú½À´Ï´Ù.",
+"Åë½Å ÆÐŶÀ» Àд Áß ¿À·ù°¡ ¹ß»ýÇÏ¿´½À´Ï´Ù."
+"Åë½Å ÆÐŶÀ» Àд Áß timeoutÀÌ ¹ß»ýÇÏ¿´½À´Ï´Ù.",
+"Åë½Å ÆÐŶÀ» ±â·ÏÇÏ´Â Áß ¿À·ù°¡ ¹ß»ýÇÏ¿´½À´Ï´Ù.",
+"Åë½Å ÆÐÆÂÀ» ±â·ÏÇÏ´Â Áß timeoutÀÌ ¹ß»ýÇÏ¿´½À´Ï´Ù.",
+"Result string is longer than max_allowed_packet",
+"The used table type doesn't support BLOB/TEXT columns",
+"The used table type doesn't support AUTO_INCREMENT columns",
+"INSERT DELAYED can't be used with table '%-.64s', because it is locked with LOCK TABLES",
+"Incorrect column name '%-.100s'",
+"The used table handler can't index column '%-.64s'",
+"All tables in the MERGE table are not defined identically",
+"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",
+"Result consisted of more than one row",
+"This table type requires a primary key",
+"This version of MySQL is not compiled with RAID support",
+"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",
+"You are not allowed to execute this command in a transaction",
+"Got error %d during COMMIT",
+"Got error %d during ROLLBACK",
+"Got error %d during FLUSH_LOGS",
+"Got error %d during CHECKPOINT",
+"Aborted connection %ld to db: '%-.64s' user: '%-.32s' host: `%-.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",
diff --git a/sql/share/norwegian-ny/.cvsignore b/sql/share/norwegian-ny/.cvsignore
new file mode 100755
index 00000000000..2f68f259c40
--- /dev/null
+++ b/sql/share/norwegian-ny/.cvsignore
@@ -0,0 +1 @@
+errmsg.sys
diff --git a/sql/share/norwegian-ny/errmsg.txt b/sql/share/norwegian-ny/errmsg.txt
new file mode 100644
index 00000000000..75f86103f7c
--- /dev/null
+++ b/sql/share/norwegian-ny/errmsg.txt
@@ -0,0 +1,197 @@
+/* Copyright Abandoned 1997 TCX DataKonsult AB & Monty Program KB & Detron HB
+ This file is public domain and comes with NO WARRANTY of any kind */
+
+/* Roy-Magne Mo rmo@www.hivolda.no 97 */
+
+"hashchk",
+"isamchk",
+"NEI",
+"JA",
+"Kan ikkje opprette fila '%-.64s' (Feilkode: %d)",
+"Kan ikkje opprette tabellen '%-.64s' (Feilkode: %d)",
+"Kan ikkje opprette databasen '%-.64s'. Feil %d",
+"Kan ikkje opprette databasen '%-.64s'. Databasen eksisterer",
+"Kan ikkje fjerne (drop) '%-.64s'. Databasen eksisterer ikkje",
+"Feil ved fjerning (drop) av databasen (kan ikkje slette '%-.64s', feil %d)",
+"Feil ved sletting av database (kan ikkje slette katalogen '%-.64s', feil %d)",
+"Feil ved sletting av '%-.64s' (Feilkode: %d)",
+"Kan ikkje lese posten i systemkatalogen",
+"Kan ikkje lese statusen til '%-.64s' (Feilkode: %d)",
+"Kan ikkje lese aktiv katalog(Feilkode: %d)",
+"Kan ikkje låse fila (Feilkode: %d)",
+"Kan ikkje åpne fila: '%-.64s'. (Feilkode: %d)",
+"Kan ikkje finne fila: '%-.64s' (Feilkode: %d)",
+"Kan ikkje lese katalogen '%-.64s' (Feilkode: %d)",
+"Kan ikkje skifte katalog til '%-.64s' (Feilkode: %d)",
+"Posten har vorte endra sidan den sist vart lesen '%-.64s'",
+"Ikkje meir diskplass (%s). Ventar på å få frigjort plass....",
+"Kan ikkje skrive, flere like nyklar i tabellen '%-.64s'",
+"Feil ved lukking av '%-.64s' (Feilkode: %d)",
+"Feil ved lesing av '%-.64s' (Feilkode: %d)",
+"Feil ved omdøyping av '%-.64s' til '%-.64s' (Feilkode: %d)",
+"Feil ved skriving av fila '%-.64s' (Feilkode: %d)",
+"'%-.64s' er låst mot oppdateringar",
+"Sortering avbrote",
+"View '%-.64s' eksisterar ikkje for '%-.64s'",
+"Mottok feil %d fra tabell handterar",
+"Tabell håndteraren for '%-.64s' har ikkje denne moglegheita",
+"Kan ikkje finne posten i '%-.64s'",
+"Feil informasjon i fila: '%-.64s'",
+"Tabellen '%-.64s' har feil i nykkelfila, Prøv å reparere den",
+"Gammel nykkelfil for tabellen '%-.64s'; Reparer den!",
+"'%-.64s' er skrivetryggja",
+"Ikkje meir minne. Start på nytt tenesten og prøv igjen (trengte %d bytar)",
+"Ikkje meir sorteringsminne. Auk sorteringsminnet (sorteringsbffer storleik) for tenesten",
+"Uventa slutt på fil (eof) ved lesing av fila '%-.64s' (Feilkode: %d)",
+"For mange tilkoplingar (connections)",
+"Tomt for tråd plass/minne",
+"Kan ikkje få tak i vertsnavn for di adresse",
+"Feil handtrykk (handshake)",
+"Tilgang ikkje tillate for brukar: '%-.32s@%-.64s' til databasen '%-.64s' nekta",
+"Tilgang ikke tillate for brukar: '%-.32s@%-.64s' (Brukar passord: %s)",
+"Ingen database vald",
+"Ukjent kommando",
+"Kolonne '%-.64s' kan ikkje vere null",
+"Ukjent database '%-.64s'",
+"Tabellen '%-.64s' eksisterar allereide",
+"Ukjent tabell '%-.64s'",
+"Kolonne: '%-.64s' i tabell %s er ikkje eintydig",
+"Tenar nedkopling er i gang",
+"Ukjent felt '%-.64s' i tabell %s",
+"Brukte '%-.64s' som ikkje var i group by",
+"Kan ikkje gruppere på '%-.64s'",
+"Uttrykket har summer (sum) funksjoner og kolonner i same uttrykk",
+"Kolonne telling stemmer verdi telling",
+"Identifikator '%-.64s' er for lang",
+"Feltnamnet '%-.64s' eksisterte frå før",
+"Nøkkelnamnet '%-.64s' eksisterte frå før",
+"Like verdiar '%-.64s' for nykkel %d",
+"Feil kolonne spesifikator for kolonne '%-.64s'",
+"%s attmed '%-.64s' på line %d",
+"Førespurnad var tom",
+"Ikkje unikt tabell/alias: '%-.64s'",
+"Ugyldig standardverdi for '%-.64s'",
+"Fleire primærnyklar spesifisert",
+"For mange nykler spesifisert. Maks %d nyklar tillatt",
+"For mange nykkeldelar spesifisert. Maks %d delar tillatt",
+"Spesifisert nykkel var for lang. Maks nykkellengde er %d",
+"Nykkel kolonne '%-.64s' eksiterar ikkje i tabellen",
+"Blob kolonne '%-.64s' kan ikkje brukast ved spesifikasjon av nyklar",
+"For stor nykkellengde for felt '%-.64s' (maks = %d). Bruk BLOB istadenfor",
+"Bare eitt auto felt kan være definert som nøkkel.",
+"%s: klar for tilkoblingar\n",
+"%s: Normal nedkopling\n",
+"%s: Oppdaga signal %d. Avsluttar!\n",
+"%s: Nedkopling komplett\n",
+"%s: Påtvinga avslutning av tråd %ld brukar: '%-.64s'\n",
+"Kan ikkje opprette IP socket",
+"Tabellen '%-.64s' har ingen index som den som er brukt i CREATE INDEX. Oprett tabellen på nytt",
+"Felt skiljer argumenta er ikkje som venta, sjå dokumentasjonen",
+"Ein kan ikkje bruke faste feltlengder med BLOB. Vennlisgt bruk 'fields terminated by'.",
+"Filen '%-.64s' må være i database-katalogen for å være lesbar for alle",
+"Filen '%-.64s' eksisterte allereide",
+"Poster: %ld Fjerna: %ld Hoppa over: %ld Åtvaringar: %ld",
+"Poster: %ld Like: %ld",
+"Feil delnykkel. Den brukte delnykkelen er ikkje ein streng eller den oppgitte lengda er lengre enn nykkellengden",
+"Ein kan ikkje slette alle felt med ALTER TABLE. Bruk DROP TABLE istadenfor.",
+"Kan ikkje DROP '%-.64s'. Undersøk om felt/nøkkel eksisterar.",
+"Postar: %ld Like: %ld Åtvaringar: %ld",
+"INSERT TABLE '%-.64s' er ikkje tillate i FROM tabell liste",
+"Ukjent tråd id: %lu",
+"Du er ikkje eigar av tråd %lu",
+"Ingen tabellar i bruk",
+"For mange tekststrengar felt %s og SET",
+"Kan ikkje lage unikt loggfilnavn %s.(1-999)\n",
+"Tabellen '%-.64s' var låst med READ lås og kan ikkje oppdaterast",
+"Tabellen '%-.64s' var ikkje låst med LOCK TABLES",
+"Blob feltet '%-.64s' kan ikkje ha ein standard verdi",
+"Ugyldig database namn '%-.64s'",
+"Ugyldig tabell namn '%-.64s'",
+"SELECT ville undersøkje for mange postar og ville sannsynligvis ta veldig lang tid. Undersøk WHERE klausulen og bruk SET OPTION SQL_BIG_SELECTS=1 om SELECTen er korrekt"
+"Ukjend feil",
+"Ukjend prosedyre %s",
+"Feil parameter tal til prosedyra %s",
+"Feil parameter til prosedyra %s",
+"Ukjend tabell '%-.64s' i %s",
+"Feltet '%-.64s' er spesifisert to gangar",
+"Invalid use of group function",
+"Table '%-.64s' uses a extension that doesn't exist in this MySQL version",
+"A table must have at least 1 column",
+"The table '%-.64s' is full",
+"Unknown character set: '%-.64s'",
+"Too many tables. MySQL can only use %d tables in a join",
+"Too many fields",
+"Too big row size. The maximum row size, not counting blobs, is %d. You have to change some fields to blobs",
+"Thread stack overrun: Used: %ld of a %ld stack. Use 'mysqld -O thread_stack=#' to specify a bigger stack if needed",
+"Cross dependency found in OUTER JOIN. Examine your ON conditions",
+"Column '%-.32s' is used with UNIQUE or INDEX but is not defined as NOT NULL",
+"Can't load function '%-.64s'",
+"Can't initialize function '%-.64s'; %-.80s",
+"No paths allowed for shared library",
+"Function '%-.64s' already exist",
+"Can't open shared library '%-.64s' (errno: %d %s)",
+"Can't find function '%-.64s' in library'",
+"Function '%-.64s' is not defined",
+"Host '%-.64s' is blocked because of many connection errors. Unblock with 'mysqladmin flush-hosts'",
+"Host '%-.64s' is not allowed to connect to this MySQL server",
+"You are using MySQL as an anonymous users and anonymous users are not allowed to change passwords",
+"You must have privileges to update tables in the mysql database to be able to change passwords for others",
+"Can't find any matching row in the user table",
+"Rows matched: %ld Changed: %ld Warnings: %ld",
+"Can't create a new thread (errno %d). If you are not out of available memory you can consult the manual for any possible OS dependent bug",
+"Column count doesn't match value count at row %ld",
+"Can't reopen table: '%-.64s',
+"Invalid use of NULL value",
+"Got error '%-.64s' from regexp",
+"Mixing of GROUP columns (MIN(),MAX(),COUNT()...) with no GROUP columns is illegal if there is no GROUP BY clause",
+"There is no such grant defined for user '%-.32s' on host '%-.64s'",
+"%-.16s command denied to user: '%-.32s@%-.64s' for table '%-.64s'",
+"%-.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",
+"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",
+"Delayed insert thread couldn't get requested lock for table %-.64s",
+"Too many delayed threads in use",
+"Aborted connection %ld to db: '%-.64s' user: '%-.64s' (%s)",
+"Got a packet bigger than 'max_allowed_packet'",
+"Got a read error from the connection pipe",
+"Got an error from fcntl()",
+"Got packets out of order",
+"Couldn't uncompress communication packet",
+"Got an error reading communication packets"
+"Got timeout reading communication packets",
+"Got an error writing communication packets",
+"Got timeout writing communication packets",
+"Result string is longer than max_allowed_packet",
+"The used table type doesn't support BLOB/TEXT columns",
+"The used table type doesn't support AUTO_INCREMENT columns",
+"INSERT DELAYED can't be used with table '%-.64s', because it is locked with LOCK TABLES",
+"Incorrect column name '%-.100s'",
+"The used table handler can't index column '%-.64s'",
+"All tables in the MERGE table are not defined identically",
+"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",
+"Result consisted of more than one row",
+"This table type requires a primary key",
+"This version of MySQL is not compiled with RAID support",
+"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",
+"You are not allowed to execute this command in a transaction",
+"Got error %d during COMMIT",
+"Got error %d during ROLLBACK",
+"Got error %d during FLUSH_LOGS",
+"Got error %d during CHECKPOINT",
+"Aborted connection %ld to db: '%-.64s' user: '%-.32s' host: `%-.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",
diff --git a/sql/share/norwegian/.cvsignore b/sql/share/norwegian/.cvsignore
new file mode 100755
index 00000000000..2f68f259c40
--- /dev/null
+++ b/sql/share/norwegian/.cvsignore
@@ -0,0 +1 @@
+errmsg.sys
diff --git a/sql/share/norwegian/errmsg.txt b/sql/share/norwegian/errmsg.txt
new file mode 100644
index 00000000000..7ad97882398
--- /dev/null
+++ b/sql/share/norwegian/errmsg.txt
@@ -0,0 +1,197 @@
+/* Copyright Abandoned 1997 TCX DataKonsult AB & Monty Program KB & Detron HB
+ This file is public domain and comes with NO WARRANTY of any kind */
+
+/* Roy-Magne Mo rmo@www.hivolda.no 97 */
+
+"hashchk",
+"isamchk",
+"NEI",
+"JA",
+"Kan ikke opprette fila '%-.64s' (Feilkode: %d)",
+"Kan ikke opprette tabellen '%-.64s' (Feilkode: %d)",
+"Kan ikke opprette databasen '%-.64s'. Feil %d",
+"Kan ikke opprette databasen '%-.64s'. Databasen eksisterer",
+"Kan ikke fjerne (drop) '%-.64s'. Databasen eksisterer ikke",
+"Feil ved fjerning (drop) av databasen (kan ikke slette '%-.64s', feil %d)",
+"Feil ved sletting av database (kan ikke slette katalogen '%-.64s', feil %d)",
+"Feil ved sletting av '%-.64s' (Feilkode: %d)",
+"Kan ikke lese posten i systemkatalogen",
+"Kan ikke lese statusen til '%-.64s' (Feilkode: %d)",
+"Kan ikke lese aktiv katalog(Feilkode: %d)",
+"Kan ikke låse fila (Feilkode: %d)",
+"Kan ikke åpne fila: '%-.64s'. (Feilkode: %d)",
+"Kan ikke finne fila: '%-.64s' (Feilkode: %d)",
+"Kan ikke lese katalogen '%-.64s' (Feilkode: %d)",
+"Kan ikke skifte katalog til '%-.64s' (Feilkode: %d)",
+"Posten har blitt endret siden den ble lest '%-.64s'",
+"Ikke mer diskplass (%s). Venter på å få frigjort plass....",
+"Kan ikke skrive, flere like nøkler i tabellen '%-.64s'",
+"Feil ved lukking av '%-.64s' (Feilkode: %d)",
+"Feil ved lesing av '%-.64s' (Feilkode: %d)",
+"Feil ved omdøping av '%-.64s' til '%-.64s' (Feilkode: %d)",
+"Feil ved skriving av fila '%-.64s' (Feilkode: %d)",
+"'%-.64s' er låst mot oppdateringer",
+"Sortering avbrutt",
+"View '%-.64s' eksisterer ikke for '%-.64s'",
+"Mottok feil %d fra tabell håndterer",
+"Tabell håndtereren for '%-.64s' har ikke denne muligheten",
+"Kan ikke finne posten i '%-.64s'",
+"Feil informasjon i filen: '%-.64s'",
+"Tabellen '%-.64s' har feil i nøkkelfilen, forsøk å reparer den",
+"Gammel nøkkelfil for tabellen '%-.64s'; Reparer den!",
+"'%-.64s' er skrivebeskyttet",
+"Ikke mer minne. Star på nytt tjenesten og prøv igjen (trengte %d byter)",
+"Ikke mer sorteringsminne. Øk sorteringsminnet (sort buffer size) for tjenesten",
+"Uventet slutt på fil (eof) ved lesing av filen '%-.64s' (Feilkode: %d)",
+"For mange tilkoblinger (connections)",
+"Tomt for tråd plass/minne",
+"Kan ikke få tak i vertsnavn for din adresse",
+"Feil håndtrykk (handshake)",
+"Tilgang nektet for bruker: '%-.32s@%-.64s' til databasen '%-.64s' nektet",
+"Tilgang nektet for bruker: '%-.32s@%-.64s' (Bruker passord: %s)",
+"Ingen database valgt",
+"Ukjent kommando",
+"Kolonne '%-.64s' kan ikke vere null",
+"Ukjent database '%-.64s'",
+"Tabellen '%-.64s' eksisterer allerede",
+"Ukjent tabell '%-.64s'",
+"Felt: '%-.64s' i tabell %s er ikke entydig",
+"Database nedkobling er i gang",
+"Ukjent kolonne '%-.64s' i tabell %s",
+"Brukte '%-.64s' som ikke var i group by",
+"Kan ikke gruppere på '%-.64s'",
+"Uttrykket har summer (sum) funksjoner og kolonner i samme uttrykk",
+"Felt telling stemmer verdi telling",
+"Identifikator '%-.64s' er for lang",
+"Feltnavnet '%-.64s' eksisterte fra før",
+"Nøkkelnavnet '%-.64s' eksisterte fra før",
+"Like verdier '%-.64s' for nøkkel %d",
+"Feil kolonne spesifikator for felt '%-.64s'",
+"%s nær '%-.64s' på linje %d",
+"Forespørsel var tom",
+"Ikke unikt tabell/alias: '%-.64s'",
+"Ugyldig standardverdi for '%-.64s'",
+"Fleire primærnøkle spesifisert",
+"For mange nøkler spesifisert. Maks %d nøkler tillatt",
+"For mange nøkkeldeler spesifisert. Maks %d deler tillatt",
+"Spesifisert nøkkel var for lang. Maks nøkkellengde er is %d",
+"Nøkkel felt '%-.64s' eksiterer ikke i tabellen",
+"Blob felt '%-.64s' kan ikke brukes ved spesifikasjon av nøkler",
+"For stor nøkkellengde for kolonne '%-.64s' (maks = %d). Bruk BLOB istedenfor",
+"Bare ett auto felt kan være definert som nøkkel.",
+"%s: klar for tilkoblinger\n",
+"%s: Normal avslutning\n",
+"%s: Oppdaget signal %d. Avslutter!\n",
+"%s: Avslutning komplett\n",
+"%s: Påtvinget avslutning av tråd %ld bruker: '%-.64s'\n",
+"Kan ikke opprette IP socket",
+"Tabellen '%-.64s' har ingen index som den som er brukt i CREATE INDEX. Gjenopprett tabellen",
+"Felt skiller argumentene er ikke som forventet, se dokumentasjonen",
+"En kan ikke bruke faste feltlengder med BLOB. Vennlisgt bruk 'fields terminated by'.",
+"Filen '%-.64s' må være i database-katalogen for å være lesbar for alle",
+"Filen '%-.64s' eksisterte allerede",
+"Poster: %ld Fjernet: %ld Hoppet over: %ld Advarsler: %ld",
+"Poster: %ld Like: %ld",
+"Feil delnøkkel. Den brukte delnøkkelen er ikke en streng eller den oppgitte lengde er lengre enn nøkkel lengden",
+"En kan ikke slette alle felt med ALTER TABLE. Bruk DROP TABLE isteden.",
+"Kan ikke DROP '%-.64s'. Undersøk om felt/nøkkel eksisterer.",
+"Poster: %ld Like: %ld Advarsler: %ld",
+"INSERT TABLE '%-.64s' er ikke tillatt i FROM tabell liste",
+"Ukjent tråd id: %lu",
+"Du er ikke eier av tråden %lu",
+"Ingen tabeller i bruk",
+"For mange tekststrenger kolonne %s og SET",
+"Kan ikke lage unikt loggfilnavn %s.(1-999)\n",
+"Tabellen '%-.64s' var låst med READ lås og kan ikke oppdateres",
+"Tabellen '%-.64s' var ikke låst med LOCK TABLES",
+"Blob feltet '%-.64s' kan ikke ha en standard verdi",
+"Ugyldig database navn '%-.64s'",
+"Ugyldig tabell navn '%-.64s'",
+"SELECT ville undersøke for mange poster og ville sannsynligvis ta veldig lang tid. Undersøk WHERE klausulen og bruk SET OPTION SQL_BIG_SELECTS=1 om SELECTen er korrekt"
+"Ukjent feil",
+"Ukjent prosedyre %s",
+"Feil parameter antall til prosedyren %s",
+"Feil parametre til prosedyren %s",
+"Ukjent tabell '%-.64s' i %s",
+"Feltet '%-.64s' er spesifisert to ganger",
+"Invalid use of group function",
+"Table '%-.64s' uses a extension that doesn't exist in this MySQL version",
+"A table must have at least 1 column",
+"The table '%-.64s' is full",
+"Unknown character set: '%-.64s'",
+"Too many tables. MySQL can only use %d tables in a join",
+"Too many fields",
+"Too big row size. The maximum row size, not counting blobs, is %d. You have to change some fields to blobs",
+"Thread stack overrun: Used: %ld of a %ld stack. Use 'mysqld -O thread_stack=#' to specify a bigger stack if needed",
+"Cross dependency found in OUTER JOIN. Examine your ON conditions",
+"Column '%-.32s' is used with UNIQUE or INDEX but is not defined as NOT NULL",
+"Can't load function '%-.64s'",
+"Can't initialize function '%-.64s'; %-.80s",
+"No paths allowed for shared library",
+"Function '%-.64s' already exist",
+"Can't open shared library '%-.64s' (errno: %d %s)",
+"Can't find function '%-.64s' in library'",
+"Function '%-.64s' is not defined",
+"Host '%-.64s' is blocked because of many connection errors. Unblock with 'mysqladmin flush-hosts'",
+"Host '%-.64s' is not allowed to connect to this MySQL server",
+"You are using MySQL as an anonymous users and anonymous users are not allowed to change passwords",
+"You must have privileges to update tables in the mysql database to be able to change passwords for others",
+"Can't find any matching row in the user table",
+"Rows matched: %ld Changed: %ld Warnings: %ld",
+"Can't create a new thread (errno %d). If you are not out of available memory you can consult the manual for any possible OS dependent bug",
+"Column count doesn't match value count at row %ld",
+"Can't reopen table: '%-.64s',
+"Invalid use of NULL value",
+"Got error '%-.64s' from regexp",
+"Mixing of GROUP columns (MIN(),MAX(),COUNT()...) with no GROUP columns is illegal if there is no GROUP BY clause",
+"There is no such grant defined for user '%-.32s' on host '%-.64s'",
+"%-.16s command denied to user: '%-.32s@%-.64s' for table '%-.64s'",
+"%-.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",
+"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",
+"Delayed insert thread couldn't get requested lock for table %-.64s",
+"Too many delayed threads in use",
+"Aborted connection %ld to db: '%-.64s' user: '%-.64s' (%s)",
+"Got a packet bigger than 'max_allowed_packet'",
+"Got a read error from the connection pipe",
+"Got an error from fcntl()",
+"Got packets out of order",
+"Couldn't uncompress communication packet",
+"Got an error reading communication packets"
+"Got timeout reading communication packets",
+"Got an error writing communication packets",
+"Got timeout writing communication packets",
+"Result string is longer than max_allowed_packet",
+"The used table type doesn't support BLOB/TEXT columns",
+"The used table type doesn't support AUTO_INCREMENT columns",
+"INSERT DELAYED can't be used with table '%-.64s', because it is locked with LOCK TABLES",
+"Incorrect column name '%-.100s'",
+"The used table handler can't index column '%-.64s'",
+"All tables in the MERGE table are not defined identically",
+"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",
+"Result consisted of more than one row",
+"This table type requires a primary key",
+"This version of MySQL is not compiled with RAID support",
+"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",
+"You are not allowed to execute this command in a transaction",
+"Got error %d during COMMIT",
+"Got error %d during ROLLBACK",
+"Got error %d during FLUSH_LOGS",
+"Got error %d during CHECKPOINT",
+"Aborted connection %ld to db: '%-.64s' user: '%-.32s' host: `%-.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",
diff --git a/sql/share/polish/errmsg.sys b/sql/share/polish/errmsg.sys
new file mode 100644
index 00000000000..e3879e04810
--- /dev/null
+++ b/sql/share/polish/errmsg.sys
Binary files differ
diff --git a/sql/share/polish/errmsg.txt b/sql/share/polish/errmsg.txt
new file mode 100644
index 00000000000..b73631afed1
--- /dev/null
+++ b/sql/share/polish/errmsg.txt
@@ -0,0 +1,199 @@
+/* Copyright Abandoned 1997 TCX DataKonsult AB & Monty Program KB & Detron HB
+ This file is public domain and comes with NO WARRANTY of any kind
+
+ Changed by Jaroslaw Lewandowski <jotel@itnet.com.pl>
+ Charset ISO-8859-2
+*/
+
+"hashchk",
+"isamchk",
+"TAK",
+"NIE",
+"Nie mo¿na stworzyæ pliku '%-.64s' (Kod b³êdu: %d)",
+"Nie mo¿na stworzyæ tabeli '%-.64s' (Kod b³êdu: %d)",
+"Nie mo¿na stworzyæ bazy danych '%-.64s'. B³?d %d",
+"Nie mo¿na stworzyæ bazy danych '%-.64s'. Baza danych ju¿ istnieje",
+"Nie mo¿na usun?æ bazy danych '%-.64s'. Baza danych nie istnieje",
+"B³?d podczas usuwania bazy danych (nie mo¿na usun?æ '%-.64s', b³?d %d)",
+"B³?d podczas usuwania bazy danych (nie mo¿na wykonaæ rmdir '%-.64s', b³?d %d)",
+"B³?d podczas usuwania '%-.64s' (Kod b³êdu: %d)",
+"Nie mo¿na odczytaæ rekordu z tabeli systemowej",
+"Nie mo¿na otrzymaæ statusu '%-.64s' (Kod b³êdu: %d)",
+"Nie mo¿na rozpoznaæ aktualnego katalogu (Kod b³êdu: %d)",
+"Nie mo¿na zablokowaæ pliku (Kod b³êdu: %d)",
+"Nie mo¿na otworzyæ pliku: '%-.64s'. (Kod b³êdu: %d)",
+"Nie mo¿na znale¥æ pliku: '%-.64s' (Kod b³êdu: %d)",
+"Nie mo¿na odczytaæ katalogu '%-.64s' (Kod b³êdu: %d)",
+"Nie mo¿na zmieniæ katalogu na '%-.64s' (Kod b³êdu: %d)",
+"Rekord zosta³ zmieniony od ostaniego odczytania z tabeli '%-.64s'",
+"Dysk pe³ny (%s). Oczekiwanie na zwolnienie miejsca....",
+"Nie mo¿na zapisaæ, powtórzone klucze w tabeli '%-.64s'",
+"B³?d podczas zamykania '%-.64s' (Kod b³êdu: %d)",
+"B³?d podczas odczytu pliku '%-.64s' (Kod b³êdu: %d)",
+"B³?d podczas zmieniania nazwy '%-.64s' na '%-.64s' (Kod b³êdu: %d)",
+"B³?d podczas zapisywania pliku '%-.64s' (Kod b³êdu: %d)",
+"'%-.64s' jest zablokowany na wypadek zmian",
+"Sortowanie przerwane",
+"Widok '%-.64s' nie istnieje dla '%-.64s'",
+"Otrzymano b³?d %d z obs³ugi tabeli",
+"Obs³uga tabeli '%-.64s' nie posiada tej opcji",
+"Nie mo¿na znale¥æ rekordu w '%-.64s'",
+"Niew³a?ciwa informacja w pliku: '%-.64s'",
+"Niew³a?ciwy plik kluczy dla tabeli: '%-.64s'. Spróbuj go naprawiæ",
+"Plik kluczy dla tabeli '%-.64s' jest starego typu; Napraw go!",
+"'%-.64s' jest tylko do odczytu",
+"Zbyt ma³o pamiêci. Uruchom ponownie demona i spróbuj ponownie (potrzeba %d bajtów)",
+"Zbyt ma³o pamiêci dla sortowania. Zwiêksz wielko?æ bufora demona dla sortowania",
+"Nieoczekiwany 'eof' napotkany podczas czytania z pliku '%-.64s' (Kod b³êdu: %d)",
+"Zbyt wiele po³?czeñ",
+"Zbyt ma³o miejsca/pamiêci dla w?tku",
+"Nie mo¿na otrzymaæ nazwy hosta dla twojego adresu",
+"Z³y uchwyt(handshake)",
+"Access denied for user: '%-.32s@%-.64s' to database '%-.64s'",
+"Access denied for user: '%-.32s@%-.64s' (Using password: %s)",
+"Nie wybrano ¿adnej bazy danych",
+"Nieznana komenda",
+"Kolumna '%-.64s' nie mo¿e byæ null",
+"Nieznana baza danych '%-.64s'",
+"Tabela '%-.64s' ju¿ istnieje",
+"Nieznana tabela '%-.64s'",
+"Kolumna: '%-.64s' w %s jest dwuznaczna",
+"Trwa koñczenie dzia³ania serwera",
+"Nieznana kolumna '%-.64s' w %s",
+"U¿yto '%-.64s' bez umieszczenia w group by",
+"Nie mo¿na grupowaæ po '%-.64s'",
+"Zapytanie ma funkcje sumuj?ce i kolumny w tym samym zapytaniu",
+"Liczba kolumn nie odpowiada liczbie warto?ci",
+"Nazwa identyfikatora '%-.64s' jest zbyt d³uga",
+"Powtórzona nazwa kolumny '%-.64s'",
+"Powtórzony nazwa klucza '%-.64s'",
+"Powtórzone wyst?pienie '%-.64s' dla klucza %d",
+"B³êdna specyfikacja kolumny dla kolumny '%-.64s'",
+"%s obok '%-.64s' w linii %d",
+"Zapytanie by³o puste",
+"Tabela/alias nie s? unikalne: '%-.64s'",
+"Niew³a?ciwa warto?æ domy?lna dla '%-.64s'",
+"Zdefiniowano wiele kluczy podstawowych",
+"Okre?lono zbyt wiele kluczy. Dostêpnych jest maksymalnie %d kluczy",
+"Okre?lono zbyt wiele czê?ci klucza. Dostêpnych jest maksymalnie %d czê?ci",
+"Zdefinowany klucz jest zbyt d³ugi. Maksymaln? d³ugo?ci? klucza jest %d",
+"Kolumna '%-.64s' zdefiniowana w kluczu nie istnieje w tabeli",
+"Kolumna typu Blob '%-.64s' nie mo¿e byæ u¿yta w specyfikacji klucza",
+"Zbyt du¿a d³ugo?æ kolumny '%-.64s' (maks. = %d). W zamian u¿yj typu BLOB",
+"W tabeli mo¿e byæ tylko jedno pole auto i musi ono byæ zdefiniowane jako klucz",
+"%s: gotowe do po³?czenia\n",
+"%s: Standardowe zakoñczenie dzia³ania\n",
+"%s: Otrzymano sygna³ %d. Koñczenie dzia³ania!\n",
+"%s: Zakoñczenie dzia³ania wykonane\n",
+"%s: Wymuszenie zamkniêcia w?tku %ld u¿ytkownik: '%-.64s'\n",
+"Nie mo¿na stworzyæ socket'u IP",
+"Tabela '%-.64s' nie ma indeksu takiego jak w CREATE INDEX. Stwórz tabelê",
+"Nie oczekiwano separatora. Sprawd¥ podrêcznik","
+"Nie mo¿na u¿yæ sta³ej d³ugo?ci wiersza z polami typu BLOB. U¿yj 'fields terminated by'.",
+"Plik '%-.64s' musi znajdowaæ sie w katalogu bazy danych lub mieæ prawa czytania przez wszystkich",
+"Plik '%-.64s' ju¿ istnieje",
+"Recordów: %ld Usuniêtych: %ld Pominiêtych: %ld Ostrze¿eñ: %ld",
+"Rekordów: %ld Duplikatów: %ld",
+"B³êdna podczê?æ klucza. U¿yta czê?æ klucza nie jest ³añcuchem lub u¿yta d³ugo?æ jest wiêksza ni¿ czê?æ klucza",
+"Nie mo¿na usun?æ wszystkich pól wykorzystuj?c ALTER TABLE. W zamian u¿yj DROP TABLE",
+"Nie mo¿na wykonaæ operacji DROP '%-.64s'. Sprawd¥, czy to pole/klucz istnieje",
+"Rekordów: %ld Duplikatów: %ld Ostrze¿eñ: %ld",
+"Operacja INSERT TABLE '%-.64s' nie jest dozwolona w li?cie tabel w FROM",
+"Nieznany identyfikator w?tku: %lu",
+"Nie jeste? w³a?cicielem w?tku %lu",
+"Nie ma ¿adej u¿ytej tabeli",
+"Zbyt wiele ³añcuchów dla kolumny %s i polecenia SET",
+"Nie mo¿na stworzyæ unikalnej nazwy pliku z logiem %s.(1-999)\n",
+"Tabela '%-.64s' zosta³a zablokowana przez READ i nie mo¿e zostaæ zaktualizowana",
+"Tabela '%-.64s' nie zosta³a zablokowana poleceniem LOCK TABLES",
+"Pole typu blob '%-.64s' nie mo¿e mieæ domy?lnej warto?ci",
+"Niedozwolona nazwa bazy danych '%-.64s'",
+"Niedozwolona nazwa tabeli '%-.64s'...",
+"Operacja SELECT bêdzie dotyczy³a zbyt wielu rekordów i prawdopodobnie zajmie bardzo du¿o czasu. Sprawd¥ warunek WHERE i u¿yj SQL_OPTION BIG_SELECTS=1 je?li operacja SELECT jest poprawna",
+"Unknown error",
+"Unkown procedure %s",
+"Wrong parameter count to procedure %s",
+"Wrong parameters to procedure %s",
+"Unknown table '%-.64s' in %s",
+"Field '%-.64s' specified twice",
+"Invalid use of group function",
+"Table '%-.64s' uses a extension that doesn't exist in this MySQL version",
+"A table must have at least 1 column",
+"The table '%-.64s' is full",
+"Unknown character set: '%-.64s'",
+"Too many tables. MySQL can only use %d tables in a join",
+"Too many fields",
+"Too big row size. The maximum row size, not counting blobs, is %d. You have to change some fields to blobs",
+"Thread stack overrun: Used: %ld of a %ld stack. Use 'mysqld -O thread_stack=#' to specify a bigger stack if needed",
+"Cross dependency found in OUTER JOIN. Examine your ON conditions",
+"Column '%-.32s' is used with UNIQUE or INDEX but is not defined as NOT NULL",
+"Can't load function '%-.64s'",
+"Can't initialize function '%-.64s'; %-.80s",
+"No paths allowed for shared library",
+"Function '%-.64s' already exist",
+"Can't open shared library '%-.64s' (errno: %d %s)",
+"Can't find function '%-.64s' in library'",
+"Function '%-.64s' is not defined",
+"Host '%-.64s' is blocked because of many connection errors. Unblock with 'mysqladmin flush-hosts'",
+"Host '%-.64s' is not allowed to connect to this MySQL server",
+"You are using MySQL as an anonymous users and anonymous users are not allowed to change passwords",
+"You must have privileges to update tables in the mysql database to be able to change passwords for others",
+"Can't find any matching row in the user table",
+"Rows matched: %ld Changed: %ld Warnings: %ld",
+"Can't create a new thread (errno %d). If you are not out of available memory you can consult the manual for any possible OS dependent bug",
+"Column count doesn't match value count at row %ld",
+"Can't reopen table: '%-.64s',
+"Invalid use of NULL value",
+"Got error '%-.64s' from regexp",
+"Mixing of GROUP columns (MIN(),MAX(),COUNT()...) with no GROUP columns is illegal if there is no GROUP BY clause",
+"There is no such grant defined for user '%-.32s' on host '%-.64s'",
+"%-.16s command denied to user: '%-.32s@%-.64s' for table '%-.64s'",
+"%-.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",
+"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",
+"Delayed insert thread couldn't get requested lock for table %-.64s",
+"Too many delayed threads in use",
+"Aborted connection %ld to db: '%-.64s' user: '%-.64s' (%s)",
+"Got a packet bigger than 'max_allowed_packet'",
+"Got a read error from the connection pipe",
+"Got an error from fcntl()",
+"Got packets out of order",
+"Couldn't uncompress communication packet",
+"Got an error reading communication packets"
+"Got timeout reading communication packets",
+"Got an error writing communication packets",
+"Got timeout writing communication packets",
+"Result string is longer than max_allowed_packet",
+"The used table type doesn't support BLOB/TEXT columns",
+"The used table type doesn't support AUTO_INCREMENT columns",
+"INSERT DELAYED can't be used with table '%-.64s', because it is locked with LOCK TABLES",
+"Incorrect column name '%-.100s'",
+"The used table handler can't index column '%-.64s'",
+"All tables in the MERGE table are not defined identically",
+"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",
+"Result consisted of more than one row",
+"This table type requires a primary key",
+"This version of MySQL is not compiled with RAID support",
+"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",
+"You are not allowed to execute this command in a transaction",
+"Got error %d during COMMIT",
+"Got error %d during ROLLBACK",
+"Got error %d during FLUSH_LOGS",
+"Got error %d during CHECKPOINT",
+"Aborted connection %ld to db: '%-.64s' user: '%-.32s' host: `%-.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",
diff --git a/sql/share/portuguese/errmsg.sys b/sql/share/portuguese/errmsg.sys
new file mode 100644
index 00000000000..e9d5d4b9cec
--- /dev/null
+++ b/sql/share/portuguese/errmsg.sys
Binary files differ
diff --git a/sql/share/portuguese/errmsg.txt b/sql/share/portuguese/errmsg.txt
new file mode 100644
index 00000000000..466a2cadfd4
--- /dev/null
+++ b/sql/share/portuguese/errmsg.txt
@@ -0,0 +1,195 @@
+/* Copyright Abandoned 1997 TCX DataKonsult AB & Monty Program KB & Detron HB
+ This file is public domain and comes with NO WARRANTY of any kind */
+
+"hashchk",
+"isamchk",
+"NO",
+"YES",
+"Nao consegui criar o arquivo '%-.64s' (Erro: %d)",
+"Nao consegui criar a tabela '%-.64s' (Erro: %d)",
+"Nao consegui criar o banco de dados '%-.64s'. Erro %d",
+"Nao consegui criar o banco de dados '%-.64s'. Este banco ja existe",
+"Nao consegui deletar o banco de dados '%-.64s'. Este banco nao existe",
+"Erro deletando o banco de dados(Nao foi possivel deletar '%-.64s', erro %d)",
+"Erro deletando o banco de dados(Nao foi possivel remover o diretorio '%-.64s', erro %d)",
+"Erro ao deletar '%-.64s' (Erro: %d)",
+"Nao foi possivel ler o registro na tabela do sistema",
+"Nao foi possivel obter o status de '%-.64s' (Erro: %d)",
+"Nao foi possivel obter o diretorio corrente (Erro: %d)",
+"Nao foi possivel travar o arquivo (Erro: %d)",
+"Nao foi possivel abrir arquivo: '%-.64s'. (Erro: %d)",
+"Nao foi possivel encontrar arquivo: '%-.64s' (Erro: %d)",
+"Nao foi possivel ler o diretorio de '%-.64s' (Erro: %d)",
+"Nao foi possivel ir para o diretorio '%-.64s' (Erro: %d)",
+"Registro alterado apos a ultima leitura da tabela '%-.64s'",
+"Disco cheio (%s). Aguardando espaco livre....",
+"Nao foi possivel gravar, chave duplicada na tabela '%-.64s'",
+"Erro ao fechar '%-.64s' (Erro: %d)",
+"Erro lendo arquivo '%-.64s' (Erro: %d)",
+"Erro ao renomear '%-.64s' to '%-.64s' (Erro: %d)",
+"Error gravando arquivo '%-.64s' (Erro: %d)",
+"'%-.64s' esta travado contra alteracoes",
+"Ordenacao cancelada",
+"Visao '%-.64s' nao existe para '%-.64s'",
+"Erro %d do manipulador de tabelas",
+"Manipulador da tabela '%-.64s' nao suporta esta opcao",
+"Nao foi possivel encontrar o registro em '%-.64s'",
+"Informacao invalida no arquivo: '%-.64s'",
+"Arquivo de indice invalido na tabela: '%-.64s'. Tente conserta-lo!",
+"Arquivo de indice destaualizado na tabela '%-.64s'; Conserte-o!",
+"'%-.64s' esta disponivel somente para leitura",
+"Sem memoria. Renicie o programa e tente novamente (Necessita de %d bytes)",
+"Sem memoria para ordenacao. Aumente o espaco de memoria para ordenacao.",
+"Fim de arquivo inesperado enquanto lendo o arquivo '%-.64s' (Erro: %d)",
+"Excesso de conexoes",
+"Thread sem memoria disponivel",
+"Nao foi possivel obter o nome da maquina para este endereco IP",
+"Comunicacao invalida",
+"Acesso negado ao usuario : '%-.32s@%-.64s' ao banco de dados '%-.64s'",
+"Acesso negado ao usuario: '%-.32s@%-.64s' (usando a senha: %s)",
+"Nenhum banco de dados selecionado",
+"Comando desconhecido",
+"Coluna '%-.64s' nao pode ser vazia",
+"Banco de dados '%-.64s' desconhecido",
+"Tabela '%-.64s' ja existe",
+"Tabela '%-.64s' desconhecida",
+"Coluna: '%-.64s' em %s e ambigua",
+"Finalizacao do servidor em andamento",
+"Coluna '%-.64s' desconhecida em %s",
+"'%-.64s' utilizado nao esta em 'group by'",
+"Nao foi possivel agrupar em '%-.64s'",
+"Clausula contem funcoes de soma e colunas juntos",
+"Contagem de colunas nao confere com a contagem de valores",
+"Nome do identificador '%-.64s' muito grande",
+"Nome da coluna '%-.64s' duplicado",
+"Nome da chave '%-.64s' duplicado",
+"Inclusao de '%-.64s' duplicada para a chave %d",
+"Especificador de coluna invalido para a coluna '%-.64s'",
+"%s proximo de '%-.64s' a linha %d",
+"Selecao vazia",
+"Tabela/alias nao e unica: '%-.64s'",
+"Valor padrao invalido para '%-.64s'",
+"Mais de uma chave primaria definida",
+"Muitas chaves definidas. O maximo permitido sao %d chaves",
+"Muitas partes de chave definidas. O maximo permitido sao %d partes",
+"Chave especificada e muito longa. O comprimento maximo permitido e %d",
+"Coluna chave '%-.64s' nao existe na tabela",
+"Coluna binaria '%-.64s' nao pode ser utilizada na definicao de chaves",
+"Comprimento da coluna '%-.64s' muito grande(max = %d). Utilize o campo binario",
+"Somente e permitido um campo auto incrementado, e ele deve ser chave da tabela",
+"%s: pronto para conexoes\n",
+"%s: Finalizacao concluida normalmente\n",
+"%s: Recebeu o sinal %d. Cancelando!\n",
+"%s: Finalizacao concluida\n",
+"%s: Forcando a finalizacao da tarefa %ld usuario: '%-.64s'\n",
+"Nao foi possivel criar o socket IP",
+"Tabela '%-.64s' nao possui um indice criado por CREATE INDEX. Recrie a tabela",
+"O separador de campos nao esta conforme esperado. Confira no manual","
+"Nao e possivel utilizar comprimento de linha fixo com campos binarios. Favor usar 'fields terminated by'.",
+"O arquivo '%-.64s' precisa estar no diretorio do banco de dados, e sua leitura permitida a todos",
+"Arquivo '%-.64s' ja existe",
+"Registros: %ld Apagados: %ld Ignorados: %ld Avisos: %ld",
+"Registros: %ld Duplicados: %ld",
+"Parte da chave errada. A parte utilizada nao e um texto ou tem comprimento maior que o definido",
+"Nao e possivel retirar todas as colunas da tabela com ALTER TABLE. Use DROP TABLE",
+"Nao foi possivel DROP '%-.64s'. Confira se este campo/chave existe",
+"Registros: %ld Duplicados: %ld Avisos: %ld",
+"INSERT TABLE '%-.64s' nao e permitido em FROM lista de tabelas",
+"Tarefa desconhecida id: %lu",
+"Voce nao e o responsavel pela tarefa %lu",
+"Nenhuma tabela em uso",
+"Muitos textos para a coluna %s e SET",
+"Nao foi possivel um unico nome para o arquivo %s.(1-999)\n",
+"Tabela '%-.64s' esta travada para leitura, e nao pode ser atualizada",
+"Tabela '%-.64s' nao foi travada com LOCK TABLES",
+"Campo binario '%-.64s' nao pode ter um valor inicial",
+"Nome de banco de dados invalido: '%-.64s'",
+"Nome de tabela invalido: '%-.64s'",
+"O SELECT muitos registros, e possivelmente vai demorar. Confira sua clausula WHERE e utilize SET OPTION SQL_BIG_SELECTS=1 se o SELECT esta correto",
+"Erro desconhecido",
+"Procedimento %s desconhecido",
+"Numero de parametros para o procedimento %s esta incorreto",
+"Parametro incorreto para o procedimento %s",
+"Tabela '%-.64s' descohecida em %s",
+"Campo '%-.64s' definido em duplicidade",
+"Invalid use of group function",
+"Table '%-.64s' uses a extension that doesn't exist in this MySQL version",
+"A table must have at least 1 column",
+"The table '%-.64s' is full",
+"Unknown character set: '%-.64s'",
+"Too many tables. MySQL can only use %d tables in a join",
+"Too many fields",
+"Too big row size. The maximum row size, not counting blobs, is %d. You have to change some fields to blobs",
+"Thread stack overrun: Used: %ld of a %ld stack. Use 'mysqld -O thread_stack=#' to specify a bigger stack if needed",
+"Cross dependency found in OUTER JOIN. Examine your ON conditions",
+"Column '%-.32s' is used with UNIQUE or INDEX but is not defined as NOT NULL",
+"Can't load function '%-.64s'",
+"Can't initialize function '%-.64s'; %-.80s",
+"No paths allowed for shared library",
+"Function '%-.64s' already exist",
+"Can't open shared library '%-.64s' (errno: %d %s)",
+"Can't find function '%-.64s' in library'",
+"Function '%-.64s' is not defined",
+"Host '%-.64s' is blocked because of many connection errors. Unblock with 'mysqladmin flush-hosts'",
+"Host '%-.64s' is not allowed to connect to this MySQL server",
+"You are using MySQL as an anonymous users and anonymous users are not allowed to change passwords",
+"You must have privileges to update tables in the mysql database to be able to change passwords for others",
+"Can't find any matching row in the user table",
+"Rows matched: %ld Changed: %ld Warnings: %ld",
+"Can't create a new thread (errno %d). If you are not out of available memory you can consult the manual for any possible OS dependent bug",
+"Column count doesn't match value count at row %ld",
+"Can't reopen table: '%-.64s',
+"Invalid use of NULL value",
+"Got error '%-.64s' from regexp",
+"Mixing of GROUP columns (MIN(),MAX(),COUNT()...) with no GROUP columns is illegal if there is no GROUP BY clause",
+"There is no such grant defined for user '%-.32s' on host '%-.64s'",
+"%-.16s command denied to user: '%-.32s@%-.64s' for table '%-.64s'",
+"%-.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",
+"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",
+"Delayed insert thread couldn't get requested lock for table %-.64s",
+"Too many delayed threads in use",
+"Aborted connection %ld to db: '%-.64s' user: '%-.64s' (%s)",
+"Got a packet bigger than 'max_allowed_packet'",
+"Got a read error from the connection pipe",
+"Got an error from fcntl()",
+"Got packets out of order",
+"Couldn't uncompress communication packet",
+"Got an error reading communication packets"
+"Got timeout reading communication packets",
+"Got an error writing communication packets",
+"Got timeout writing communication packets",
+"Result string is longer than max_allowed_packet",
+"The used table type doesn't support BLOB/TEXT columns",
+"The used table type doesn't support AUTO_INCREMENT columns",
+"INSERT DELAYED can't be used with table '%-.64s', because it is locked with LOCK TABLES",
+"Incorrect column name '%-.100s'",
+"The used table handler can't index column '%-.64s'",
+"All tables in the MERGE table are not defined identically",
+"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",
+"Result consisted of more than one row",
+"This table type requires a primary key",
+"This version of MySQL is not compiled with RAID support",
+"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",
+"You are not allowed to execute this command in a transaction",
+"Got error %d during COMMIT",
+"Got error %d during ROLLBACK",
+"Got error %d during FLUSH_LOGS",
+"Got error %d during CHECKPOINT",
+"Aborted connection %ld to db: '%-.64s' user: '%-.32s' host: `%-.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",
diff --git a/sql/share/romania/errmsg.sys b/sql/share/romania/errmsg.sys
new file mode 100644
index 00000000000..018333d3e1e
--- /dev/null
+++ b/sql/share/romania/errmsg.sys
Binary files differ
diff --git a/sql/share/romania/errmsg.txt b/sql/share/romania/errmsg.txt
new file mode 100644
index 00000000000..5d34f32e602
--- /dev/null
+++ b/sql/share/romania/errmsg.txt
@@ -0,0 +1,199 @@
+/* Copyright Abandoned 1997 TCX DataKonsult AB & Monty Program KB & Detron HB
+ This file is public domain and comes with NO WARRANTY of any kind
+
+ Translated into Romanian by Stefan Saroiu
+ e-mail: tzoompy@cs.washington.edu
+*/
+
+"hashchk",
+"isamchk",
+"NU",
+"DA",
+"Nu pot sa creez fisierul '%-.64s' (Eroare: %d)",
+"Nu pot sa creez tabla '%-.64s' (Eroare: %d)",
+"Nu pot sa creez baza de date '%-.64s'. (Eroare: %d)",
+"Nu pot sa creez baza de date '%-.64s'. Baza de date exista deja",
+"Nu pot sa drop baza de date '%-.64s'. Baza da date este inexistenta",
+"Eroare dropuind baza de date (nu pot sa sterg '%-.64s', Eroare: %d)",
+"Eroare dropuind baza de date (nu pot sa rmdir '%-.64s', Eroare: %d)",
+"Eroare incercind sa delete '%-.64s' (Eroare: %d)",
+"Nu pot sa citesc cimpurile in tabla de system (system table)",
+"Nu pot sa obtin statusul lui '%-.64s' (Eroare: %d)",
+"Nu pot sa obtin directorul current (working directory) (Eroare: %d)",
+"Nu pot sa lock fisierul (Eroare: %d)",
+"Nu pot sa deschid fisierul: '%-.64s'. (Eroare: %d)",
+"Nu pot sa gasesc fisierul: '%-.64s' (Eroare: %d)",
+"Nu pot sa citesc directorul '%-.64s' (Eroare: %d)",
+"Nu pot sa schimb directorul '%-.64s' (Eroare: %d)",
+"Cimpul a fost schimbat de la ultima citire a tabelei '%-.64s'",
+"Hard-disk-ul este plin (%s). Astept sa se elibereze ceva spatiu....",
+"Nu pot sa scriu (can't write), cheie duplicata in tabela '%-.64s'",
+"Eroare inchizind '%-.64s' (errno: %d)",
+"Eroare citind fisierul '%-.64s' (errno: %d)",
+"Eroare incercind sa renumesc '%-.64s' in '%-.64s' (errno: %d)",
+"Eroare scriind fisierul '%-.64s' (errno: %d)",
+"'%-.64s' este blocat pentry schimbari (loccked against change)",
+"Sortare intrerupta",
+"View '%-.64s' nu exista pentru '%-.64s'",
+"Eroarea %d obtinuta din handlerul tabelei",
+"Handlerul tabelei pentru '%-.64s' nu are aceasta optiune",
+"Nu pot sa gasesc recordul in '%-.64s'",
+"Informatie incorecta in fisierul: '%-.64s'",
+"Cheia fisierului incorecta pentru tabela: '%-.64s'. Incearca s-o repari",
+"Cheia fisierului e veche pentru tabela '%-.64s'; Repar-o!",
+"Tabela '%-.64s' e read-only",
+"Out of memory. Porneste daemon-ul din nou si incearca inca o data (e nevoie de %d bytes)",
+"Out of memory pentru sortare. Largeste marimea buffer-ului pentru sortare in daemon (sort buffer size)",
+"Sfirsit de fisier neasteptat in citirea fisierului '%-.64s' (errno: %d)",
+"Prea multe conectiuni",
+"Out of memory; Verifica daca mysqld sau vreun alt proces foloseste toate memoria disponbila. Altfel, trebuie sa folosesi 'ulimit' ca sa permiti lui memoria disponbila. Altfel, trebuie sa folosesi 'ulimit' ca sa permiti lui mysqld sa foloseasca mai multa memorie ori adauga mai mult spatiu pentru swap (swap space)",
+"Nu pot sa obtin hostname-ul adresei tale",
+"Prost inceput de conectie (bad handshake)",
+"Acces interzis pentru utilizatorul: '%-.32s@%-.64s' la baza de date '%-.64s'",
+"Acces interzis pentru utilizatorul: '%-.32s@%-.64s' (Folosind parola: %s)",
+"Nici o baza de data nu a fost selectata inca",
+"Comanda invalida",
+"Coloana '%-.64s' nu poate sa fie null",
+"Baza de data invalida '%-.64s'",
+"Tabela '%-.64s' exista deja",
+"Tabela '%-.64s' este invalida",
+"Coloana: '%-.64s' in %-.64s este ambigua",
+"Terminarea serverului este in desfasurare",
+"Coloana invalida '%-.64s' in '%-.64s'",
+"'%-.64s' nu exista in clauza GROUP BY",
+"Nu pot sa grupez pe (group on) '%-.64s'",
+"Comanda are functii suma si coloane in aceeasi comanda",
+"Numarul de coloane nu este acelasi cu numarul valoarei",
+"Numele indentificatorului '%-.100s' este prea lung",
+"Numele coloanei '%-.64s' e duplicat",
+"Numele cheiei '%-.64s' e duplicat",
+"Cimpul '%-.64s' e duplicat pentru cheia %d",
+"Specificandul coloanei '%-.64s' este incorect",
+"%s linga '%-.80s' pe linia %d",
+"Query-ul a fost gol",
+"Tabela/alias: '%-.64s' nu este unic",
+"Valoarea de default este invalida pentru '%-.64s'",
+"Chei primare definite de mai multe ori",
+"Prea multe chei. Numarul de chei maxim este %d",
+"Prea multe chei. Numarul de chei maxim este %d",
+"Cheia specificata este prea lunga. Marimea maxima a unei chei este de %d",
+"Coloana cheie '%-.64s' nu exista in tabela",
+"Coloana de tip BLOB '%-.64s' nu poate fi folosita in specificarea cheii cu tipul de tabla folosit",
+"Lungimea coloanei '%-.64s' este prea lunga (maximum = %d). Foloseste BLOB mai bine",
+"Definitia tabelei este incorecta; Nu pot fi mai mult de o singura coloana de tip auto si aceasta trebuie definita ca cheie",
+"%s: sint gata pentru conectii\n",
+"%s: Terminare normala\n",
+"%s: Semnal %d obtinut. Aborting!\n",
+"%s: Terminare completa\n",
+"%s: Terminare fortata a thread-ului %ld utilizatorului: '%-.32s'\n",
+"Nu pot crea IP socket",
+"Tabela '%-.64s' nu are un index ca acela folosit in CREATE INDEX. Re-creeaza tabela",
+"Argumentul pentru separatorul de cimpuri este diferit de ce ma asteptam. Verifica manualul","
+"Nu poti folosi lungime de cimp fix pentru BLOB-uri. Foloseste 'fields terminated by'.",
+"Fisierul '%-.64s' trebuie sa fie in directorul bazei de data sau trebuie sa poata sa fie citit de catre toata lumea (verifica permisiile)",
+"Fisierul '%-.80s' exista deja",
+"Recorduri: %ld Sterse: %ld Sarite (skipped): %ld Atentionari (warnings): %ld",
+"Recorduri: %ld Duplicate: %ld",
+"Componentul cheii este incorrect. Componentul folosit al cheii nu este un sir sau lungimea folosita este mai lunga decit lungimea cheii",
+"Nu poti sterge toate coloanele cu ALTER TABLE. Foloseste DROP TABLE in schimb",
+"Nu pot sa DROP '%-.64s'. Verifica daca coloana/cheia exista",
+"Recorduri: %ld Duplicate: %ld Atentionari (warnings): %ld",
+"INSERT TABLE '%-.64s' nu este permis in lista FROM de tabele",
+"Id-ul: %lu thread-ului este necunoscut",
+"Nu sinteti proprietarul threadului %lu",
+"Nici o tabela folosita",
+"Prea multe siruri pentru coloana %-.64s si SET",
+"Nu pot sa generez un nume de log unic %-.64s.(1-999)\n",
+"Tabela '%-.64s' a fost locked cu un READ lock si nu poate fi actualizata",
+"Tabela '%-.64s' nu a fost locked cu LOCK TABLES",
+"Coloana BLOB '%-.64s' nu poate avea o valoare default",
+"Numele bazei de date este incorect '%-.100s'",
+"Numele tabelei este incorect '%-.100s'",
+"SELECT-ul ar examina prea multe cimpuri si probabil ar lua prea mult timp. Verifica clauza WHERE si foloseste SET OPTION SQL_BIG_SELECTS=1 daca SELECT-ul e ok",
+"Eroare unknown",
+"Procedura unknown '%-.64s'",
+"Procedura '%-.64s' are un numar incorect de parametri",
+"Procedura '%-.64s' are parametrii incorecti",
+"Tabla '%-.64s' invalida in %-.32s",
+"Coloana '%-.64s' specificata de doua ori",
+"Folosire incorecta a functiei group",
+"Tabela '%-.64s' foloseste o extensire inexistenta in versiunea curenta de MySQL",
+"O tabela trebuie sa aiba cel putin o coloana",
+"Tabela '%-.64s' e plina",
+"Set de caractere invalid: '%-.64s'",
+"Prea multe tabele. MySQL nu poate folosi mai mult de %d tabele intr-un join",
+"Prea multe coloane",
+"Marimea liniei (row) prea mare. Marimea maxima a liniei, excluzind BLOB-urile este de %d. Trebuie sa schimbati unele cimpuri in BLOB-uri",
+"Stack-ul thread-ului a fost depasit (prea mic): Folositi: %ld intr-un stack de %ld. Folositi 'mysqld -O thread_stack=#' ca sa specifici un stack mai mare",
+"Dependinta incrucisata (cross dependency) gasita in OUTER JOIN. Examinati conditiile ON",
+"Coloana '%-.64s' e folosita cu UNIQUE sau INDEX dar fara sa fie definita ca NOT NULL",
+"Nu pot incarca functia '%-.64s'",
+"Nu pot initializa functia '%-.64s'; %-.80s",
+"Nici un paths nu e permis pentru o librarie shared",
+"Functia '%-.64s' exista deja",
+"Nu pot deschide libraria shared '%-.64s' (Eroare: %d %-.64s)",
+"Nu pot gasi functia '%-.64s' in libraria",
+"Functia '%-.64s' nu e definita",
+"Host-ul '%-.64s' e blocat din cauza multelor erori de conectie. Poti deploca folosind 'mysqladmin flush-hosts'",
+"Host-ul '%-.64s' nu este permis a se conecta la aceste server MySQL",
+"Dumneavoastra folositi MySQL ca un utilizator anonim si utilizatorii anonimi nu au voie sa schime parolele",
+"Trebuie sa aveti privilegii sa actualizati tabelele in bazele de date mysql ca sa puteti sa schimati parolele altora",
+"Nu pot gasi nici o linie corespunzatoare in tabela utilizatorului",
+"Linii identificate (matched): %ld Schimbate: %ld Atentionari (warnings): %ld",
+"Nu pot crea un thread nou (Eroare %d). Daca mai aveti memorie disponibila in sistem, puteti consulta manualul - ar putea exista un potential bug in legatura cu sistemul de operare",
+"Numarul de coloane nu corespunde cu numarul de valori la linia %ld",
+"Nu pot redeschide tabela: '%-.64s'",
+"Folosirea unei value NULL e invalida",
+"Eroarea '%-.64s' obtinuta din expresia regulara (regexp)",
+"Amestecarea de coloane GROUP (MIN(),MAX(),COUNT()...) fara coloane GROUP este ilegala daca nu exista o clauza GROUP BY",
+"Nu exista un astfel de grant definit pentru utilzatorul '%-.32s' de pe host-ul '%-.64s'",
+"Comanda %-.16s interzisa utilizatorului: '%-.32s@%-.64s' pentru tabela '%-.64s'",
+"Comanda %-.16s interzisa utilizatorului: '%-.32s@%-.64s' pentru coloana '%-.64s' in tabela '%-.64s'",
+"Comanda GRANT/REVOKE ilegala. Consultati manualul in privinta privilegiilor ce pot fi folosite.",
+"Argumentul host-ului sau utilizatorului pentru GRANT e prea lung",
+"Tabela '%-.64s.%-.64s' nu exista",
+"Nu exista un astfel de privilegiu (grant) definit pentru utilizatorul '%-.32s' de pe host-ul '%-.64s' pentru tabela '%-.64s'",
+"Comanda folosita nu este permisa pentru aceasta versiune de MySQL",
+"Aveti o eroare in sintaxa RSQL",
+"Thread-ul pentru inserarea aminata nu a putut obtine lacatul (lock) pentru tabela %-.64s",
+"Prea multe threaduri aminate care sint in uz",
+"Conectie terminata %ld la baza de date: '%-.64s' utilizator: '%-32s' (%-.64s)",
+"Un packet mai mare decit 'max_allowed_packet' a fost primit",
+"Eroare la citire din cauza lui 'connection pipe'",
+"Eroare obtinuta de la fcntl()",
+"Packets care nu sint ordonati au fost gasiti",
+"Nu s-a putut decompresa pachetul de comunicatie (communication packet)",
+"Eroare obtinuta citind pachetele de comunicatie (communication packets)",
+"Timeout obtinut citind pachetele de comunicatie (communication packets)",
+"Eroare in scrierea pachetelor de comunicatie (communication packets)",
+"Timeout obtinut scriind pachetele de comunicatie (communication packets)",
+"Sirul rezultat este mai lung decit max_allowed_packet",
+"Tipul de tabela folosit nu suporta coloane de tip BLOB/TEXT",
+"Tipul de tabela folosit nu suporta coloane de tip AUTO_INCREMENT",
+"INSERT DELAYED nu poate fi folosit cu tabela '%-.64s', deoarece este locked folosing LOCK TABLES",
+"Nume increct de coloana '%-.100s'",
+"Handler-ul tabelei folosite nu poate indexa coloana '%-.64s'",
+"Toate tabelele din tabela MERGE nu sint definite identic",
+"Nu pot scrie pe hard-drive, din cauza constraintului unic (unique constraint) pentru tabela '%-.64s'",
+"Coloana BLOB '%-.64s' este folosita in specificarea unei chei fara ca o lungime de cheie sa fie folosita",
+"Toate partile unei chei primare (PRIMARY KEY) trebuie sa fie NOT NULL; Daca aveti nevoie de NULL in vreo cheie, folositi UNIQUE in schimb",
+"Resultatul constista din mai multe linii",
+"Aceast tip de tabela are nevoie de o cheie primara",
+"Aceasta versiune de MySQL, nu a fost compilata cu suport pentru RAID",
+"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",
+"You are not allowed to execute this command in a transaction",
+"Got error %d during COMMIT",
+"Got error %d during ROLLBACK",
+"Got error %d during FLUSH_LOGS",
+"Got error %d during CHECKPOINT",
+"Aborted connection %ld to db: '%-.64s' user: '%-.32s' host: `%-.64s' (%-.64s)",
+"The handler for the table does not support binary table dump","Binlog closed while trying to FLUSH MASTER",
+"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",
diff --git a/sql/share/russian/errmsg.sys b/sql/share/russian/errmsg.sys
new file mode 100644
index 00000000000..5ddaf2e2973
--- /dev/null
+++ b/sql/share/russian/errmsg.sys
Binary files differ
diff --git a/sql/share/russian/errmsg.txt b/sql/share/russian/errmsg.txt
new file mode 100644
index 00000000000..fe12cad035d
--- /dev/null
+++ b/sql/share/russian/errmsg.txt
@@ -0,0 +1,198 @@
+/* Copyright Abandoned 1998.
+ This file is public domain and comes with NO WARRANTY of any kind */
+/* Primary translation was done by "Timur I. Bakeyev" <translate@bat.ru> */
+/* Additional translation by "Alexander I. Barkov" <bar@izhcom.ru> */
+/* charset: KOI8-R */
+
+"hashchk",
+"isamchk",
+"îåô",
+"äá",
+"îÅ ÍÏÇÕ ÓÏÚÄÁÔØ ÆÁÊÌ '%-.64s' (ïÛÉÂËÁ: %d)",
+"îÅ ÍÏÇÕ ÓÏÚÄÁÔØ ÔÁÂÌÉÃÕ '%-.64s' (ïÛÉÂËÁ: %d)",
+"îÅ ÍÏÇÕ ÓÏÚÄÁÔØ ÂÁÚÕ '%-.64s'. ïÛÉÂËÁ: %d",
+"îÅ ÍÏÇÕ ÓÏÚÄÁÔØ ÂÁÚÕ '%-.64s'. âÁÚÁ ÕÖÅ ÓÕÝÅÓÔ×ÕÅÔ",
+"îÅ ÍÏÇÕ ÕÄÁÌÉÔØ ÂÁÚÕ '%-.64s'. âÁÚÁ ÎÅ ÓÕÝÅÓÔ×ÕÅÔ",
+"ïÛÉÂËÁ ÐÒÉ ÕÄÁÌÅÎÉÉ ÂÁÚÙ (îÅ ÍÏÇÕ ÕÄÁÌÉÔØ '%-.64s', ÏÛÉÂËÁ %d)",
+"ïÛÉÂËÁ ÐÒÉ ÕÄÁÌÅÎÉÉ ÂÁÚÙ (îÅ ÍÏÇÕ ÕÄÁÌÉÔØ ËÁÔÁÌÏÇ '%-.64s', ÏÛÉÂËÁ %d)",
+"ïÛÉÂËÁ ÐÒÉ ÕÄÁÌÅÎÉÉ '%-.64s' (ïÛÉÂËÁ: %d)",
+"îÅ ÍÏÇÕ ÐÒÏÞÅÓÔØ ÚÁÐÉÓØ × ÓÉÓÔÅÍÎÏÊ ÔÁÂÌÉÃÅ",
+"îÅ ÍÏÇÕ ÐÏÌÕÞÉÔØ ÓÔÁÔÕÓ '%-.64s' (ïÛÉÂËÁ: %d)",
+"îÅ ÍÏÇÕ ÏÐÒÅÄÅÌÉÔØ ÒÁÂÏÞÉÊ ËÁÔÁÌÏÇ (ïÛÉÂËÁ: %d)",
+"îÅ ÍÏÇÕ ÂÌÏËÉÒÏ×ÁÔØ ÆÁÊÌ (ïÛÉÂËÁ: %d)",
+"îÅ ÍÏÇÕ ÏÔËÒÙÔØ ÆÁÊÌ: '%-.64s'. (ïÛÉÂËÁ: %d)",
+"îÅ ÍÏÇÕ ÎÁÊÔÉ ÆÁÊÌ: '%-.64s' (ïÛÉÂËÁ: %d)",
+"îÅ ÍÏÇÕ ÐÒÏÞÅÓÔØ ËÁÔÁÌÏÇ '%-.64s' (ïÛÉÂËÁ: %d)",
+"îÅ ÍÏÇÕ ÐÅÒÅÊÔÉ × ËÁÔÁÌÏÇ '%-.64s' (ïÛÉÂËÁ: %d)",
+"úÁÐÉÓØ ÂÙÌÁ ÉÚÍÅÎÅÎÁ ÓÏ ×ÒÅÍÅÎÉ ÐÏÓÌÅÄÎÅÇÏ ÞÔÅÎÉÑ ÔÁÂÌÉÃÙ '%-.64s'",
+"ðÅÒÅÐÏÌÎÅÎ ÄÉÓË (%-.64s). íÏÖÅÔ, ËÔÏ-ÎÉÂÕÄØ ÕÂÅÒÅÔ ÚÁ ÓÏÂÏÊ ÍÕÓÏÒ....",
+"îÅ ÍÏÇÕ ÐÒÏÉÚ×ÅÓÔÉ ÚÁÐÉÓØ, ËÌÀÞ ÄÕÂÌÉÒÕÅÔÓÑ × ÔÁÂÌÉÃÅ '%-.64s'",
+"ïÛÉÂËÁ ÐÒÉ ÚÁËÒÙÔÉÉ '%-.64s' (ïÛÉÂËÁ: %d)",
+"ïÛÉÂËÁ ÞÔÅÎÉÑ ÆÁÊÌÁ '%-.64s' (ïÛÉÂËÁ: %d)",
+"ïÛÉÂËÁ ÐÒÉ ÐÅÒÅÉÍÅÎÏ×ÁÎÉÉ '%-.64s' × '%-.64s' (ïÛÉÂËÁ: %d)",
+"ïÛÉÂËÁ ÚÁÐÉÓÉ × ÆÁÊÌ '%-.64s' (ïÛÉÂËÁ: %d)",
+"'%-.64s' ÚÁÂÌÏËÉÒÏ×ÁÎ ÄÌÑ ÉÚÍÅÎÅÎÉÊ",
+"óÏÒÔÉÒÏ×ËÁ ÐÒÅÒ×ÁÎÁ",
+"View '%-.64s' ÎÅ ÓÕÝÅÓÔ×ÕÅÔ ÄÌÑ '%-.64s'",
+"ðÏÌÕÞÅÎÁ ÏÛÉÂËÁ %d ÏÔ ÄÅÓËÒÉÐÔÏÒÁ ÔÁÂÌÉÃÙ",
+"äÅÓËÒÉÐÔÏÒ ÔÁÂÌÉÃÙ '%-.64s' ÎÅ ÉÍÅÅÔ ÔÁËÏÇÏ Ó×ÏÊÓÔ×Á",
+"îÅ ÍÏÇÕ ÎÁÊÔÉ ÚÁÐÉÓØ × '%-.64s'",
+"îÅ×ÅÒÎÁÑ ÉÎÆÏÒÍÁÃÉÑ × ÆÁÊÌÅ: '%-.64s'",
+"îÅ×ÅÒÎÙÊ ÉÎÄÅËÓÎÙÊ ÆÁÊÌ ÄÌÑ ÔÁÂÌÉÃÙ: '%-.64s'. ðÏÐÒÏÂÕÊÔÅ ÅÇÏ ×ÏÓÓÏÚÄÁÔØ",
+"óÔÁÒÙÊ ÉÎÄÅËÓÎÙÊ ÆÁÊÌ ÄÌÑ ÔÁÂÌÉÃÙ '%-.64s'; òÅÉÎÄÅËÓÉÒÕÊÔÅ ÅÅ!",
+"'%-.64s' ÔÏÌØËÏ ÄÌÑ ÞÔÅÎÉÑ",
+"îÅ È×ÁÔÁÅÔ ÐÁÍÑÔÉ. ðÅÒÅÇÒÕÚÉÔÅ ÓÅÒ×ÅÒ É ÐÏÐÒÏÂÕÊÔÅ ÓÎÏ×Á (ÎÕÖÎÏ %d ÂÁÊÔ)",
+"îÅ È×ÁÔÁÅÔ ÐÁÍÑÔÉ ÄÌÑ ÓÏÒÔÉÒÏ×ËÉ. õ×ÅÌÉÞØÔÅ ÒÁÚÍÅÒ ÂÕÆÅÒÁ ÓÏÒÔÉÒÏ×ËÉ Õ ÓÅÒ×ÅÒÁ",
+"îÅÏÖÉÄÁÎÎÙÊ ËÏÎÅà ÆÁÊÌÁ '%-.64s' (ïÛÉÂËÁ: %d)",
+"óÌÉÛËÏÍ ÍÎÏÇÏ ÓÏÅÄÉÎÅÎÉÊ",
+"îÅÄÏÓÔÁÔÏË ÍÅÓÔÁ/ÐÁÍÑÔÉ ÄÌÑ ÎÉÔÉ",
+"îÅ ÍÏÇÕ ÏÐÒÅÄÅÌÉÔØ ÉÍÑ ÈÏÓÔÁ ÐÏ ÷ÁÛÅÍÕ ÁÄÒÅÓÕ",
+"îÅËÏÒÒÅËÔÎÁÑ ÉÎÉÃÉÁÌÉÚÁÃÉÑ Ó×ÑÚÉ",
+"äÏÓÔÕÐ ÚÁËÒÙÔ ÄÌÑ ÐÏÌØÚÏ×ÁÔÅÌÑ: '%-.32s@%-.64s' Ë ÂÁÚÅ '%-.64s'",
+"äÏÓÔÕÐ ÚÁËÒÙÔ ÄÌÑ ÐÏÌØÚÏ×ÁÔÅÌÑ: '%-.32s@%-.64s' (âÙÌ ÉÓÐÏÌØÚÏ×ÁÎ ÐÁÒÏÌØ: %-.64s)",
+"îÅ ×ÙÂÒÁÎÁ ÂÁÚÁ",
+"îÅÉÚ×ÅÓÔÎÁÑ ËÏÍÁÎÄÁ",
+"óÔÏÌÂÅà '%-.64s' ÎÅ ÍÏÖÅÔ ÂÙÔØ ÐÕÓÔÙÍ/ÎÕÌÅ×ÙÍ",
+"îÅÉÚ×ÅÓÔÎÁÑ ÂÁÚÁ '%-.64s'",
+"ôÁÂÌÉÃÁ '%-.64s' ÕÖÅ ÅÓÔØ",
+"îÅÉÚ×ÅÓÔÎÁÑ ÔÁÂÌÉÃÁ '%-.64s'",
+"ðÏÌÅ '%-.64s' × %-.64s ÎÅ ÏÄÎÏÚÎÁÞÎÏ",
+"ðÒÏÉÓÈÏÄÉÔ ×ÙËÌÀÞÅÎÉÅ ÓÅÒ×ÅÒÁ",
+"îÅÉÚ×ÅÓÔÎÏÅ ÐÏÌÅ '%-.64s' × %-.64s",
+" '%-.64s' ÉÓÐÏÌØÚÏ×ÁÎÏ ×ÎÅ ×ÙÒÁÖÅÎÉÑ GROUP BY",
+"îÅ ÍÏÇÕ ÐÒÏÉÚ×ÅÓÔÉ ÇÒÕÐÐÉÒÏ×ËÕ ÐÏ '%-.64s'",
+"÷ ÏÄÎÏÍ ×ÙÒÁÖÅÎÉÉ ÓÏÄÅÒÖÁÔØÓÑ É ÉÍÅÎÁ ÐÏÌÅÊ, É ÓÕÍÍÉÒÕÀÝÉÅ ÆÕÎËÃÉÉ",
+"þÉÓÌÏ ÓÔÏÌÂÃÏ× ÎÅ ÓÏÏÔ×ÅÔÓÔ×ÕÅÔ ÞÉÓÌÕ ÚÎÁÞÅÎÉÊ"
+"óÌÉÛËÏÍ ÄÌÉÎÎÙÊ ÉÄÅÎÔÉÆÉËÁÔÏÒ: '%-.64s'",
+"äÕÂÌÉÒÏ×ÁÎÎÏÅ ÉÍÑ ÐÏÌÑ '%-.64s'",
+"äÕÂÌÉÒÏ×ÁÎÎÏÅ ÉÍÑ ËÌÀÞÁ '%-.64s'",
+"ðÏ×ÔÏÒÑÀÝÅÅÓÑ ÚÎÁÞÅÎÉÅ '%-.64s' ÄÌÑ ËÌÀÞÁ %d",
+"îÅ×ÅÒÎÙÊ ÓÐÅÃÉÆÉËÁÔÏÒ ÐÏÌÑ: '%-.64s'",
+"%-.64s ÏËÏÌÏ '%-.64s', × ÓÔÒÏËÅ %d",
+"ðÕÓÔÏÊ ÚÁÐÒÏÓ",
+"îÅ ÕÎÉËÁÌØÎÁÑ ÔÁÂÌÉÃÁ/ÐÓÅ×ÄÏÎÉÍ: '%-.64s'",
+"îÅ×ÅÒÎÏÅ ÚÎÁÞÅÎÉÅ ÐÏ ÕÍÏÌÞÁÎÉÀ '%-.64s'",
+"ðÅÒ×ÉÞÎÙÊ ËÌÀÞ ÏÐÒÅÄÅÌÅÎ ÎÅÓËÏÌØËÏ ÒÁÚ",
+"ïÐÒÅÄÅÌÅÎÎÏ ÓÌÉÛËÏÍ ÍÎÏÇÏ ËÌÀÞÅÊ. ÷ÏÚÍÏÖÎÏ ÍÁËÓÉÍÁÌØÎÏ %d ËÌÀÞÅÊ",
+"ïÐÒÅÄÅÌÅÎÎÏ ÓÌÉÛËÏÍ ÍÎÏÇÏ ÓÏÓÔÁ×ÌÑÀÝÉÈ ËÌÀÞÁ. ÷ÏÚÍÏÖÎÏ ÍÁËÓÉÍÁÌØÎÏ %d ÓÏÓÔÁ×ÌÑÀÝÉÈ",
+"ëÌÀÞ ÓÌÉÛËÏÍ ÂÏÌØÛÏÊ. íÁËÓÉÍÁÌØÎÁÑ ÄÌÉÎÁ %d",
+"ëÌÀÞÅ×ÏÅ ÐÏÌÅ '%-.64s' ÎÅ ÓÏÄÅÒÖÉÔÓÑ × ÔÁÂÌÉÃÅ",
+"ïÂßÅËÔ BLOB '%-.64s' ÎÅ ÍÏÖÅÔ ÐÒÉÓÕÔÓÔ×Ï×ÁÔØ × ÏÐÒÅÄÅÌÅÎÉÉ ËÌÀÞÁ",
+"óÌÉÛËÏÍ ×ÅÌÉË ÒÁÚÍÅÒ ÐÏÌÑ '%-.64s' (max = %d). ÷ÏÓÐÏÌØÚÕÊÔÅÓØ ÏÂßÅËÔÏÍ BLOB",
+"á×ÔÏÍÁÔÉÞÅÓËÏÅ ÐÏÌÅ ÍÏÖÅÔ ÂÙÔØ ÔÏÌØËÏ ÏÄÎÏ É ÄÏÌÖÎÏ ÂÙÔØ ËÌÀÞÏÍ",
+"%-.64s: îÁ Ó×ÑÚÉ!\n",
+"%-.64s: îÏÒÍÁÌØÎÏÅ ÚÁ×ÅÒÛÅÎÉÅ\n",
+"%-.64s: ðÏÌÕÞÅÎ ÓÉÇÎÁÌ %d. õÍÙ×ÁÀ ÒÕËÉ!\n",
+"%-.64s: ïÔËÌÀÞÅÎÉÅ ×ÙÐÏÌÎÅÎÏ\n",
+"%-.64s: ðÒÉÎÕÄÉÔÅÌØÎÏÅ ÐÒÅËÒÁÝÅÎÉÅ ÎÉÔÉ %ld ÐÏÌØÚÏ×ÁÔÅÌÑ: '%-.64s'\n",
+"îÅ ÍÏÇÕ ÓÏÚÄÁÔØ IP socket",
+"ôÁÂÌÉÃÁ '%-.64s' ÉÍÅÅÔ ÉÎÄÅËÓ, ÎÅ ÓÏ×ÐÁÄÁÀÝÉÊ Ó ÕËÁÚÁÎÎÙÍ × CREATE INDEX. óÏÚÄÁÊÔÅ ÔÁÂÌÉÃÕ ÅÝÅ ÒÁÚ",
+"òÁÚÄÅÌÉÔÅÌÉ ÐÏÌÅÊ ÎÅ ÓÏ×ÐÁÄÁÀÔ Ó ÏÖÉÄÁÅÍÙÍÉ. ðÒÏÞÔÉÔÅ ÎÁËÏÎÅà ÉÎÓÔÒÕËÃÉÀ!",
+"îÅÌØÚÑ ÓÓÙÌÁÔØÓÑ ÎÁ ÆÉËÓÉÒÏ×ÁÎÎÕÀ ÄÌÉÎÕ ÓÔÒÏËÉ × BLOB. éÓÐÏÌØÚÕÊÔÅ 'fields terminated by'.",
+"æÁÊÌ '%-.64s' ÄÏÌÖÅÎ ÂÙÔØ × ËÁÔÁÌÏÇÅ ÂÁÚ ÌÉÂÏ ÄÏÓÔÕÐÅÎ ×ÓÅÍ ÄÌÑ ÞÔÅÎÉÑ",
+"æÁÊÌ '%-.64s' ÕÖÅ ÅÓÔØ",
+"úÁÐÉÓÅÊ: %ld õÄÁÌÅÎÏ: %ld ðÒÏÐÕÝÅÎÏ: %ld ðÒÅÄÕÐÒÅÖÄÅÎÉÊ: %ld",
+"úÁÐÉÓÅÊ: %ld äÕÂÌÅÊ: %ld",
+"îÅ×ÅÒÎÁÑ ÓÏÓÔÁ×ÌÑÀÝÁÑ ËÌÀÞÁ. äÁÎÎÁÑ ÓÏÓÔÁ×ÌÑÀÝÁÑ ÌÉÂÏ ÎÅ ÓÔÒÏËÏ×ÁÑ, ÌÉÂÏ ÂÏÌØÛÅ, ÞÅÍ ÍÏÖÅÔ ÂÙÔØ",
+"îÅÌØÚÑ ÕÄÁÌÉÔØ ×ÓÅ ÐÏÌÑ ÞÅÒÅÚ ALTER TABLE. ÷ÏÓÐÏÌØÚÕÊÔÅÓØ DROP TABLE",
+"îÅ ÍÏÇÕ ÓÂÒÏÓÉÔØ '%-.64s'. ðÒÏ×ÅÒØÔÅ, ÞÔÏ ÜÔÏ ÐÏÌÅ/ËÌÀÞ ÓÕÝÅÓÔ×ÕÀÔ",
+"úÁÐÉÓÅÊ: %ld äÕÂÌÅÊ: %ld ðÒÅÄÕÐÒÅÖÄÅÎÉÊ: %ld",
+"INSERT TABLE '%-.64s' ÎÅ ÒÁÚÒÅÛÅÎÏ × ÓÐÉÓËÅ FROM TABLE",
+"îÅÉÚ×ÅÓÔÎÁÑ ÎÉÔØ: %lu",
+"÷Ù ÎÅ ×ÌÁÄÅÌÅà ÎÉÔÉ %lu",
+"ôÁÂÌÉÃÙ ÎÅ ÉÓÐÏÌØÚÏ×ÁÎÙ",
+"ïÞÅÎØ ÍÎÏÇÏ ÓÔÒÏË ÄÌÑ ÐÏÌÑ %-.64s É SET",
+"îÅ ÍÏÇÕ ÓÏÚÄÁÔØ ÕÎÉËÁÌØÎÙÊ ÌÏÇ-ÆÁÊÌ %-.64s.(1-999)\n",
+"ôÁÂÌÉÃÁ '%-.64s' ÚÁÂÌÏËÉÒÏ×ÁÎÁ READ É ÎÅ ÍÏÖÅÔ ÂÙÔØ ÏÂÎÏ×ÌÅÎÁ",
+"ôÁÂÌÉÃÁ '%-.64s' ÎÅ ÂÙÌÁ ÂÌÏËÉÒÏ×ÁÎÁ LOCK TABLES",
+"ïÂßÅËÔ BLOB '%-.64s' ÎÅ ÍÏÖÅÔ ÉÍÅÅÔ ÚÎÁÞÅÎÉÊ ÐÏ ÕÍÏÌÞÁÎÉÀ",
+"îÅÄÏÐÕÓÔÉÍÏÅ ÉÍÑ ÂÁÚÙ '%-.64s'",
+"îÅÄÏÐÕÓÔÉÍÏÅ ÉÍÑ ÔÁÂÌÉÃÙ '%-.64s'",
+"SELECT ÏÂÒÁÂÏÔÁÅÔ ÏÞÅÎØ ÍÎÏÇÏ ÚÁÐÉÓÅÊ É ÜÔÏ îáäïìçï. ðÒÏ×ÅÒØÔÅ ÕÓÌÏ×ÉÅ WHERE É ×ÏÓÐÏÌØÚÕÊÔÅÓØ SQL_OPTION BIG_SELECTS=1 ÅÓÌÉ SELECT ËÏÒÒÅËÔÅÎ",
+"îÅÉÚ×ÅÓÔÎÁÑ ÏÛÉÂËÁ",
+"îÅÉÚ×ÅÓÔÎÁÑ ÐÒÏÃÅÄÕÒÁ %-.64s",
+"îÅ×ÅÒÎÏÅ ËÏÌÉÞÅÓÔ×Ï ÐÁÒÁÍÅÔÒÏ× × ×ÙÚÏ×Å %-.64s",
+"îÅ×ÅÒÎÙÅ ÐÁÒÁÍÅÔÒÙ × ÐÒÏÃÅÄÕÒÅ %-.64s",
+"îÅÉÚ×ÅÓÔÎÁÑ ÔÁÂÌÉÃÁ '%-.64s' × %-.64s",
+"ðÏÌÅ '%-.64s' ÏÂßÑ×ÌÅÎÎÏ Ä×ÁÖÄÙ",
+"îÅ×ÅÒÎÏÅ ÉÓÐÏÌØÚÏ×ÁÎÉÅ ÇÒÕÐÐÏ×ÏÊ ÆÕÎËÃÉÉ",
+"ôÁÂÌÉÃÁ '%-.64s' ÉÓÐÏÌØÚÕÅÔ ÒÁÓÛÉÒÅÎÉÅ, ÎÅ ÓÕÝÅÓÔ×ÕÀÝÅÅ × ÄÁÎÎÏÊ ×ÅÒÓÉÉ MySQL",
+"÷ ÔÁÂÌÉÃÅ ÄÏÌÖÎÏ ÂÙÔØ ÈÏÔÑ ÂÙ ÏÄÎÏ ÐÏÌÅ",
+"ôÁÂÌÉÃÁ '%-.64s' ÐÅÒÅÐÏÌÎÅÎÁ",
+"îÅÉÚ×ÅÓÔÎÙÊ ÎÁÂÏÒ ÓÉÍ×ÏÌÏ×: '%-.64s'",
+"óÌÉÛËÏÍ ÍÎÏÇÏ ÔÁÂÌÉÃ. MySQL ÍÏÖÅÔ ÉÓÐÏÌØÚÏ×ÁÔØ ÔÏÌØËÏ %d ÔÁÂÌÉÃ × ÏÂßÅÄÉÎÅÎÉÉ",
+"óÌÉÛËÏÍ ÍÎÏÇÏ ÐÏÌÅÊ",
+"óÌÉÛËÏÍ ÂÏÌØÛÏÊ ÒÁÚÍÅÒ ÚÁÐÉÓÉ. íÁËÓÉÍÁÌØÎÙÊ ÒÁÚÍÅÒ ÚÁÐÉÓÉ - %d, ÎÅ ÓÞÉÔÁÑ blob. úÁÍÅÎÉÔÅ ÎÅËÏÔÏÒÙÅ ÐÏÌÑ ÎÁ blob",
+"ðÅÒÅÐÏÌÎÅÎÉÅ ÎÉÔÅ×ÏÇÏ ÓÔÅËÁ: éÓÐÏÌØÚÏ×ÁÎÏ %ld ÉÚ %ld. åÓÌÉ ÎÅÏÂÈÏÄÉÍÏ, ÉÓÐÏÌØÚÕÊÔÅ 'mysqld -O thread_stack=#' ÞÔÏÂÙ Õ×ÅÌÉÞÉÔØ ÒÁÚÍÅÒ ÓÔÅËÁ",
+"ðÅÒÅËÒÅÓÔÎÁÑ ÚÁ×ÉÓÉÍÏÓÔØ × OUTER JOIN. ðÒÏ×ÅÒØÔÅ ÕÓÌÏ×ÉÑ ON",
+"ðÏÌÅ '%-.32s' ÉÓÐÏÌØÚÕÅÔÓÑ ËÁË UNIQUE ÉÌÉ INDEX ÎÏ ÎÅ ÏÐÒÅÄÅÌÅÎÏ ËÁË NOT NULL",
+"îÅ ÍÏÇÕ ÚÁÇÒÕÚÉÔØ ÆÕÎËÃÉÀ '%-.64s'",
+"îÅ ÍÏÇÕ ÉÎÉÃÉÁÌÉÚÉÒÏ×ÁÔØ ÆÕÎËÃÉÀ '%-.64s'; %-.80s",
+"îÅÌØÚÑ ÉÓÐÏÌØÚÏ×ÁÔØ ÐÕÔÉ ÄÌÑ ÒÁÚÄÅÌÑÅÍÙÈ ÂÉÂÌÉÏÔÅË",
+"æÕÎËÃÉÑ '%-.64s' ÕÖÅ ÓÕÝÅÓÔ×ÕÅÔ",
+"îÅ ÍÏÇÕ ÏÔËÒÙÔØ ÒÁÚÄÅÌÑÅÍÕÀ ÂÉÂÌÉÏÔÅËÕ '%-.64s' (ïÛÉÂËÁ: %d %-.64s)",
+"îÅ ÍÏÇÕ ÎÁÊÔÉ ÆÕÎËÃÉÀ '%-.64s' × ÂÉÂÌÉÏÔÅËÅ'",
+"æÕÎËÃÉÑ '%-.64s' ÎÅ ÏÐÒÅÄÅÌÅÎÁ",
+"èÏÓÔ '%-.64s' ÚÁÂÌÏËÉÒÏ×ÁÎ ÉÚ-ÚÁ ÏÂÉÌÉÑ ÏÛÉÂÏË ÓÏÅÄÉÎÅÎÉÑ. òÁÚÂÌÏËÉÒÏ×ÁÔØ ÍÏÖÎÏ Ó ÐÏÍÏÝØÀ 'mysqladmin flush-hosts'",
+"èÏÓÔÕ '%-.64s' ÎÅ ÒÁÚÒÅÛÅÎÏ ÓÏÅÄÉÎÑÔØÓÑ Ó ÄÁÎÎÙÍ ÓÅÒ×ÅÒÏÍ MySQL",
+"÷Ù ÐÏÄËÌÀÞÅÎÙ Ë MySQL ËÁË ÁÎÏÎÉÍÎÙÊ ÐÏÌØÚÏ×ÁÔÅÌØ É ÷ÁÍ ÎÅ ÒÁÚÒÅÛÅÎÏ ÉÚÍÅÎÑÔØ ÐÁÒÏÌÉ",
+"÷Ù ÄÏÌÖÎÙ ÉÍÅÔØ ÐÒÁ×Á ÎÁ ÏÂÎÏ×ÌÅÎÉÅ ÔÁÂÌÉÃ × ÂÁÚÅ, ÞÔÏÂÙ ÉÚÍÅÎÉÔØ ÐÁÒÏÌØ ÄÒÕÇÉÍ ÐÏÌØÚÏ×ÁÔÅÌÑÍ",
+"îÅ ÍÏÇÕ ÎÁÊÔÉ ÎÉ ÏÄÎÏÇÏ ÓÏÏÔ×ÅÔÓÔ×ÉÑ × ÔÁÂÌÉÃÅ ÐÏÌØÚÏ×ÁÔÅÌÅÊ",
+"óÏÏÔ×ÅÔÓÔ×ÕÀÝÉÈ ÚÁÐÉÓÅÊ: %ld éÚÍÅÎÅÎÏ: %ld ðÒÅÄÕÐÒÅÖÄÅÎÉÊ: %ld",
+"îÅ ÍÏÇÕ ÓÏÚÄÁÔØ ÎÏ×ÕÀ ÎÉÔØ (ÏÛÉÂËÁ %d). åÓÌÉ ÜÔÏ ÎÅ ÉÚ-ÚÁ ÎÅÈ×ÁÔËÉ ÐÁÍÑÔÉ, ÐÏÓÍÏÔÒÉÔÅ × ÒÕËÏ×ÏÄÓÔ×Å ×ÏÚÍÏÖÎÙÅ OS-ÚÁ×ÉÓÉÍÙÅ ÇÌÀËÉ",
+"þÉÓÌÏ ÓÔÏÌÂÃÏ× ÎÅ ÓÏÏÔ×ÅÔÓÔ×ÕÅÔ ÞÉÓÌÕ ÚÎÁÞÅÎÉÊ × ÓÔÒÏËÅ %ld",
+"îÅ ÍÏÇÕ ÚÁÎÏ×Ï ÏÔËÒÙÔØ ÔÁÂÌÉÃÕ: '%-.64s',
+"îÅÐÒÁ×ÉÌØÎÏÅ ÉÓÐÏÌØÚÏ×ÁÎÉÅ ÚÎÁÞÅÎÉÑ NULL",
+"REGEXP ×ÅÒÎÕÌ ÏÛÉÂËÕ '%-.64s'",
+"éÓÐÏÌØÚÏ×ÁÎÉÅ ÁÇÒÅÇÁÔÎÙÈ ÆÕÎËÃÉÊ (MIN(),MAX(),COUNT()...) ÓÏ×ÍÅÓÔÎÏ Ó ÏÂÙÞÎÙÍÉ ÚÎÁÞÅÎÉÑÍÉ ×ÏÚÍÏÖÎÏ ÔÏÌØËÏ ÐÒÉ ÎÁÌÉÞÉÉ ÒÁÚÄÅÌÁ GROUP BY",
+"äÌÑ ÐÏÌØÚÏ×ÁÔÅÌÑ '%-.32s' Ó ÈÏÓÔÁ '%-.64s' ÐÒÉ×ÉÌÅÇÉÉ ÎÅ ÏÐÒÅÄÅÌÅÎÙ",
+"%-.16s ËÏÍÁÎÄÁ ÎÅ ÒÁÚÒÅÛÅÎÁ ÐÏÌØÚÏ×ÁÔÅÌÀ: '%-.32s@%-.64s' ÄÌÑ ÔÁÂÌÉÃÙ '%-.64s'",
+"%-.16s ËÏÍÁÎÄÁ ÎÅ ÒÁÚÒÅÛÅÎÁ ÐÏÌØÚÏ×ÁÔÅÌÀ: '%-.32s@%-.64s'\n ÄÌÑ ÐÏÌÑ '%-.64s' ÉÚ ÔÁÂÌÉÃÙ '%-.64s'",
+"úÁÄÁÎÙ ÎÅ×ÅÒÎÙÅ ÐÒÉ×ÉÌÅÇÉÉ ÄÌÑ ÔÁÂÌÉÃÙ",
+"éÍÑ ÈÏÓÔÁ ÉÌÉ ÐÏÌØÚÏ×ÁÔÅÌÑ ÓÌÉÛËÏÍ ×ÅÌÉËÏ ÄÌÑ ÔÁÂÌÉÃÙ ÐÒÉ×ÉÌÅÇÉÊ",
+"ôÁÂÌÉÃÁ '%-.64s.%-.64s' ÎÅ ÓÕÝÅÓÔ×ÕÅÔ",
+"ðÒÉ×ÉÌÅÇÉÉ ÐÏÌØÚÏ×ÁÔÅÌÑ '%-.32s' Ó ÈÏÓÔÁ '%-.64s' ÄÌÑ ÔÁÂÌÉÃÙ '%-.64s' ÎÅ ÏÐÒÅÄÅÌÅÎÙ",
+"äÁÎÎÁÑ ËÏÍÁÎÄÁ ÎÅ ÐÏÄÄÅÒÖÉ×ÁÅÔÓÑ ÜÔÏÊ ×ÅÒÓÉÅÊ MySQL",
+"ëÁËÁÑ-ÔÏ ÓÉÎÔÁËÓÉÞÅÓËÁÑ ÏÛÉÂËÁ",
+"ðÏÔÏË ÄÌÑ delayed insert ÎÅ ÍÏÖÅÔ ÐÏÌÕÞÉÔØ ÂÌÏËÉÒÏ×ËÕ ÄÌÑ ÔÁÂÌÉÃÙ %-.64s",
+"éÓÐÏÌØÚÕÅÔÓÑ ÓÌÉÛËÏÍ ÍÎÏÇÏ delayed ÐÏÔÏËÏ×",
+"ðÒÅÒ×ÁÎÎÁÑ Ó×ÑÚØ %ld Ó ÂÁÚÏÊ ÄÁÎÎÙÈ: '%-.64s' ÐÏÌØÚÏ×ÁÔÅÌØ: '%-.64s' (%-.64s)",
+"ðÁËÅÔ ÂÏÌØÛÅ ÞÅÍ 'max_allowed_packet'",
+"ïÛÉÂËÁ ÞÔÅÎÉÑ ÉÚ ÔÒÕÂÙ ËÏÎÎÅËÔÁ",
+"fcntl() ×ÅÒÎÕÌ ÏÛÉÂËÕ",
+"ðÏÌÕÞÅÎ ÐÁËÅÔ × ÎÅÐÒÁ×ÉÌØÎÏÍ ÐÏÒÑÄËÅ",
+"îÅ ÍÏÇÕ ÒÁÓÐÁËÏ×ÁÔØ ÐÁËÅÔ",
+"ïÛÉÂËÁ ÐÒÉ ÞÔÅÎÉÉ ÐÁËÅÔÏ×"
+"Timeout ÐÒÉ ÞÔÅÎÉÉ ÐÁËÅÔÏ×",
+"ïÛÉÂËÁ ÐÒÉ ÏÔÐÒÁ×ËÅ ÐÁËÅÔÏ×",
+"ïÛÉÂËÁ ÐÒÉ ÏÔÐÒÁ×ËÅ ÐÁËÅÔÏ×",
+"òÅÚÕÌØÔÉÒÕÀÝÁÑ ÓÔÒÏËÁ ÂÏÌØÛÅ ÞÅÍ max_allowed_packet",
+"éÓÐÏÌØÚÕÅÍÁÑ ÔÁÂÌÉÃÁ ÎÅ ÐÏÄÄÅÒÖÉ×ÁÅÔ ÐÏÌÑ BLOB/TEXT",
+"éÓÐÏÌØÚÕÅÍÁÑ ÔÁÂÌÉÃÁ ÎÅ ÐÏÄÄÅÒÖÉ×ÁÅÔ ÐÏÌÑ AUTO_INCREMENT",
+"INSERT DELAYED ÎÅ ÍÏÖÅÔ ÉÓÐÏÌØÚÏ×ÁÔØÓÑ Ó ÔÁÂÌÉÃÅÊ '%-.64s', ÏÎÁ ÚÁÎÑÔÁ ÉÓÐÏÌØÚÏ×ÁÎÉÅÍ LOCK TABLES",
+"îÅ×ÅÒÎÏÅ ÉÍÑ ÐÏÌÑ '%-.100s'",
+"éÓÐÏÌØÚÕÅÍÙÊ table handler ÎÅ ÍÏÖÅÔ ÉÎÄÅËÓÉÒÏ×ÁÔØ ÐÏÌÅ '%-.64s'",
+"All tables in the MERGE table are not defined identically",
+"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",
+"Result consisted of more than one row",
+"This table type requires a primary key",
+"This version of MySQL is not compiled with RAID support",
+"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",
+"You are not allowed to execute this command in a transaction",
+"Got error %d during COMMIT",
+"Got error %d during ROLLBACK",
+"Got error %d during FLUSH_LOGS",
+"Got error %d during CHECKPOINT",
+"Aborted connection %ld to db: '%-.64s' user: '%-.32s' host: `%-.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",
+"FULLTEXT ÉÎÄÅËÓ, ÓÏÏÔ×ÅÔÓÔ×ÕÀÝÉÊ ÚÁÄÁÎÎÏÍÕ ÓÐÉÓËÕ ÓÔÏÌÂÃÏ×, ÎÅ ÎÁÊÄÅÎ",
diff --git a/sql/share/slovak/errmsg.sys b/sql/share/slovak/errmsg.sys
new file mode 100644
index 00000000000..c14049c829f
--- /dev/null
+++ b/sql/share/slovak/errmsg.sys
Binary files differ
diff --git a/sql/share/slovak/errmsg.txt b/sql/share/slovak/errmsg.txt
new file mode 100644
index 00000000000..7b5d8f9a015
--- /dev/null
+++ b/sql/share/slovak/errmsg.txt
@@ -0,0 +1,203 @@
+/* Copyright Abandoned 1997 TCX DataKonsult AB & Monty Program KB & Detron HB
+ This file is public domain and comes with NO WARRANTY of any kind */
+
+/*
+ Translated from both E n g l i s h & C z e c h error messages
+ by steve: (billik@sun.uniag.sk).
+ Encoding: ISO LATIN-8852-2
+ Server version: 3.21.25-gamma
+ Date: Streda 11. November 1998 20:58:15
+*/
+
+"hashchk",
+"isamchk",
+"NIE",
+"Áno",
+"Nemô¾em vytvori» súbor '%-.64s' (chybový kód: %d)",
+"Nemô¾em vytvori» tabuµku '%-.64s' (chybový kód: %d)",
+"Nemô¾em vytvori» databázu '%-.64s'. (chybový kód: %d)",
+"Nemô¾em vytvori» databázu '%-.64s'. Databáza existuje",
+"Nemô¾em zmaza» databázu '%-.64s'. Databáza neexistuje",
+"Chyba pri mazaní databázy (nemô¾em zmaza» '%-.64s', chybový kód: %d)",
+"Chyba pri mazaní databázy (nemô¾em vymaza» adresár '%-.64s', chybový kód: %d)",
+"Chyba pri mazaní '%-.64s' (chybový kód: %d)",
+"Nemô¾em èíta» záznam v systémovej tabuµke",
+"Nemô¾em zisti» stav '%-.64s' (chybový kód: %d)",
+"Nemô¾em zisti» pracovný adresár (chybový kód: %d)",
+"Nemô¾em zamknú» súbor (chybový kód: %d)",
+"Nemô¾em otvori» súbor: '%-.64s'. (chybový kód: %d)",
+"Nemô¾em nájs» súbor: '%-.64s' (chybový kód: %d)",
+"Nemô¾em èíta» adresár '%-.64s' (chybový kód: %d)",
+"Nemô¾em vojs» do adresára '%-.64s' (chybový kód: %d)",
+"Záznam bol zmenený od posledného èítania v tabuµke '%-.64s'",
+"Disk je plný (%s), èakám na uvoµnenie miesta....",
+"Nemô¾em zapísa», duplikát kµúèa v tabuµke '%-.64s'",
+"Chyba pri zatváraní '%-.64s' (chybový kód: %d)",
+"Chyba pri èítaní súboru '%-.64s' (chybový kód: %d)",
+"Chyba pri premenovávaní '%-.64s' na '%-.64s' (chybový kód: %d)",
+"Chyba pri zápise do súboru '%-.64s' (chybový kód: %d)",
+"'%-.64s' je zamknutý proti zmenám",
+"Triedenie preru¹ené",
+"Pohµad '%-.64s' neexistuje pre '%-.64s'",
+"Obsluha tabuµky vrátila chybu %d",
+"Obsluha tabuµky '%-.64s' nemá tento parameter",
+"Nemô¾em nájs» záznam v '%-.64s'",
+"Nesprávna informácia v súbore: '%-.64s'",
+"Nesprávny kµúè pre tabuµku '%-.64s'. Pokúste sa ho opravi»",
+"Starý kµúèový súbor pre '%-.64s'; Opravte ho!",
+"'%-.64s' is èíta» only",
+"Málo pamäti. Re¹tartujte daemona a skúste znova (je potrebných %d bytov)",
+"Málo pamäti pre triedenie, zvý¹te veµkos» triediaceho bufferu",
+"Neoèakávaný koniec súboru pri èítaní '%-.64s' (chybový kód: %d)",
+"Príli¹ mnoho spojení",
+"Málo miesta-pamäti pre vlákno",
+"Nemô¾em zisti» meno hostiteµa pre va¹u adresu",
+"Chyba pri nadväzovaní spojenia",
+"Zakázaný prístup pre u¾ívateµa: '%-.32s@%-.64s' k databázi '%-.64s'",
+"Zakázaný prístup pre u¾ívateµa: '%-.32s@%-.64s' (pou¾itie hesla: %s)",
+"Nebola vybraná databáza",
+"Neznámy príkaz",
+"Pole '%-.64s' nemô¾e by» null",
+"Neznáma databáza '%-.64s'",
+"Tabuµka '%-.64s' u¾ existuje",
+"Neznáma tabuµka '%-.64s'",
+"Pole: '%-.64s' v %-.64s je nejasné",
+"Prebieha ukonèovanie práce servera",
+"Neznáme pole '%-.64s' v '%-.64s'",
+"Pou¾ité '%-.64s' nebolo v 'group by'",
+"Nemô¾em pou¾i» 'group' na '%-.64s'",
+"Príkaz obsahuje zároveò funkciu 'sum' a poµa",
+"Poèet polí nezodpovedá zadanej hodnote",
+"Meno identifikátora '%-.100s' je príli¹ dlhé",
+"Opakované meno poµa '%-.64s'",
+"Opakované meno kµúèa '%-.64s'",
+"Opakovaný kµúè '%-.64s' (èíslo kµúèa %d)",
+"Chyba v ¹pecifikácii poµa '%-.64s'",
+"%s blízko '%-.80s' na riadku %d",
+"Výsledok po¾iadavky bol prázdny",
+"Nie jednoznaèná tabuµka/alias: '%-.64s'",
+"Chybná implicitná hodnota pre '%-.64s'",
+"Zadefinovaných viac primárnych kµúèov",
+"Zadaných ríli¹ veµa kµúèov. Najviac %d kµúèov je povolených",
+"Zadaných ríli¹ veµa èastí kµúèov. Je povolených najviac %d èastí",
+"Zadaný kµúè je príli¹ dlhý, najväè¹ia då¾ka kµúèa je %d",
+"Kµúèový ståpec '%-.64s' v tabuµke neexistuje",
+"Blob pole '%-.64s' nemô¾e by» pou¾ité ako kµúè",
+"Príli¹ veµká då¾ka pre pole '%-.64s' (maximum = %d). Pou¾ite BLOB",
+"Mô¾ete ma» iba jedno AUTO pole a to musí by» definované ako kµúè",
+"%s: pripravený na spojenie\n",
+"%s: normálne ukonèenie\n",
+"%s: prijatý signál %d, ukonèenie (Abort)!\n",
+"%s: práca ukonèená\n",
+"%s: násilné ukonèenie vlákna %ld u¾ívateµa '%-.64s'\n",
+"Nemô¾em vytvori» IP socket",
+"Tabuµka '%-.64s' nemá index zodpovedajúci CREATE INDEX. Vytvorte tabulku znova",
+"Argument oddeµovaè polí nezodpovedá po¾iadavkám. Skontrolujte v manuáli","
+"Nie je mo¾né pou¾i» fixnú då¾ku s BLOBom. Pou¾ite 'fields terminated by'.",
+"Súbor '%-.64s' musí by» v adresári databázy, alebo èitateµný pre v¹etkých",
+"Súbor '%-.64s' u¾ existuje",
+"Záznamov: %ld Zmazaných: %ld Preskoèených: %ld Varovania: %ld",
+"Záznamov: %ld Opakovaných: %ld",
+"Wrong sub part key. The used key part isn't a string or the used length is longer than the key part",
+"One nemô¾em zmaza» all fields with ALTER TABLE. Use DROP TABLE instead",
+"Nemô¾em zru¹i» (DROP) '%-.64s'. Skontrolujte, èi neexistujú záznamy/kµúèe",
+"Záznamov: %ld Opakovaných: %ld Varovania: %ld",
+"INSERT TABLE '%-.64s' nie je dovolené v zozname tabuliek FROM",
+"Neznáma identifikácia vlákna: %lu",
+"Nie ste vlastníkom vlákna %lu",
+"Nie je pou¾itá ¾iadna tabuµka",
+"Príli¹ mnoho re»azcov pre pole %-.64s a SET",
+"Nemô¾em vytvori» unikátne meno log-súboru %-.64s.(1-999)\n",
+"Tabuµka '%-.64s' bola zamknutá s READ a nemô¾e by» zmenená",
+"Tabuµka '%-.64s' nebola zamknutá s LOCK TABLES",
+"Pole BLOB '%-.64s' nemô¾e ma» implicitnú hodnotu",
+"Neprípustné meno databázy '%-.100s'",
+"Neprípustné meno tabuµky '%-.100s'",
+"Zadaná po¾iadavka SELECT by prechádzala príli¹ mnoho záznamov a trvala by príli¹ dlho. Skontrolujte tvar WHERE a ak je v poriadku, pou¾ite SET OPTION SQL_BIG_SELECTS=1",
+"Neznámá chyba",
+"Neznámá procedúra '%-.64s'",
+"Chybný poèet parametrov procedúry '%-.64s'",
+"Chybné parametre procedúry '%-.64s'",
+"Neznáma tabuµka '%-.64s' v %s",
+"Pole '%-.64s' je zadané dvakrát",
+"Nesprávne pou¾itie funkcie GROUP",
+"Tabuµka '%-.64s' pou¾íva roz¹írenie, ktoré v tejto verzii MySQL nie je",
+"Tabuµka musí ma» aspoò 1 pole",
+"Tabuµka '%-.64s' je plná",
+"Neznáma znaková sada: '%-.64s'",
+"Príli¹ mnoho tabuliek. MySQL mô¾e pou¾i» len %d v JOIN-e",
+"Príli¹ mnoho polí",
+"Riadok je príli¹ veµký. Maximálna veµkos» riadku, okrem 'BLOB', je %d. Musíte zmeni» niektoré polo¾ky na BLOB",
+"Preteèenie zásobníku vlákna: pou¾ité: %ld z %ld. Pou¾ite 'mysqld -O thread_stack=#' k zadaniu väè¹ieho zásobníka",
+"V OUTER JOIN bol nájdený krí¾ový odkaz. Skontrolujte podmienky ON",
+"Pole '%-.64s' je pou¾ité s UNIQUE alebo INDEX, ale nie je zadefinované ako NOT NULL",
+"Nemô¾em naèíta» funkciu '%-.64s'",
+"Nemô¾em inicializova» funkciu '%-.64s'; %-.80s",
+"Neprípustné ¾iadne cesty k zdieµanej kni¾nici",
+"Funkcia '%-.64s' u¾ existuje",
+"Nemô¾em otvori» zdieµanú kni¾nicu '%-.64s' (chybový kód: %d %s)",
+"Nemô¾em nájs» funkciu '%-.64s' v kni¾nici'",
+"Funkcia '%-.64s' nie je definovaná",
+"Host '%-.64s' is blocked because of many connection errors. Unblock with 'mysqladmin flush-hosts'",
+"Host '%-.64s' is not allowed to connect to this MySQL server",
+"You are using MySQL as an anonymous users and anonymous users are not allowed to change passwords",
+"You must have privileges to update tables in the mysql database to be able to change passwords for others",
+"Can't find any matching row in the user table",
+"Rows matched: %ld Changed: %ld Warnings: %ld",
+"Can't create a new thread (errno %d). If you are not out of available memory, you can consult the manual for a possible OS-dependent bug",
+"Column count doesn't match value count at row %ld",
+"Can't reopen table: '%-.64s',
+"Invalid use of NULL value",
+"Got error '%-.64s' from regexp",
+"Mixing of GROUP columns (MIN(),MAX(),COUNT()...) with no GROUP columns is illegal if there is no GROUP BY clause",
+"There is no such grant defined for user '%-.32s' on host '%-.64s'",
+"%-.16s command denied to user: '%-.32s@%-.64s' for table '%-.64s'",
+"%-.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",
+"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",
+"Delayed insert thread couldn't get requested lock for table %-.64s",
+"Too many delayed threads in use",
+"Aborted connection %ld to db: '%-.64s' user: '%-.64s' (%s)",
+"Got a packet bigger than 'max_allowed_packet'",
+"Got a read error from the connection pipe",
+"Got an error from fcntl()",
+"Got packets out of order",
+"Couldn't uncompress communication packet",
+"Got an error reading communication packets"
+"Got timeout reading communication packets",
+"Got an error writing communication packets",
+"Got timeout writing communication packets",
+"Result string is longer than max_allowed_packet",
+"The used table type doesn't support BLOB/TEXT columns",
+"The used table type doesn't support AUTO_INCREMENT columns",
+"INSERT DELAYED can't be used with table '%-.64s', because it is locked with LOCK TABLES",
+"Incorrect column name '%-.100s'",
+"The used table handler can't index column '%-.64s'",
+"All tables in the MERGE table are not defined identically",
+"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",
+"Result consisted of more than one row",
+"This table type requires a primary key",
+"This version of MySQL is not compiled with RAID support",
+"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",
+"You are not allowed to execute this command in a transaction",
+"Got error %d during COMMIT",
+"Got error %d during ROLLBACK",
+"Got error %d during FLUSH_LOGS",
+"Got error %d during CHECKPOINT",
+"Aborted connection %ld to db: '%-.64s' user: '%-.32s' host: `%-.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",
diff --git a/sql/share/spanish/errmsg.sys b/sql/share/spanish/errmsg.sys
new file mode 100644
index 00000000000..60c71cb8e8f
--- /dev/null
+++ b/sql/share/spanish/errmsg.sys
Binary files differ
diff --git a/sql/share/spanish/errmsg.txt b/sql/share/spanish/errmsg.txt
new file mode 100644
index 00000000000..0f16b66f6d8
--- /dev/null
+++ b/sql/share/spanish/errmsg.txt
@@ -0,0 +1,196 @@
+/* Copyright Abandoned 1997 TCX DataKonsult AB & Monty Program KB & Detron HB
+ This file is public domain and comes with NO WARRANTY of any kind
+ Traduccion por Miguel Angel Fernandez Roiz -- LoboCom Sistemas, s.l. */
+
+"hashchk",
+"isamchk",
+"NO",
+"SI",
+"No puedo crear archivo '%-.64s' (Error: %d)",
+"No puedo crear tabla '%-.64s' (Error: %d)",
+"No puedo crear base de datos '%-.64s'. Error %d",
+"No puedo crear base de datos '%-.64s'. La base de datos ya existe",
+"No puedo eliminar base de datos '%-.64s'. La base de datos no existe",
+"Error eliminando la base de datos(no puedo borrar '%-.64s', error %d)",
+"Error eliminando la base de datos (No puedo borrar directorio '%-.64s', error %d)",
+"Error en el borrado de '%-.64s' (Error: %d)",
+"No puedo leer el registro en la tabla del sistema",
+"No puedo obtener el estado de '%-.64s' (Error: %d)",
+"No puedo acceder al directorio (Error: %d)",
+"No puedo bloquear archivo: (Error: %d)",
+"No puedo abrir archivo: '%-.64s'. (Error: %d)",
+"No puedo encontrar archivo: '%-.64s' (Error: %d)",
+"No puedo leer el directorio de '%-.64s' (Error: %d)",
+"No puedo cambiar al directorio de '%-.64s' (Error: %d)",
+"El registro ha cambiado desde la ultima lectura de la tabla '%-.64s'",
+"Disco lleno (%s). Esperando para que se libere algo de espacio....",
+"No puedo escribir, clave duplicada en la tabla '%-.64s'",
+"Error en el cierre de '%-.64s' (Error: %d)",
+"Error leyendo el fichero '%-.64s' (Error: %d)",
+"Error en el renombrado de '%-.64s' a '%-.64s' (Error: %d)",
+"Error escribiendo el archivo '%-.64s' (Error: %d)",
+"'%-.64s' esta bloqueado contra cambios",
+"Ordeancion cancelada",
+"La vista '%-.64s' no existe para '%-.64s'",
+"Error %d desde el manejador de la tabla",
+"El manejador de la tabla de '%-.64s' no tiene esta opcion",
+"No puedo encontrar el registro en '%-.64s'",
+"Informacion erronea en el archivo: '%-.64s'",
+"Clave de archivo erronea para la tabla: '%-.64s'. Intente repararlo",
+"Clave de archivo antigua para la tabla '%-.64s'; Reparelo!",
+"'%-.64s' es de solo lectura",
+"Memoria insuficiente. Reinicie el demonio e intentelo otra vez (necesita %d bytes)",
+"Memoria de ordenacion insuficiente. Incremente el tamano del buffer de ordenacion",
+"Inesperado fin de ficheroU mientras leiamos el archivo '%-.64s' (Error: %d)",
+"Demasiadas conexiones",
+"Memoria/espacio de tranpaso insuficiente",
+"No puedo obtener el nombre de maquina de tu direccion",
+"Protocolo erroneo",
+"Access denied for user: '%-.32s@%-.64s' to database '%-.64s'",
+"Access denied for user: '%-.32s@%-.64s' (Using password: %s)",
+"Base de datos no seleccionada",
+"Comando desconocido",
+"La columna '%-.64s' no puede ser nula",
+"Base de datos desconocida '%-.64s'",
+"La tabla '%-.64s' ya existe",
+"Tabla '%-.64s' desconocida",
+"La columna: '%-.64s' en %s es ambigua",
+"Desconexion de servidor en proceso",
+"La columna '%-.64s' en %s es desconocida",
+"Usado '%-.64s' el cual no esta group by",
+"No puedo agrupar por '%-.64s'",
+"El estamento tiene funciones de suma y columnas en el mismo estamento",
+"La columna con count no tiene valores para contar",
+"El nombre del identificador '%-.64s' es demasiado grande",
+"Nombre de columna duplicado '%-.64s'",
+"Nombre de clave duplicado '%-.64s'",
+"Entrada duplicada '%-.64s' para la clave %d",
+"Especificador de columna erroneo para la columna '%-.64s'",
+"%s cerca '%-.64s' en la linea %d",
+"La query estaba vacia",
+"Tabla/alias: '%-.64s' es no unica",
+"Valor por defecto invalido para '%-.64s'",
+"Multiples claves primarias definidas",
+"Demasiadas claves primarias declaradas. Un maximo de %d claves son permitidas",
+"Demasiadas partes de clave declaradas. Un maximo de %d partes son permitidas",
+"Declaracion de clave demasiado larga. La maxima longitud de clave es %d",
+"La columna clave '%-.64s' no existe en la tabla",
+"La columna Blob '%-.64s' no puede ser usada en una declaracion de clave",
+"Longitud de columna demasiado grande para la columna '%-.64s' (maximo = %d).Usar BLOB en su lugar",
+"Puede ser solamente un campo automatico y este debe ser definido como una clave",
+"%s: preparado para conexiones\n",
+"%s: Apagado normal\n",
+"%s: Recibiendo signal %d. Abortando!\n",
+"%s: Apagado completado\n",
+"%s: Forzando a cerrar el thread %ld usuario: '%-.64s'\n",
+"No puedo crear IP socket",
+"La tabla '%-.64s' no tiene indice como el usado en CREATE INDEX. Crea de nuevo la table",
+"Los separadores de argumentos del campo no son los especificados. Comprueba el manual","
+"No puedes usar longitudes de filas fijos con BLOBs. Por favor usa 'campos terminados por '.",
+"El archivo '%-.64s' debe estar en el directorio de la base de datos o ser de lectura por todos",
+"El archivo '%-.64s' ya existe",
+"Registros: %ld Borrados: %ld Saltados: %ld Peligros: %ld",
+"Registros: %ld Duplicados: %ld",
+"Parte de la calve es erronea. Una parte de la calve no es una cadena o la longitud usada es tan grandecomo la parte de la clave",
+"No puede borrar todos los campos con ALTER TABLE. Usa DROP TABLE para hacerlo",
+"No puedo ELIMINAR '%-.64s'. compuebe que el campo/clave existe",
+"Registros: %ld Duplicados: %ld Peligros: %ld",
+"INSERT TABLE '%-.64s' no esta permitido en FROM tabla lista",
+"Identificador del thread: %lu desconocido",
+"Tu no eres el propietario del thread%lu",
+"No ha tablas usadas",
+"Too many strings for column %s and SET",
+"Can't generate a unique logfilename %s.(1-999)\n",
+"Table '%-.64s' was locked with a READ lock and can't be updated",
+"Table '%-.64s' was not locked with LOCK TABLES",
+"Blob field '%-.64s' can't have a default value",
+"Illegal database name '%-.64s'",
+"Illegal table name '%-.64s'",
+"The SELECT would examine too many records and probably take very long time. Check your WHERE and use SET OPTION SQL_BIG_SELECTS=1 if the SELECT is ok",
+"Unknown error",
+"Unkown procedure %s",
+"Wrong parameter count to procedure %s",
+"Wrong parameters to procedure %s",
+"Unknown table '%-.64s' in %s",
+"Field '%-.64s' specified twice",
+"Invalid use of group function",
+"Table '%-.64s' uses a extension that doesn't exist in this MySQL version",
+"A table must have at least 1 column",
+"The table '%-.64s' is full",
+"Unknown character set: '%-.64s'",
+"Too many tables. MySQL can only use %d tables in a join",
+"Too many fields",
+"Too big row size. The maximum row size, not counting blobs, is %d. You have to change some fields to blobs",
+"Thread stack overrun: Used: %ld of a %ld stack. Use 'mysqld -O thread_stack=#' to specify a bigger stack if needed",
+"Cross dependency found in OUTER JOIN. Examine your ON conditions",
+"Column '%-.32s' is used with UNIQUE or INDEX but is not defined as NOT NULL",
+"Can't load function '%-.64s'",
+"Can't initialize function '%-.64s'; %-.80s",
+"No paths allowed for shared library",
+"Function '%-.64s' already exist",
+"Can't open shared library '%-.64s' (errno: %d %s)",
+"Can't find function '%-.64s' in library'",
+"Function '%-.64s' is not defined",
+"Host '%-.64s' is blocked because of many connection errors. Unblock with 'mysqladmin flush-hosts'",
+"Host '%-.64s' is not allowed to connect to this MySQL server",
+"You are using MySQL as an anonymous users and anonymous users are not allowed to change passwords",
+"You must have privileges to update tables in the mysql database to be able to change passwords for others",
+"Can't find any matching row in the user table",
+"Rows matched: %ld Changed: %ld Warnings: %ld",
+"Can't create a new thread (errno %d). If you are not out of available memory you can consult the manual for any possible OS dependent bug",
+"Column count doesn't match value count at row %ld",
+"Can't reopen table: '%-.64s',
+"Invalid use of NULL value",
+"Got error '%-.64s' from regexp",
+"Mixing of GROUP columns (MIN(),MAX(),COUNT()...) with no GROUP columns is illegal if there is no GROUP BY clause",
+"There is no such grant defined for user '%-.32s' on host '%-.64s'",
+"%-.16s command denied to user: '%-.32s@%-.64s' for table '%-.64s'",
+"%-.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",
+"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",
+"Delayed insert thread couldn't get requested lock for table %-.64s",
+"Too many delayed threads in use",
+"Aborted connection %ld to db: '%-.64s' user: '%-.64s' (%s)",
+"Got a packet bigger than 'max_allowed_packet'",
+"Got a read error from the connection pipe",
+"Got an error from fcntl()",
+"Got packets out of order",
+"Couldn't uncompress communication packet",
+"Got an error reading communication packets"
+"Got timeout reading communication packets",
+"Got an error writing communication packets",
+"Got timeout writing communication packets",
+"Result string is longer than max_allowed_packet",
+"The used table type doesn't support BLOB/TEXT columns",
+"The used table type doesn't support AUTO_INCREMENT columns",
+"INSERT DELAYED can't be used with table '%-.64s', because it is locked with LOCK TABLES",
+"Incorrect column name '%-.100s'",
+"The used table handler can't index column '%-.64s'",
+"All tables in the MERGE table are not defined identically",
+"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",
+"Result consisted of more than one row",
+"This table type requires a primary key",
+"This version of MySQL is not compiled with RAID support",
+"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",
+"You are not allowed to execute this command in a transaction",
+"Got error %d during COMMIT",
+"Got error %d during ROLLBACK",
+"Got error %d during FLUSH_LOGS",
+"Got error %d during CHECKPOINT",
+"Aborted connection %ld to db: '%-.64s' user: '%-.32s' host: `%-.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",
diff --git a/sql/share/swedish/errmsg.OLD b/sql/share/swedish/errmsg.OLD
new file mode 100644
index 00000000000..0181d0febd6
--- /dev/null
+++ b/sql/share/swedish/errmsg.OLD
@@ -0,0 +1,195 @@
+/* Copyright Abandoned 1997 TCX DataKonsult AB & Monty Program KB & Detron HB
+ This file is public domain and comes with NO WARRANTY of any kind */
+
+"hashchk",
+"isamchk",
+"NO",
+"YES",
+"Kan inte skapa filen: '%-.64s' (Felkod: %d)",
+"Kan inte skapa tabellen: '%-.64s' (Felkod: %d)",
+"Kan inte skapa databasen '%-.64s'. (Felkod: %d)",
+"Databasen '%-.64s' existerar redan",
+"Kan inte radera databasen '%-.64s'. Databasen finns inte",
+"Fel vid radering av databasen (Kan inte radera '%-.64s'. Felkod: %d)",
+"Fel vid radering av databasen (Kan inte radera biblioteket '%-.64s'. Felkod: %d)",
+"Kan inte radera filen: '%-.64s' (Felkod: %d)",
+"Hittar inte posten i systemregistret",
+"Kan inte läsa filinformationen (stat) från '%-.64s' (Felkod: %d)",
+"Kan inte inte läsa aktivt bibliotek. (Felkod: %d)",
+"Kan inte låsa filen. (Felkod: %d)",
+"Kan inte använda: '%-.64s'. (Felkod: %d)",
+"Hittar inte filen: '%-.64s'. (Felkod: %d)",
+"Kan inte läsa från bibliotek '%-.64s'. (Felkod: %d)",
+"Kan inte byta till: '%-.64s'. (Felkod: %d)",
+"Posten har förändrats sedan den lästes i register '%-.64s'",
+"Disken är full (%s). Väntar tills det finns ledigt utrymme....",
+"Kan inte skriva, dubbel söknyckel i register '%-.64s'",
+"Fick fel vid stängning av '%-.64s' (Felkod: %d)",
+"Fick fel vid läsning av '%-.64s' (Felkod %d)",
+"Kan inte byta namn från '%-.64s' till '%-.64s' (Felkod: %d)",
+"Fick fel vid skrivning till '%-.64s' (Felkod %d)",
+"'%-.64s' är låst mot användning",
+"Sorteringen avbruten",
+"Formulär '%-.64s' finns inte i '%-.64s'",
+"Fick felkod %d från databashanteraren",
+"Registrets databas har inte denna facilitet",
+"Hittar inte posten",
+"Felaktig fil: '%-.64s'",
+"Fatalt fel vid hantering av register '%-.64s'. Kör en reparation",
+"Gammal nyckelfil '%-.64s'; Reparera registret",
+"'%-.64s' är skyddad mot förändring",
+"Oväntat slut på minnet, starta om programmet och försök på nytt (Behövde %d bytes)",
+"Sorteringsbufferten räcker inte till. Kontrollera startparametrarna",
+"Oväntat filslut vid läsning från '%-.64s' (Felkod: %d)",
+"För många anslutningar",
+"Fick slut på minnet. Kontrollera ifall mysqld eller någon annan process använder allt tillgängligt minne. Ifall inte, försök använda 'ulimit' eller allokera mera swap",
+"Kan inte hitta 'hostname' för din adress",
+"Fel vid initiering av kommunikationen med klienten",
+"Användare '%-.32s@%-.64s' är ej berättigad att använda databasen %-.64s",
+"Användare '%-.32s@%-.64s' är ej berättigad att logga in (Använder lösen: %s)",
+"Ingen databas i användning",
+"Okänt commando",
+"Kolumn '%-.64s' får inte vara NULL",
+"Okänd database '%-.64s'",
+"Tabellen '%-.64s' finns redan",
+"Okänd tabell '%-.64s'",
+"Kolumn: '%-.64s' i %s är inte unik",
+"Servern går nu ned",
+"Okänd kolumn '%-.64s' i %s",
+"'%-.64s' finns inte i GROUP BY",
+"Kan inte använda GROUP BY med '%-.64s'",
+"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",
+"Nyckel namn '%-.64s' finns flera gånger",
+"Dubbel nyckel '%-.64s' för nyckel: %d",
+"Felaktigt kolumn typ för kolumn: '%-.64s'",
+"%s nära '%-.64s' på rad %d",
+"Frågan var tom",
+"Icke unikt tabell/alias: '%-.64s'",
+"Ogiltigt DEFAULT värde för '%-.64s'",
+"Flera PRIMARY KEY använda",
+"För många nycklar använda. Man får ha högst %d nycklar",
+"För många nyckel delar använda. Man får ha högst %d nyckeldelar",
+"För lång nyckel. Högsta tillåtna nyckellängd är %d",
+"Nyckel kolumn '%-.64s' finns inte",
+"En BLOB '%-.64s' kan inte vara nyckel med den använda tabellen typen",
+"För stor kolumnlängd angiven för '%-.64s' (max= %d). Använd en BLOB instället",
+"Det får finnas endast ett AUTO_INCREMENT fält och detta måste vara en nyckel",
+"%s: klar att ta emot klienter\n",
+"%s: Normal avslutning\n",
+"%s: Fick signal %d. Avslutar!\n",
+"%s: Avslutning klar\n",
+"%s: Stänger av tråd %ld användare: '%-.64s'\n",
+"Kan inte skapa IP socket",
+"Tabellen '%-.64s' har inget index som motsvarar det angivna i CREATE INDEX. Skapa om tabellen",
+"Fält separatorerna är inte emotsägande eller för långa. Kontrollera mot manualen",
+"Man kan inte använda fast radlängd med blobs. Använd 'fields terminated by'."
+"Textfilen '%' måste finnas i databas biblioteket eller vara läsbar för alla",
+"Filen '%-.64s' existerar redan",
+"Rader: %ld Bortagna: %ld Dubletter: %ld Varningar: %ld",
+"Rader: %ld Dubletter: %ld",
+"Felaktig delnyckel. Nyckeldelen är inte en sträng eller den angivna längden är längre än kolumnlängden",
+"Man kan inte radera alla fält med ALTER TABLE. Använd DROP TABLE istället",
+"Kan inte ta bort '%-.64s'. Kontrollera att fältet/nyckel finns",
+"Rader: %ld Dubletter: %ld Varningar: %ld",
+"INSERT table '%-.64s' får inte finnas i FROM tabell-listan",
+"Finns inget thread med id %lu",
+"Du är inte ägare till thread %lu",
+"Inga tabeller angivna",
+"För många alternativ till kolumn %s för SET",
+"Kan inte generera ett unikt filnamn %s.(1-999)\n",
+"Tabell '%-.64s' kan inte uppdateras emedan den är låst för läsning",
+"Tabell '%-.64s' är inte låst med LOCK TABLES",
+"BLOB fält '%-.64s' kan inte ha ett DEFAULT värde"
+"Felaktigt databas namn '%-.64s'",
+"Felaktigt tabell namn '%-.64s'",
+"Den angivna frågan skulle troligen ta mycket long tid! Kontrollar din WHERE och använd SET OPTION SQL_BIG_SELECTS=1 ifall du vill hantera stora joins",
+"Oidentifierat fel",
+"Okänd procedur: %s",
+"Felaktigt antal parametrar till procedur %s",
+"Felaktiga parametrar till procedur %s",
+"Okänd tabell '%-.64s' i '%-.64s'",
+"Fält '%-.64s' är redan använt",
+"Felaktig användning av SQL grupp function",
+"Tabell '%-.64s' har en extension som inte finns i denna version av MySQL",
+"Tabeller måste ha minst 1 kolumn",
+"Tabellen '%-.64s' är full",
+"Okänt karaktärset: '%-.64s'",
+"För många tabeller. MySQL can ha högst %d tabeller i en och samma join"
+"För många fält",
+"För stor total rad längd. Den högst tillåtna rad-längden, förutom BLOBs, är %d. Ändra några av dina fält till BLOB",
+"Tråd-stacken tog slut: Har använt %ld av %ld bytes. Använd 'mysqld -O thread_stack=#' ifall du behöver en större stack",
+"Felaktigt referens i OUTER JOIN. Kontrollera ON uttrycket",
+"Kolumn '%-.32s' är använd med UNIQUE eller INDEX men är inte definerad med NOT NULL",
+"Kan inte ladda funktionen '%-.64s'",
+"Kan inte initialisera funktionen '%-.64s'; '%-.80s'",
+"Man får inte ange sökväg för dynamiska bibliotek",
+"Funktionen '%-.64s' finns redan",
+"Kan inte öppna det dynamiska biblioteket '%-.64s' (Felkod: %d %s)",
+"Hittar inte funktionen '%-.64s' in det dynamiska biblioteket",
+"Funktionen '%-.64s' är inte definierad",
+"Denna dator '%-.64s' är blockerad pga många felaktig paket. Gör 'mysqladmin flush-hosts' för att ta bort alla blockeringarna",
+"Denna dator '%-.64s' har inte privileger att använda denna MySQL server",
+"Du använder MySQL som en anonym användare och som sådan får du inte ändra ditt lösenord",
+"För att ändra lösenord för andra måste du ha rättigheter att uppdatera mysql databasen",
+"Hittade inte användaren i 'user' tabellen",
+"Rader: %ld Uppdaterade: %ld Varningar: %ld",
+"Kan inte skapa en ny tråd (errno %d)"
+"Antalet kolumner motsvarar inte antalet värden på rad: %ld",
+"Kunde inte stänga och öppna tabell: '%-.64s',
+"Felaktig använding av NULL",
+"Fix fel '%-.64s' från REGEXP",
+"Man får ha både GROUP kolumner (MIN(),MAX(),COUNT()...) och fält i en fråga om man inte har en GROUP BY del",
+"Det finns inget privilegium definierat för användare '%-.32s' på '%-.64s'",
+"%-.16s ej tillåtet för '%-.32s@%-.64s' för tabell '%-.64s'",
+"%-.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 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",
+"DELAYED INSERT tråden kunde inte låsa tabell '%-.64s'",
+"Det finns redan 'max_delayed_threads' trådar i använding",
+"Avbröt länken för tråd %ld till db: '%-.64s' användare: '%-.64s' (%s)",
+"Kommunkationspaketet är större än 'max_allowed_packet'",
+"Fick läsfel från klienten vid läsning från 'PIPE'",
+"Fick fatalt fel från 'fcntl()'",
+"Kommunikationspaketen kom i fel ordning",
+"Kunde inte packa up kommunikationspaketet",
+"Fick ett fel vid läsning från klienten",
+"Fick 'timeout' vid läsning från klienten",
+"Fick ett fel vid skrivning till klienten",
+"Fick 'timeout' vid skrivning till klienten",
+"Resultat strängen är längre än max_allowed_packet",
+"Den använda tabell typen kan inte hantera BLOB/TEXT kolumner",
+"Den använda tabell typen kan inte hantera AUTO_INCREMENT kolumner",
+"INSERT DELAYED kan inte användas med tabell '%-.64s', emedan den är låst med LOCK TABLES",
+"Felaktigt column namn '%-.100s'",
+"Den använda tabell typen kan inte indexera kolumn '%-.64s'",
+"Tabellerna i MERGE tabellen är inte identiskt definierade",
+"Kan inte skriva till tabell '%-.64s'; UNIQUE test",
+"Du har inte angett en nyckel längd för BLOB '%-.64s'",
+"Alla delar av en PRIMARY KEY måste vara NOT NULL; Om du vill ha en nyckel med NULL, använd UNIQUE istället",
+"Resultet bestod av mera än en rad",
+"Denna tabell typ kräver en PRIMARY KEY",
+"Denna version av MySQL är inte kompilerad med RAID",
+"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",
+"Du får inte utföra detta kommando i en transaktion",
+"Fick fel %d vid COMMIT",
+"Fick fel %d vid ROLLBACK",
+"Fick fel %d vid FLUSH_LOGS",
+"Fick fel %d vid CHECKPOINT",
+"Avbröt länken för tråd %ld till db: '%-.64s' användare: '%-.32s' Host: '%-.64s' (%.-64s)",
+"Tabellhanteraren klarar inte en binär kopiering av tabellen",
+"Binärloggen stängdes medan vi gjorde FLUSH MASTER",
+"Failed rebuilding the index of dumped table '%-.64s'",
+"Fick en master: '%-.64s'",
+"Fick nätverksfel vid läsning från master",
+"Fick nätverksfel vid skrivning till master",
+"Hittar inte ett FULLTEXT index i kolumnlist",
diff --git a/sql/share/swedish/errmsg.sys b/sql/share/swedish/errmsg.sys
new file mode 100644
index 00000000000..d48cd88bf7b
--- /dev/null
+++ b/sql/share/swedish/errmsg.sys
Binary files differ
diff --git a/sql/share/swedish/errmsg.txt b/sql/share/swedish/errmsg.txt
new file mode 100644
index 00000000000..0181d0febd6
--- /dev/null
+++ b/sql/share/swedish/errmsg.txt
@@ -0,0 +1,195 @@
+/* Copyright Abandoned 1997 TCX DataKonsult AB & Monty Program KB & Detron HB
+ This file is public domain and comes with NO WARRANTY of any kind */
+
+"hashchk",
+"isamchk",
+"NO",
+"YES",
+"Kan inte skapa filen: '%-.64s' (Felkod: %d)",
+"Kan inte skapa tabellen: '%-.64s' (Felkod: %d)",
+"Kan inte skapa databasen '%-.64s'. (Felkod: %d)",
+"Databasen '%-.64s' existerar redan",
+"Kan inte radera databasen '%-.64s'. Databasen finns inte",
+"Fel vid radering av databasen (Kan inte radera '%-.64s'. Felkod: %d)",
+"Fel vid radering av databasen (Kan inte radera biblioteket '%-.64s'. Felkod: %d)",
+"Kan inte radera filen: '%-.64s' (Felkod: %d)",
+"Hittar inte posten i systemregistret",
+"Kan inte läsa filinformationen (stat) från '%-.64s' (Felkod: %d)",
+"Kan inte inte läsa aktivt bibliotek. (Felkod: %d)",
+"Kan inte låsa filen. (Felkod: %d)",
+"Kan inte använda: '%-.64s'. (Felkod: %d)",
+"Hittar inte filen: '%-.64s'. (Felkod: %d)",
+"Kan inte läsa från bibliotek '%-.64s'. (Felkod: %d)",
+"Kan inte byta till: '%-.64s'. (Felkod: %d)",
+"Posten har förändrats sedan den lästes i register '%-.64s'",
+"Disken är full (%s). Väntar tills det finns ledigt utrymme....",
+"Kan inte skriva, dubbel söknyckel i register '%-.64s'",
+"Fick fel vid stängning av '%-.64s' (Felkod: %d)",
+"Fick fel vid läsning av '%-.64s' (Felkod %d)",
+"Kan inte byta namn från '%-.64s' till '%-.64s' (Felkod: %d)",
+"Fick fel vid skrivning till '%-.64s' (Felkod %d)",
+"'%-.64s' är låst mot användning",
+"Sorteringen avbruten",
+"Formulär '%-.64s' finns inte i '%-.64s'",
+"Fick felkod %d från databashanteraren",
+"Registrets databas har inte denna facilitet",
+"Hittar inte posten",
+"Felaktig fil: '%-.64s'",
+"Fatalt fel vid hantering av register '%-.64s'. Kör en reparation",
+"Gammal nyckelfil '%-.64s'; Reparera registret",
+"'%-.64s' är skyddad mot förändring",
+"Oväntat slut på minnet, starta om programmet och försök på nytt (Behövde %d bytes)",
+"Sorteringsbufferten räcker inte till. Kontrollera startparametrarna",
+"Oväntat filslut vid läsning från '%-.64s' (Felkod: %d)",
+"För många anslutningar",
+"Fick slut på minnet. Kontrollera ifall mysqld eller någon annan process använder allt tillgängligt minne. Ifall inte, försök använda 'ulimit' eller allokera mera swap",
+"Kan inte hitta 'hostname' för din adress",
+"Fel vid initiering av kommunikationen med klienten",
+"Användare '%-.32s@%-.64s' är ej berättigad att använda databasen %-.64s",
+"Användare '%-.32s@%-.64s' är ej berättigad att logga in (Använder lösen: %s)",
+"Ingen databas i användning",
+"Okänt commando",
+"Kolumn '%-.64s' får inte vara NULL",
+"Okänd database '%-.64s'",
+"Tabellen '%-.64s' finns redan",
+"Okänd tabell '%-.64s'",
+"Kolumn: '%-.64s' i %s är inte unik",
+"Servern går nu ned",
+"Okänd kolumn '%-.64s' i %s",
+"'%-.64s' finns inte i GROUP BY",
+"Kan inte använda GROUP BY med '%-.64s'",
+"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",
+"Nyckel namn '%-.64s' finns flera gånger",
+"Dubbel nyckel '%-.64s' för nyckel: %d",
+"Felaktigt kolumn typ för kolumn: '%-.64s'",
+"%s nära '%-.64s' på rad %d",
+"Frågan var tom",
+"Icke unikt tabell/alias: '%-.64s'",
+"Ogiltigt DEFAULT värde för '%-.64s'",
+"Flera PRIMARY KEY använda",
+"För många nycklar använda. Man får ha högst %d nycklar",
+"För många nyckel delar använda. Man får ha högst %d nyckeldelar",
+"För lång nyckel. Högsta tillåtna nyckellängd är %d",
+"Nyckel kolumn '%-.64s' finns inte",
+"En BLOB '%-.64s' kan inte vara nyckel med den använda tabellen typen",
+"För stor kolumnlängd angiven för '%-.64s' (max= %d). Använd en BLOB instället",
+"Det får finnas endast ett AUTO_INCREMENT fält och detta måste vara en nyckel",
+"%s: klar att ta emot klienter\n",
+"%s: Normal avslutning\n",
+"%s: Fick signal %d. Avslutar!\n",
+"%s: Avslutning klar\n",
+"%s: Stänger av tråd %ld användare: '%-.64s'\n",
+"Kan inte skapa IP socket",
+"Tabellen '%-.64s' har inget index som motsvarar det angivna i CREATE INDEX. Skapa om tabellen",
+"Fält separatorerna är inte emotsägande eller för långa. Kontrollera mot manualen",
+"Man kan inte använda fast radlängd med blobs. Använd 'fields terminated by'."
+"Textfilen '%' måste finnas i databas biblioteket eller vara läsbar för alla",
+"Filen '%-.64s' existerar redan",
+"Rader: %ld Bortagna: %ld Dubletter: %ld Varningar: %ld",
+"Rader: %ld Dubletter: %ld",
+"Felaktig delnyckel. Nyckeldelen är inte en sträng eller den angivna längden är längre än kolumnlängden",
+"Man kan inte radera alla fält med ALTER TABLE. Använd DROP TABLE istället",
+"Kan inte ta bort '%-.64s'. Kontrollera att fältet/nyckel finns",
+"Rader: %ld Dubletter: %ld Varningar: %ld",
+"INSERT table '%-.64s' får inte finnas i FROM tabell-listan",
+"Finns inget thread med id %lu",
+"Du är inte ägare till thread %lu",
+"Inga tabeller angivna",
+"För många alternativ till kolumn %s för SET",
+"Kan inte generera ett unikt filnamn %s.(1-999)\n",
+"Tabell '%-.64s' kan inte uppdateras emedan den är låst för läsning",
+"Tabell '%-.64s' är inte låst med LOCK TABLES",
+"BLOB fält '%-.64s' kan inte ha ett DEFAULT värde"
+"Felaktigt databas namn '%-.64s'",
+"Felaktigt tabell namn '%-.64s'",
+"Den angivna frågan skulle troligen ta mycket long tid! Kontrollar din WHERE och använd SET OPTION SQL_BIG_SELECTS=1 ifall du vill hantera stora joins",
+"Oidentifierat fel",
+"Okänd procedur: %s",
+"Felaktigt antal parametrar till procedur %s",
+"Felaktiga parametrar till procedur %s",
+"Okänd tabell '%-.64s' i '%-.64s'",
+"Fält '%-.64s' är redan använt",
+"Felaktig användning av SQL grupp function",
+"Tabell '%-.64s' har en extension som inte finns i denna version av MySQL",
+"Tabeller måste ha minst 1 kolumn",
+"Tabellen '%-.64s' är full",
+"Okänt karaktärset: '%-.64s'",
+"För många tabeller. MySQL can ha högst %d tabeller i en och samma join"
+"För många fält",
+"För stor total rad längd. Den högst tillåtna rad-längden, förutom BLOBs, är %d. Ändra några av dina fält till BLOB",
+"Tråd-stacken tog slut: Har använt %ld av %ld bytes. Använd 'mysqld -O thread_stack=#' ifall du behöver en större stack",
+"Felaktigt referens i OUTER JOIN. Kontrollera ON uttrycket",
+"Kolumn '%-.32s' är använd med UNIQUE eller INDEX men är inte definerad med NOT NULL",
+"Kan inte ladda funktionen '%-.64s'",
+"Kan inte initialisera funktionen '%-.64s'; '%-.80s'",
+"Man får inte ange sökväg för dynamiska bibliotek",
+"Funktionen '%-.64s' finns redan",
+"Kan inte öppna det dynamiska biblioteket '%-.64s' (Felkod: %d %s)",
+"Hittar inte funktionen '%-.64s' in det dynamiska biblioteket",
+"Funktionen '%-.64s' är inte definierad",
+"Denna dator '%-.64s' är blockerad pga många felaktig paket. Gör 'mysqladmin flush-hosts' för att ta bort alla blockeringarna",
+"Denna dator '%-.64s' har inte privileger att använda denna MySQL server",
+"Du använder MySQL som en anonym användare och som sådan får du inte ändra ditt lösenord",
+"För att ändra lösenord för andra måste du ha rättigheter att uppdatera mysql databasen",
+"Hittade inte användaren i 'user' tabellen",
+"Rader: %ld Uppdaterade: %ld Varningar: %ld",
+"Kan inte skapa en ny tråd (errno %d)"
+"Antalet kolumner motsvarar inte antalet värden på rad: %ld",
+"Kunde inte stänga och öppna tabell: '%-.64s',
+"Felaktig använding av NULL",
+"Fix fel '%-.64s' från REGEXP",
+"Man får ha både GROUP kolumner (MIN(),MAX(),COUNT()...) och fält i en fråga om man inte har en GROUP BY del",
+"Det finns inget privilegium definierat för användare '%-.32s' på '%-.64s'",
+"%-.16s ej tillåtet för '%-.32s@%-.64s' för tabell '%-.64s'",
+"%-.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 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",
+"DELAYED INSERT tråden kunde inte låsa tabell '%-.64s'",
+"Det finns redan 'max_delayed_threads' trådar i använding",
+"Avbröt länken för tråd %ld till db: '%-.64s' användare: '%-.64s' (%s)",
+"Kommunkationspaketet är större än 'max_allowed_packet'",
+"Fick läsfel från klienten vid läsning från 'PIPE'",
+"Fick fatalt fel från 'fcntl()'",
+"Kommunikationspaketen kom i fel ordning",
+"Kunde inte packa up kommunikationspaketet",
+"Fick ett fel vid läsning från klienten",
+"Fick 'timeout' vid läsning från klienten",
+"Fick ett fel vid skrivning till klienten",
+"Fick 'timeout' vid skrivning till klienten",
+"Resultat strängen är längre än max_allowed_packet",
+"Den använda tabell typen kan inte hantera BLOB/TEXT kolumner",
+"Den använda tabell typen kan inte hantera AUTO_INCREMENT kolumner",
+"INSERT DELAYED kan inte användas med tabell '%-.64s', emedan den är låst med LOCK TABLES",
+"Felaktigt column namn '%-.100s'",
+"Den använda tabell typen kan inte indexera kolumn '%-.64s'",
+"Tabellerna i MERGE tabellen är inte identiskt definierade",
+"Kan inte skriva till tabell '%-.64s'; UNIQUE test",
+"Du har inte angett en nyckel längd för BLOB '%-.64s'",
+"Alla delar av en PRIMARY KEY måste vara NOT NULL; Om du vill ha en nyckel med NULL, använd UNIQUE istället",
+"Resultet bestod av mera än en rad",
+"Denna tabell typ kräver en PRIMARY KEY",
+"Denna version av MySQL är inte kompilerad med RAID",
+"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",
+"Du får inte utföra detta kommando i en transaktion",
+"Fick fel %d vid COMMIT",
+"Fick fel %d vid ROLLBACK",
+"Fick fel %d vid FLUSH_LOGS",
+"Fick fel %d vid CHECKPOINT",
+"Avbröt länken för tråd %ld till db: '%-.64s' användare: '%-.32s' Host: '%-.64s' (%.-64s)",
+"Tabellhanteraren klarar inte en binär kopiering av tabellen",
+"Binärloggen stängdes medan vi gjorde FLUSH MASTER",
+"Failed rebuilding the index of dumped table '%-.64s'",
+"Fick en master: '%-.64s'",
+"Fick nätverksfel vid läsning från master",
+"Fick nätverksfel vid skrivning till master",
+"Hittar inte ett FULLTEXT index i kolumnlist",
diff --git a/sql/slave.cc b/sql/slave.cc
new file mode 100644
index 00000000000..fb28922c74f
--- /dev/null
+++ b/sql/slave.cc
@@ -0,0 +1,945 @@
+/* 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 <mysql.h>
+#include "mini_client.h"
+#include <thr_alarm.h>
+#include <my_dir.h>
+
+pthread_handler_decl(handle_slave,arg);
+extern bool volatile abort_loop, abort_slave;
+
+// the master variables are defaults read from my.cnf or command line
+extern uint master_port, master_connect_retry;
+extern my_string master_user, master_password, master_host,
+ master_info_file;
+
+extern I_List<i_string> replicate_do_db, replicate_ignore_db;
+extern I_List<THD> threads;
+bool slave_running = 0;
+pthread_t slave_real_id;
+MASTER_INFO glob_mi;
+
+
+extern bool opt_log_slave_updates ;
+
+static inline bool slave_killed(THD* thd);
+static int init_slave_thread(THD* thd);
+static int init_master_info(MASTER_INFO* mi);
+static void safe_connect(THD* thd, MYSQL* mysql, MASTER_INFO* mi);
+static void safe_reconnect(THD* thd, MYSQL* mysql, MASTER_INFO* mi);
+static int safe_sleep(THD* thd, int sec);
+static int request_table_dump(MYSQL* mysql, char* db, char* table);
+static int create_table_from_dump(THD* thd, NET* net, const char* db,
+ const char* table_name);
+
+static inline bool slave_killed(THD* thd)
+{
+ return abort_slave || abort_loop || thd->killed;
+}
+
+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())
+ return 1; // ok to replicate if the user puts no constraints
+
+ if(!db)
+ return 0; // if the user has specified restrictions on which databases to replicate
+ // and db was not selected, do not replicate
+
+ 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;
+ }
+ 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;
+
+ while((tmp=it++))
+ {
+ if(!strcmp(tmp->ptr, db))
+ return 0; // match
+ }
+
+ return 1;
+
+ }
+
+ // impossible
+ return 0;
+}
+
+static void init_strvar_from_file(char* var, int max_size, FILE* f,
+ char* default_val)
+{
+
+ if(fgets(var, max_size, f))
+ {
+ *(strend(var)-1) = 0;
+ }
+ else if(default_val)
+ strmake(var, default_val, max_size);
+}
+
+static void init_intvar_from_file(int* var, FILE* f,
+ int default_val)
+{
+ char buf[32];
+
+ if(fgets(buf, sizeof(buf), f))
+ {
+ *var = atoi(buf);
+ }
+ else if(default_val)
+ *var = default_val;
+}
+
+
+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
+ TABLE_LIST tables;
+ int error = 0;
+
+ if(packet_len == packet_error)
+ {
+ send_error(&thd->net, ER_MASTER_NET_READ);
+ return 1;
+ }
+ if(net->read_pos[0] == 255) // error from master
+ {
+ net->read_pos[packet_len] = 0;
+ net_printf(&thd->net, ER_MASTER, net->read_pos + 3);
+ return 1;
+ }
+ thd->command = COM_TABLE_DUMP;
+ thd->query = sql_alloc(packet_len + 1);
+ if(!thd->query)
+ {
+ sql_print_error("create_table_from_dump: out of memory");
+ net_printf(&thd->net, ER_GET_ERRNO, "Out of memory");
+ return 1;
+ }
+ memcpy(thd->query, net->read_pos, packet_len);
+ thd->query[packet_len] = 0;
+ thd->current_tablenr = 0;
+ thd->query_error = 0;
+ thd->net.no_send_ok = 1;
+ thd->proc_info = "Creating table from master dump";
+ char* save_db = thd->db;
+ thd->db = thd->last_nx_db; // in case we are creating in a different
+ // database
+ mysql_parse(thd, thd->query, packet_len); // run create table
+ thd->db = save_db; // leave things the way the were before
+
+ if(thd->query_error)
+ {
+ close_thread_tables(thd); // mysql_parse takes care of the error send
+ return 1;
+ }
+
+ bzero((char*) &tables,sizeof(tables));
+ tables.db = (char*)db;
+ tables.name = tables.real_name = (char*)table_name;
+ tables.lock_type = TL_WRITE;
+ thd->proc_info = "Opening master dump table";
+ if(open_tables(thd, &tables) || !tables.table)
+ {
+ // open tables will send the error
+ sql_print_error("create_table_from_dump: could not open created table");
+ close_thread_tables(thd);
+ return 1;
+ }
+
+ handler *file = tables.table->file;
+ thd->proc_info = "Reading master dump table data";
+ if(file->net_read_dump(net))
+ {
+ net_printf(&thd->net, ER_MASTER_NET_READ);
+ sql_print_error("create_table_from_dump::failed in\
+ handler::net_read_dump()");
+ close_thread_tables(thd);
+ return 1;
+ }
+
+ HA_CHECK_OPT check_opt;
+ check_opt.init();
+ check_opt.quick = 1;
+ thd->proc_info = "rebuilding the index on master dump table";
+ Vio* save_vio = thd->net.vio;
+ thd->net.vio = 0; // 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
+ if(file->repair(thd,&check_opt ))
+ {
+ net_printf(&thd->net, ER_INDEX_REBUILD,tables.table->real_name );
+ error = 1;
+ }
+ thd->net.vio = save_vio;
+ close_thread_tables(thd);
+
+ thd->net.no_send_ok = 0;
+ return error;
+}
+
+int fetch_nx_table(THD* thd, MASTER_INFO* mi)
+{
+ MYSQL* mysql = mc_mysql_init(NULL);
+ int error = 1;
+ int nx_errno = 0;
+ if(!mysql)
+ {
+ sql_print_error("fetch_nx_table: Error in mysql_init()");
+ nx_errno = ER_GET_ERRNO;
+ goto err;
+ }
+
+ safe_connect(thd, mysql, mi);
+ if(slave_killed(thd))
+ goto err;
+
+ if(request_table_dump(mysql, thd->last_nx_db, thd->last_nx_table))
+ {
+ nx_errno = ER_GET_ERRNO;
+ sql_print_error("fetch_nx_table: failed on table dump request ");
+ goto err;
+ }
+
+ if(create_table_from_dump(thd, &mysql->net, thd->last_nx_db,
+ thd->last_nx_table))
+ {
+ // create_table_from_dump will have sent the error alread
+ sql_print_error("fetch_nx_table: failed on create table ");
+ goto err;
+ }
+
+ error = 0;
+ err:
+ if(mysql)
+ mc_mysql_close(mysql);
+ if(nx_errno && thd->net.vio)
+ send_error(&thd->net, nx_errno, "Error in fetch_nx_table");
+
+ return error;
+}
+
+static int init_master_info(MASTER_INFO* mi)
+{
+ FILE* file;
+ MY_STAT stat_area;
+ char fname[FN_REFLEN];
+ fn_format(fname, master_info_file, mysql_data_home, "", 4+16+32);
+
+ if(!mi->inited)
+ pthread_mutex_init(&mi->lock, NULL);
+
+ // we need a mutex while we are changing master info parameters to
+ // keep other threads from reading bogus info
+
+ pthread_mutex_lock(&mi->lock);
+
+
+ if(!my_stat(fname, &stat_area, MYF(0))) // we do not want any messages
+ // if the file does not exist
+ {
+ file = my_fopen(fname, O_CREAT|O_RDWR, MYF(MY_WME));
+ if(!file)
+ return 1;
+ mi->log_file_name[0] = 0;
+ mi->pos = 0;
+ mi->file = file;
+
+ if(master_host)
+ strmake(mi->host, master_host, sizeof(mi->host));
+ if(master_user)
+ strmake(mi->user, master_user, sizeof(mi->user));
+ if(master_password)
+ strmake(mi->password, master_password, sizeof(mi->password));
+ mi->port = master_port;
+ mi->connect_retry = master_connect_retry;
+
+ if(flush_master_info(mi))
+ return 1;
+ }
+ else
+ {
+ file = my_fopen(fname, O_RDWR, MYF(MY_WME));
+ if(!file)
+ return 1;
+
+ if(!fgets(mi->log_file_name, sizeof(mi->log_file_name), file))
+ {
+ sql_print_error("Error reading log file name from master info file ");
+ return 1;
+ }
+
+ *(strend(mi->log_file_name) - 1) = 0; // kill \n
+ char buf[FN_REFLEN];
+ if(!fgets(buf, sizeof(buf), file))
+ {
+ sql_print_error("Error reading log file position from master info file");
+ return 1;
+ }
+
+ mi->pos = atoi(buf);
+ mi->file = file;
+ init_strvar_from_file(mi->host, sizeof(mi->host), file, master_host);
+ init_strvar_from_file(mi->user, sizeof(mi->user), file, master_user);
+ init_strvar_from_file(mi->password, sizeof(mi->password), file,
+ master_password);
+
+ init_intvar_from_file((int*)&mi->port, file, master_port);
+ init_intvar_from_file((int*)&mi->connect_retry, file,
+ master_connect_retry);
+
+ }
+
+ mi->inited = 1;
+ pthread_mutex_unlock(&mi->lock);
+
+ return 0;
+}
+
+int show_master_info(THD* thd)
+{
+ DBUG_ENTER("show_master_info");
+ List<Item> field_list;
+ field_list.push_back(new Item_empty_string("Master_Host",
+ sizeof(glob_mi.host)));
+ field_list.push_back(new Item_empty_string("Master_User",
+ sizeof(glob_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",
+ 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("Replicate_do_db", 20));
+ field_list.push_back(new Item_empty_string("Replicate_ignore_db", 20));
+ if(send_fields(thd, field_list, 1))
+ DBUG_RETURN(-1);
+
+ 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, (longlong)glob_mi.pos);
+ 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);
+
+ 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)
+{
+ FILE* file = mi->file;
+ if(my_fseek(file, 0L, MY_SEEK_SET, MYF(MY_WME)) == MY_FILEPOS_ERROR ||
+ fprintf(file, "%s\n%ld\n%s\n%s\n%s\n%d\n%d\n",
+ mi->log_file_name, mi->pos, mi->host, mi->user, mi->password,
+ mi->port, mi->connect_retry) < 0 ||
+ fflush(file))
+ {
+ sql_print_error("Write error flushing master_info: %d", errno);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int init_slave_thread(THD* thd)
+{
+ DBUG_ENTER("init_slave_thread");
+ thd->system_thread = thd->bootstrap = 1;
+ thd->client_capabilities = 0;
+ my_net_init(&thd->net, 0);
+ thd->max_packet_length=thd->net.max_packet;
+ thd->master_access= ~0;
+ thd->priv_user = 0;
+ thd->options = (((opt_log_slave_updates) ? OPTION_BIN_LOG:0)
+ | OPTION_AUTO_COMMIT | OPTION_AUTO_IS_NULL) ;
+ thd->system_thread = 1;
+ thd->client_capabilities = CLIENT_LOCAL_FILES;
+ slave_real_id=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->alloc) ||
+ my_pthread_setspecific_ptr(THR_NET, &thd->net))
+ {
+ 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();
+#ifndef __WIN__
+ sigset_t set;
+ VOID(sigemptyset(&set)); // Get mask in use
+ VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals));
+#endif
+
+ thd->alloc.free=thd->alloc.used=0;
+ if (thd->max_join_size == (ulong) ~0L)
+ thd->options |= OPTION_BIG_SELECTS;
+
+ 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)
+{
+ 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)
+ {
+ int nap_time = (int) (end_time - start_time);
+ thr_alarm(&alarmed, 2 * nap_time,&alarm_buff); // 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
+ sleep(nap_time);
+ if (thr_alarm_in_use(&alarmed)) // if we wake up before the alarm goes off, hit the button
+ thr_end_alarm(&alarmed); // so it will not wake up the wife and kids :-)
+
+ if (slave_killed(thd))
+ return 1;
+ start_time=time((time_t*) 0);
+ }
+ return 0;
+}
+
+
+static int request_dump(MYSQL* mysql, MASTER_INFO* mi)
+{
+ char buf[FN_REFLEN + 6];
+ int len;
+ int binlog_flags = 0; // for now
+ char* logname = mi->log_file_name;
+ int4store(buf, mi->pos);
+ int2store(buf + 4, binlog_flags);
+ len = strlen(logname);
+ memcpy(buf + 6, logname,len);
+ if(mc_simple_command(mysql, COM_BINLOG_DUMP, buf, len + 6, 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;
+ }
+
+ return 0;
+}
+
+static int request_table_dump(MYSQL* mysql, char* db, char* table)
+{
+ char buf[1024];
+ char * p = buf;
+ uint table_len = strlen(table);
+ uint db_len = strlen(db);
+ 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);
+ p += db_len;
+ *p++ = table_len;
+ memcpy(p, table, table_len);
+
+ if(mc_simple_command(mysql, COM_TABLE_DUMP, buf, p - buf + table_len, 1))
+ {
+ sql_print_error("request_table_dump: Error sending the table dump \
+command");
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static uint read_event(MYSQL* mysql, MASTER_INFO *mi)
+{
+ uint len = packet_error;
+ NET* net = &mysql->net;
+ int read_errno = EINTR; // for convinience lets think we start by
+ // being in the interrupted state :-)
+ // my_real_read() will time us out
+ // we check if we were told to die, and if not, try reading again
+ while (!abort_loop && !abort_slave && len == packet_error && read_errno == EINTR )
+ {
+ len = mc_net_safe_read(mysql);
+ read_errno = errno;
+ }
+ if(abort_loop || abort_slave)
+ return packet_error;
+ if (len == packet_error || (int) len < 1)
+ {
+ sql_print_error("Error reading packet from server: %s (%d)",
+ mc_mysql_error(mysql), read_errno);
+ return packet_error;
+ }
+
+ if(len == 1)
+ {
+ sql_print_error("Received 0 length packet from server, looks like master shutdown: %s (%d)",
+ mc_mysql_error(mysql), read_errno);
+ return packet_error;
+ }
+
+ DBUG_PRINT("info",( "len=%u, net->read_pos[4] = %d\n",
+ len, net->read_pos[4]));
+
+ return len - 1;
+}
+
+static int exec_event(THD* thd, NET* net, MASTER_INFO* mi, int event_len)
+{
+ Log_event * ev = Log_event::read_log_event((const char*)net->read_pos + 1 , event_len);
+ if(ev)
+ {
+ switch(ev->get_type_code())
+ {
+ case QUERY_EVENT:
+ {
+ Query_log_event* qev = (Query_log_event*)ev;
+ int q_len = qev->q_len;
+ init_sql_alloc(&thd->alloc, 8192);
+ thd->db = (char*)qev->db;
+ if(db_ok(thd->db, replicate_do_db, replicate_ignore_db))
+ {
+ thd->query = (char*)qev->query;
+ thd->set_time((time_t)qev->when);
+ thd->current_tablenr = 0;
+ VOID(pthread_mutex_lock(&LOCK_thread_count));
+ thd->query_id = query_id++;
+ VOID(pthread_mutex_unlock(&LOCK_thread_count));
+ thd->last_nx_table = thd->last_nx_db = 0;
+ for(;;)
+ {
+ thd->query_error = 0; // clear error
+ thd->last_nx_table = thd->last_nx_db = 0;
+ thd->net.last_errno = 0;
+ thd->net.last_error[0] = 0;
+ mysql_parse(thd, thd->query, q_len); // try query
+ if(!thd->query_error || slave_killed(thd)) // break if ok
+ break;
+ // if not ok
+ if(thd->last_nx_table && thd->last_nx_db)
+ {
+ // for now, let's just fail if the table is not
+ // there, and not try to be a smart alec...
+
+ // if table was not there
+ //if(fetch_nx_table(thd,&glob_mi))
+ // try to to fetch from master
+ break; // if we can't, just break
+ }
+ else
+ break; // if failed for some other reason, bail out
+
+ // if fetched the table from master successfully
+ // we need to restore query info in thd because
+ // fetch_nx_table executes create table
+ thd->query = (char*)qev->query;
+ thd->set_time((time_t)qev->when);
+ thd->current_tablenr = 0;
+ }
+ }
+ thd->db = 0;// prevent db from being freed
+ thd->query = 0; // just to be sure
+ close_thread_tables(thd);
+ free_root(&thd->alloc);
+ if(thd->query_error)
+ {
+ sql_print_error("Slave: error running query '%s' ",
+ qev->query);
+ return 1;
+ }
+ delete ev;
+
+ if(thd->fatal_error)
+ {
+ sql_print_error("Slave: Fatal error running query '%s' ",
+ thd->query);
+ return 1;
+ }
+
+ mi->inc_pos(event_len);
+ flush_master_info(mi);
+ break;
+ }
+
+ case LOAD_EVENT:
+ {
+ Load_log_event* lev = (Load_log_event*)ev;
+ init_sql_alloc(&thd->alloc, 8192);
+ thd->db = (char*)lev->db;
+ 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));
+
+ 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;
+
+ TABLE_LIST tables;
+ bzero((char*) &tables,sizeof(tables));
+ tables.db = thd->db;
+ tables.name = tables.real_name = (char*)lev->table_name;
+ tables.lock_type = TL_WRITE;
+
+ if (open_tables(thd, &tables))
+ {
+ sql_print_error("Slave: error opening table %s ",
+ tables.name);
+ delete ev;
+ return 1;
+ }
+
+ List<Item> fields;
+ lev->set_fields(fields);
+ 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 get confused
+ // about the packet sequence
+ if(mysql_load(thd, &ex, &tables, fields, handle_dup, 1,
+ TL_WRITE))
+ thd->query_error = 1;
+ 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 :-)
+ {
+ (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
+ }
+
+ 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;
+ return 1;
+ }
+ delete ev;
+
+ if(thd->fatal_error)
+ {
+ sql_print_error("Slave: Fatal error running query '%s' ",
+ thd->query);
+ return 1;
+ }
+
+ mi->inc_pos(event_len);
+ flush_master_info(mi);
+ break;
+ }
+
+ case START_EVENT:
+ mi->inc_pos(event_len);
+ flush_master_info(mi);
+ break;
+
+ case STOP_EVENT:
+ mi->inc_pos(event_len);
+ flush_master_info(mi);
+ break;
+ case ROTATE_EVENT:
+ {
+ Rotate_log_event* rev = (Rotate_log_event*)ev;
+ int ident_len = rev->ident_len;
+ memcpy(mi->log_file_name, rev->new_log_ident,ident_len );
+ mi->log_file_name[ident_len] = 0;
+ mi->pos = 0;
+ 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_pos(event_len);
+ flush_master_info(mi);
+ break;
+ }
+ }
+
+ }
+ else
+ {
+ sql_print_error("Could not parse log event entry, check the master for binlog corruption\
+ 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)))
+{
+ THD *thd;; // needs to be first for thread_stack
+ MYSQL *mysql = NULL ;
+
+ pthread_mutex_lock(&LOCK_slave);
+ if(slave_running)
+ {
+ pthread_mutex_unlock(&LOCK_slave);
+ return 0; // safety just in case
+ }
+ slave_running = 1;
+ abort_slave = 0;
+ pthread_mutex_unlock(&LOCK_slave);
+
+ int error = 1;
+
+ my_thread_init(); // needs to be up here, otherwise we get a coredump
+ // trying to use DBUG_ stuff
+ thd = new THD; // note that contructor of THD uses DBUG_ !
+ DBUG_ENTER("handle_slave");
+
+ pthread_detach_this_thread();
+ if(init_slave_thread(thd) || init_master_info(&glob_mi))
+ goto err;
+ thd->thread_stack = (char*)&thd; // remember where our stack is
+
+ threads.append(thd);
+
+ DBUG_PRINT("info",("master info: log_file_name=%s, position=%d",
+ glob_mi.log_file_name, glob_mi.pos));
+
+ mysql = mc_mysql_init(NULL);
+ if(!mysql)
+ {
+ sql_print_error("Slave thread: error in mc_mysql_init()");
+ goto err;
+ }
+
+ thd->proc_info = "connecting to master";
+ safe_connect(thd, mysql, &glob_mi);
+
+ while(!slave_killed(thd))
+ {
+ thd->proc_info = "requesting binlog dump";
+ if(request_dump(mysql, &glob_mi))
+ {
+ sql_print_error("Failed on request_dump()");
+ if(slave_killed(thd))
+ goto err;
+
+ thd->proc_info = "waiting to reconnect after a failed dump request";
+ safe_sleep(thd, glob_mi.connect_retry);
+ if(slave_killed(thd))
+ goto err;
+
+ thd->proc_info = "reconnecting after a failed dump request";
+
+ safe_reconnect(thd, mysql, &glob_mi);
+ if(slave_killed(thd))
+ goto err;
+
+ continue;
+ }
+
+
+ while(!slave_killed(thd))
+ {
+ bool reset = 0;
+ thd->proc_info = "reading master update";
+ uint event_len = read_event(mysql, &glob_mi);
+ if(slave_killed(thd))
+ goto err;
+
+ if (event_len == packet_error)
+ {
+ thd->proc_info = "waiting to reconnect after a failed read";
+ safe_sleep(thd, glob_mi.connect_retry);
+ if(slave_killed(thd))
+ goto err;
+ thd->proc_info = "reconnecting after a failed read";
+ safe_reconnect(thd, mysql, &glob_mi);
+ if(slave_killed(thd))
+ goto err;
+ reset = 1;
+ }
+
+ if(reset)
+ break;
+
+ 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");
+ 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
+ }
+
+ }
+ }
+
+ error = 0;
+ err:
+ thd->query = thd->db = 0; // extra safety
+ if(mysql)
+ mc_mysql_close(mysql);
+ thd->proc_info = "waiting for slave mutex on exit";
+ pthread_mutex_lock(&LOCK_slave);
+ slave_running = 0;
+ abort_slave = 0;
+ pthread_cond_broadcast(&COND_slave_stopped); // tell the world we are done
+ pthread_mutex_unlock(&LOCK_slave);
+ delete thd;
+ my_thread_end();
+ pthread_exit(0);
+ DBUG_RETURN(0); // Can't return anything here
+}
+
+static void safe_connect(THD* thd, MYSQL* mysql, MASTER_INFO* mi)
+ // will try to connect until successful
+{
+ while(!slave_killed(thd) &&
+ !mc_mysql_connect(mysql, mi->host, mi->user, mi->password, 0,
+ mi->port, 0, 0))
+ {
+ sql_print_error(
+ "Slave thread: error connecting to slave:%s, retry in %d sec",
+ mc_mysql_error(mysql), mi->connect_retry);
+ safe_sleep(thd, mi->connect_retry);
+ }
+
+}
+
+// will try to connect until successful
+
+static void safe_reconnect(THD* thd, MYSQL* mysql, MASTER_INFO* mi)
+{
+ while(!slave_killed(thd) && mc_mysql_reconnect(mysql))
+ {
+ sql_print_error(
+ "Slave thread: error connecting to slave:%s, retry in %d sec",
+ mc_mysql_error(mysql), mi->connect_retry);
+ safe_sleep(thd, mi->connect_retry);
+ }
+
+}
+
+#ifdef __GNUC__
+template class I_List_iterator<i_string>;
+#endif
+
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
new file mode 100644
index 00000000000..d2f8680aee3
--- /dev/null
+++ b/sql/sql_acl.cc
@@ -0,0 +1,2547 @@
+/* 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 */
+
+
+/*
+ The privileges are saved in the following tables:
+ mysql/user ; super user who are allowed to do almoust anything
+ mysql/host ; host priviliges. This is used if host is empty in mysql/db.
+ mysql/db ; database privileges / user
+
+ data in tables is sorted according to how many not-wild-cards there is
+ in the relevant fields. Empty strings comes last.
+*/
+
+#include "mysql_priv.h"
+#include "sql_acl.h"
+#include "hash_filo.h"
+#include <m_ctype.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;
+};
+
+class ACL_HOST :public ACL_ACCESS
+{
+public:
+ acl_host_and_ip host;
+ char *db;
+};
+
+class ACL_USER :public ACL_ACCESS
+{
+public:
+ acl_host_and_ip host;
+ uint hostname_length;
+ char *user,*password;
+ ulong salt[2];
+};
+
+class ACL_DB :public ACL_ACCESS
+{
+public:
+ acl_host_and_ip host;
+ char *user,*db;
+};
+
+class acl_entry :public hash_filo_element
+{
+public:
+ uint 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)))
+{
+ *length=(uint) entry->length;
+ return (byte*) entry->key;
+}
+
+#define ACL_KEY_LENGTH (sizeof(long)+NAME_LEN+17)
+
+static DYNAMIC_ARRAY acl_hosts,acl_users,acl_dbs;
+static MEM_ROOT mem, memex;
+static bool initialized=0;
+static bool allow_all_hosts=1;
+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 int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b);
+static ulong get_sort(uint count,...);
+static void init_check_host(void);
+static ACL_USER *find_acl_user(const char *host, const char *user);
+static bool update_user_table(THD *thd, const char *host, const char *user,
+ const char *new_password);
+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)
+{
+ THD *thd;
+ TABLE_LIST tables[3];
+ TABLE *table;
+ READ_RECORD read_record_info;
+ 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);
+ if (dont_read_acl_tables)
+ DBUG_RETURN(0); /* purecov: tested */
+
+ if (!(thd=new THD))
+ DBUG_RETURN(1); /* purecov: inspected */
+ 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));
+ bzero((char*) &tables,sizeof(tables));
+ tables[0].name=tables[0].real_name=(char*) "host";
+ tables[1].name=tables[1].real_name=(char*) "user";
+ tables[2].name=tables[2].real_name=(char*) "db";
+ tables[0].next=tables+1;
+ tables[1].next=tables+2;
+ tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_READ;
+ tables[0].db=tables[1].db=tables[2].db=thd->db;
+
+ if (open_tables(thd,tables))
+ {
+ close_thread_tables(thd); /* purecov: inspected */
+ delete thd; /* purecov: inspected */
+ DBUG_RETURN(1); /* purecov: inspected */
+ }
+ 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)
+ {
+ close_thread_tables(thd); /* purecov: inspected */
+ delete thd; /* purecov: inspected */
+ DBUG_RETURN(1); /* purecov: inspected */
+ }
+
+ init_sql_alloc(&mem,1024);
+ init_read_record(&read_record_info,thd,table= tables[0].table,NULL,1,0);
+ VOID(init_dynamic_array(&acl_hosts,sizeof(ACL_HOST),20,50));
+ while (!(read_record_info.read_record(&read_record_info)))
+ {
+ 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);
+#ifndef TO_BE_REMOVED
+ if (table->fields == 8)
+ { // Without grant
+ if (host.access & CREATE_ACL)
+ host.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
+ }
+#endif
+ VOID(push_dynamic(&acl_hosts,(gptr) &host));
+ }
+ qsort((gptr) dynamic_element(&acl_hosts,0,ACL_HOST*),acl_hosts.elements,
+ sizeof(ACL_HOST),(qsort_cmp) acl_compare);
+ end_read_record(&read_record_info);
+ freeze_size(&acl_hosts);
+
+ init_read_record(&read_record_info,thd,table=tables[1].table,NULL,1,0);
+ VOID(init_dynamic_array(&acl_users,sizeof(ACL_USER),50,100));
+ if (table->field[2]->field_length == 8 &&
+ protocol_version == PROTOCOL_VERSION)
+ {
+ sql_print_error(
+ "Old 'user' table. (Check README or the Reference manual). Continuing --old-protocol"); /* purecov: tested */
+ protocol_version=9; /* purecov: tested */
+ }
+
+ allow_all_hosts=0;
+ while (!(read_record_info.read_record(&read_record_info)))
+ {
+ ACL_USER user;
+ uint length=0;
+ update_hostname(&user.host,get_field(&mem, table,0));
+ user.user=get_field(&mem, table,1);
+ user.password=get_field(&mem, table,2);
+ if (user.password && (length=strlen(user.password)) == 8 &&
+ protocol_version == PROTOCOL_VERSION)
+ {
+ sql_print_error(
+ "Found old style password for user '%s'. Ignoring user. (You may want to restart using --old-protocol)",
+ user.user ? user.user : ""); /* purecov: tested */
+ }
+ else if (length % 8) // This holds true for passwords
+ {
+ sql_print_error(
+ "Found invalid password for user: '%s@%s'; Ignoring user",
+ user.user ? user.user : "",
+ user.host.hostname ? user.host.hostname : ""); /* purecov: tested */
+ continue; /* purecov: tested */
+ }
+ get_salt_from_password(user.salt,user.password);
+ user.access=get_access(table,3);
+ user.sort=get_sort(2,user.host.hostname,user.user);
+ user.hostname_length=user.host.hostname ? 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;
+ }
+#endif
+ VOID(push_dynamic(&acl_users,(gptr) &user));
+ if (!user.host.hostname || user.host.hostname[0] == wild_many &&
+ !user.host.hostname[1])
+ allow_all_hosts=1; // Anyone can connect
+ }
+ qsort((gptr) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements,
+ sizeof(ACL_USER),(qsort_cmp) acl_compare);
+ end_read_record(&read_record_info);
+ freeze_size(&acl_users);
+
+ init_read_record(&read_record_info,thd,table=tables[2].table,NULL,1,0);
+ VOID(init_dynamic_array(&acl_dbs,sizeof(ACL_DB),50,100));
+ while (!(read_record_info.read_record(&read_record_info)))
+ {
+ ACL_DB db;
+ update_hostname(&db.host,get_field(&mem, table,0));
+ db.db=get_field(&mem, table,1);
+ db.user=get_field(&mem, table,2);
+ db.access=get_access(table,3);
+ db.access=fix_rights_for_db(db.access);
+ db.sort=get_sort(3,db.host.hostname,db.db,db.user);
+#ifndef TO_BE_REMOVED
+ if (table->fields <= 9)
+ { // Without grant
+ if (db.access & CREATE_ACL)
+ db.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
+ }
+#endif
+ VOID(push_dynamic(&acl_dbs,(gptr) &db));
+ }
+ qsort((gptr) dynamic_element(&acl_dbs,0,ACL_DB*),acl_dbs.elements,
+ sizeof(ACL_DB),(qsort_cmp) acl_compare);
+ end_read_record(&read_record_info);
+ freeze_size(&acl_dbs);
+ init_check_host();
+
+ mysql_unlock_tables(thd, lock);
+ thd->version--; // Force close to free memory
+ close_thread_tables(thd);
+ delete thd;
+ initialized=1;
+ DBUG_RETURN(0);
+}
+
+
+void acl_free(bool end)
+{
+ free_root(&mem);
+ delete_dynamic(&acl_hosts);
+ delete_dynamic(&acl_users);
+ delete_dynamic(&acl_dbs);
+ delete_dynamic(&acl_wild_hosts);
+ hash_free(&acl_check_hosts);
+ if (!end)
+ acl_cache->clear(1); /* purecov: inspected */
+ else
+ {
+ delete acl_cache;
+ acl_cache=0;
+ }
+}
+
+ /* Reload acl list if possible */
+
+void acl_reload(void)
+{
+ 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)
+ { // Can't have locked tables here
+ current_thd->lock=current_thd->locked_tables;
+ current_thd->locked_tables=0;
+ close_thread_tables(current_thd);
+ }
+ if ((old_initialized=initialized))
+ VOID(pthread_mutex_lock(&acl_cache->lock));
+
+ old_acl_hosts=acl_hosts;
+ old_acl_users=acl_users;
+ old_acl_dbs=acl_dbs;
+ old_mem=mem;
+ delete_dynamic(&acl_wild_hosts);
+ hash_free(&acl_check_hosts);
+
+ if (acl_init(0))
+ { // Error. Revert to old list
+ acl_free(); /* purecov: inspected */
+ acl_hosts=old_acl_hosts;
+ acl_users=old_acl_users;
+ acl_dbs=old_acl_dbs;
+ mem=old_mem;
+ init_check_host();
+ }
+ else
+ {
+ free_root(&old_mem);
+ delete_dynamic(&old_acl_hosts);
+ delete_dynamic(&old_acl_users);
+ delete_dynamic(&old_acl_dbs);
+ }
+ if (old_initialized)
+ VOID(pthread_mutex_unlock(&acl_cache->lock));
+ DBUG_VOID_RETURN;
+}
+
+
+/* Get all access bits from table after fieldnr */
+
+static uint get_access(TABLE *form,uint fieldnr)
+{
+ uint 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)
+ {
+ (*pos)->val_str(&res,&res);
+ if (toupper(res[0]) == 'Y')
+ access_bits|=bit;
+ }
+ return access_bits;
+}
+
+
+/*
+ return a number with if sorted put string in this order:
+ no wildcards
+ wildcards
+ empty string
+ */
+
+static ulong get_sort(uint count,...)
+{
+ va_list args;
+ va_start(args,count);
+ ulong sort=0;
+
+ while (count--)
+ {
+ char *str=va_arg(args,char*);
+ uint chars=0,wild=0;
+
+ if (str)
+ {
+ for (; *str ; str++)
+ {
+ if (*str == wild_many || *str == wild_one || *str == wild_prefix)
+ wild++;
+ else
+ chars++;
+ }
+ }
+ sort= (sort << 8) + (wild ? 1 : chars ? 2 : 0);
+ }
+ va_end(args);
+ return sort;
+}
+
+
+static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b)
+{
+ if (a->sort > b->sort)
+ return -1;
+ if (a->sort < b->sort)
+ return 1;
+ return 0;
+}
+
+
+/* Get master privilges for user (priviliges for all tables) */
+
+
+uint acl_getroot(const char *host, const char *ip, const char *user,
+ const char *password,const char *message,char **priv_user,
+ bool old_ver)
+{
+ uint user_access=NO_ACCESS;
+ *priv_user=(char*) user;
+
+ if (!initialized)
+ return (uint) ~NO_ACCESS; // If no data allow anything /* purecov: tested */
+ VOID(pthread_mutex_lock(&acl_cache->lock));
+
+ /*
+ Get possible access from user_list. This is or'ed to others not
+ fully specified
+ */
+ for (uint i=0 ; i < acl_users.elements ; i++)
+ {
+ ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
+ if (!acl_user->user || !strcmp(user,acl_user->user))
+ {
+ if (compare_hostname(&acl_user->host,host,ip))
+ {
+ if (!acl_user->password && !*password ||
+ (acl_user->password && *password &&
+ !check_scramble(password,message,acl_user->salt,
+ (my_bool) old_ver)))
+ {
+ user_access=acl_user->access;
+ if (!acl_user->user)
+ *priv_user=(char*) ""; // Change to anonymous user /* purecov: inspected */
+ break;
+ }
+#ifndef ALLOW_DOWNGRADE_OF_USERS
+ break; // Wrong password breaks loop /* purecov: inspected */
+#endif
+ }
+ }
+ }
+ VOID(pthread_mutex_unlock(&acl_cache->lock));
+ return user_access;
+}
+
+
+/*
+** Functions to add and change user and database privileges when one
+** changes things with GRANT
+*/
+
+static byte* check_get_key(ACL_USER *buff,uint *length,
+ my_bool not_used __attribute__((unused)))
+{
+ *length=buff->hostname_length;
+ return (byte*) buff->host.hostname;
+}
+
+static void acl_update_user(const char *user, const char *host,
+ const char *password, uint privileges)
+{
+ for (uint i=0 ; i < acl_users.elements ; i++)
+ {
+ ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
+ if (!acl_user->user && !user[0] ||
+ acl_user->user &&
+ !strcmp(user,acl_user->user))
+ {
+ if (!acl_user->host.hostname && !host[0] ||
+ acl_user->host.hostname && !strcmp(host,acl_user->host.hostname))
+ {
+ acl_user->access=privileges;
+ if (password)
+ {
+ if (!password[0])
+ acl_user->password=0;
+ else
+ {
+ acl_user->password=(char*) ""; // Just point at something
+ get_salt_from_password(acl_user->salt,password);
+ }
+ }
+ break;
+ }
+ }
+ }
+}
+
+
+static void acl_insert_user(const char *user, const char *host,
+ const char *password,
+ uint 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.sort=get_sort(2,acl_user.host.hostname,acl_user.user);
+ acl_user.hostname_length=strlen(acl_user.host.hostname);
+ if (password)
+ {
+ acl_user.password=(char*) ""; // Just point at something
+ get_salt_from_password(acl_user.salt,password);
+ }
+
+ VOID(push_dynamic(&acl_users,(gptr) &acl_user));
+ if (!acl_user.host.hostname || acl_user.host.hostname[0] == wild_many
+ && !acl_user.host.hostname[1])
+ allow_all_hosts=1; // Anyone can connect /* purecov: tested */
+ qsort((gptr) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements,
+ sizeof(ACL_USER),(qsort_cmp) acl_compare);
+
+ /* We must free acl_check_hosts as its memory is mapped to acl_user */
+ delete_dynamic(&acl_wild_hosts);
+ hash_free(&acl_check_hosts);
+ init_check_host();
+}
+
+
+static void acl_update_db(const char *user, const char *host, const char *db,
+ uint privileges)
+{
+ for (uint i=0 ; i < acl_dbs.elements ; i++)
+ {
+ ACL_DB *acl_db=dynamic_element(&acl_dbs,i,ACL_DB*);
+ if (!acl_db->user && !user[0] ||
+ acl_db->user &&
+ !strcmp(user,acl_db->user))
+ {
+ if (!acl_db->host.hostname && !host[0] ||
+ acl_db->host.hostname && !strcmp(host,acl_db->host.hostname))
+ {
+ if (!acl_db->db && !db[0] ||
+ acl_db->db && !strcmp(db,acl_db->db))
+ {
+ if (privileges)
+ acl_db->access=privileges;
+ else
+ delete_dynamic_element(&acl_dbs,i);
+ }
+ }
+ }
+ }
+}
+
+
+static void acl_insert_db(const char *user, const char *host, const char *db,
+ uint privileges)
+{
+ ACL_DB acl_db;
+ /* The acl_cache mutex is locked by mysql_grant */
+ acl_db.user=strdup_root(&mem,user);
+ update_hostname(&acl_db.host,strdup_root(&mem,host));
+ acl_db.db=strdup_root(&mem,db);
+ acl_db.access=privileges;
+ acl_db.sort=get_sort(3,acl_db.host.hostname,acl_db.db,acl_db.user);
+ VOID(push_dynamic(&acl_dbs,(gptr) &acl_db));
+ qsort((gptr) dynamic_element(&acl_dbs,0,ACL_DB*),acl_dbs.elements,
+ sizeof(ACL_DB),(qsort_cmp) acl_compare);
+}
+
+
+/*****************************************************************************
+** Get privilege for a host, user and db combination
+*****************************************************************************/
+
+uint 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;
+ db_access=0; host_access= ~0;
+ char key[ACL_KEY_LENGTH],*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);
+ key_length=(uint) (end-key);
+ if ((entry=(acl_entry*) acl_cache->search(key,key_length)))
+ {
+ db_access=entry->access;
+ VOID(pthread_mutex_unlock(&acl_cache->lock));
+ return db_access;
+ }
+
+ /*
+ Check if there are some access rights for database and user
+ */
+ for (i=0 ; i < acl_dbs.elements ; i++)
+ {
+ ACL_DB *acl_db=dynamic_element(&acl_dbs,i,ACL_DB*);
+ if (!acl_db->user || !strcmp(user,acl_db->user))
+ {
+ if (compare_hostname(&acl_db->host,host,ip))
+ {
+ if (!acl_db->db || !wild_compare(db,acl_db->db))
+ {
+ db_access=acl_db->access;
+ if (acl_db->host.hostname)
+ goto exit; // Fully specified. Take it
+ break; /* purecov: tested */
+ }
+ }
+ }
+ }
+ if (!db_access)
+ goto exit; // Can't be better
+
+ /*
+ No host specified for user. Get hostdata from host table
+ */
+ host_access=0; // Host must be found
+ for (i=0 ; i < acl_hosts.elements ; i++)
+ {
+ ACL_HOST *acl_host=dynamic_element(&acl_hosts,i,ACL_HOST*);
+ if (compare_hostname(&acl_host->host,host,ip))
+ {
+ if (!acl_host->db || !wild_compare(db,acl_host->db))
+ {
+ host_access=acl_host->access; // Fully specified. Take it
+ break;
+ }
+ }
+ }
+exit:
+ /* Save entry in cache for quick retrieval */
+ if ((entry= (acl_entry*) malloc(sizeof(acl_entry)+key_length)))
+ {
+ entry->access=(db_access & host_access);
+ entry->length=key_length;
+ memcpy((gptr) entry->key,key,key_length);
+ acl_cache->add(entry);
+ }
+ VOID(pthread_mutex_unlock(&acl_cache->lock));
+ return (db_access & host_access);
+}
+
+
+int wild_case_compare(const char *str,const char *wildstr)
+{
+ reg3 int flag;
+ DBUG_ENTER("wild_case_compare");
+
+ while (*wildstr)
+ {
+ while (*wildstr && *wildstr != wild_many && *wildstr != wild_one)
+ {
+ if (*wildstr == wild_prefix && wildstr[1])
+ wildstr++;
+ if (toupper(*wildstr++) != toupper(*str++)) DBUG_RETURN(1);
+ }
+ if (! *wildstr ) DBUG_RETURN (*str != 0);
+ if (*wildstr++ == wild_one)
+ {
+ if (! *str++) DBUG_RETURN (1); /* One char; skipp */
+ }
+ else
+ { /* Found '*' */
+ if (!*wildstr) DBUG_RETURN(0); /* '*' as last char: OK */
+ flag=(*wildstr != wild_many && *wildstr != wild_one);
+ do
+ {
+ if (flag)
+ {
+ char cmp;
+ if ((cmp= *wildstr) == wild_prefix && wildstr[1])
+ cmp=wildstr[1];
+ cmp=toupper(cmp);
+ while (*str && toupper(*str) != cmp)
+ str++;
+ if (!*str) DBUG_RETURN (1);
+ }
+ if (wild_case_compare(str,wildstr) == 0) DBUG_RETURN (0);
+ } while (*str++);
+ DBUG_RETURN(1);
+ }
+ }
+ DBUG_RETURN (*str != '\0');
+}
+
+/*****************************************************************************
+** check if there are any possible matching entries for this host
+** All host names without wild cards are stored in a hash table,
+** entries with wildcards are stored in a dynamic array
+*****************************************************************************/
+
+static void init_check_host(void)
+{
+ DBUG_ENTER("init_check_host");
+ VOID(init_dynamic_array(&acl_wild_hosts,sizeof(struct acl_host_and_ip),
+ acl_users.elements,1));
+ VOID(hash_init(&acl_check_hosts,acl_users.elements,0,0,
+ (hash_get_key) check_get_key,0,HASH_CASE_INSENSITIVE));
+ if (!allow_all_hosts)
+ {
+ for (uint i=0 ; i < acl_users.elements ; i++)
+ {
+ ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
+ if (strchr(acl_user->host.hostname,wild_many) ||
+ strchr(acl_user->host.hostname,wild_one) ||
+ acl_user->host.ip_mask)
+ { // Has wildcard
+ uint j;
+ for (j=0 ; j < acl_wild_hosts.elements ; j++)
+ { // Check if host already exists
+ acl_host_and_ip *acl=dynamic_element(&acl_wild_hosts,j,
+ acl_host_and_ip *);
+ if (!my_strcasecmp(acl_user->host.hostname,acl->hostname))
+ break; // already stored
+ }
+ if (j == acl_wild_hosts.elements) // If new
+ (void) push_dynamic(&acl_wild_hosts,(char*) &acl_user->host);
+ }
+ else if (!hash_search(&acl_check_hosts,(byte*) &acl_user->host,
+ strlen(acl_user->host.hostname)))
+ {
+ if (hash_insert(&acl_check_hosts,(byte*) acl_user))
+ { // End of memory
+ allow_all_hosts=1; // Should never happen
+ DBUG_VOID_RETURN;
+ }
+ }
+ }
+ }
+ freeze_size(&acl_wild_hosts);
+ freeze_size(&acl_check_hosts.array);
+ DBUG_VOID_RETURN;
+}
+
+
+/* Return true if there is no users that can match the given host */
+
+bool acl_check_host(const char *host, const char *ip)
+{
+ if (allow_all_hosts)
+ return 0;
+ VOID(pthread_mutex_lock(&acl_cache->lock));
+
+ if (host && hash_search(&acl_check_hosts,(byte*) host,strlen(host)) ||
+ ip && hash_search(&acl_check_hosts,(byte*) ip,strlen(ip)))
+ {
+ VOID(pthread_mutex_unlock(&acl_cache->lock));
+ return 0; // Found host
+ }
+ for (uint i=0 ; i < acl_wild_hosts.elements ; i++)
+ {
+ acl_host_and_ip *acl=dynamic_element(&acl_wild_hosts,i,acl_host_and_ip*);
+ if (compare_hostname(acl, host, ip))
+ {
+ VOID(pthread_mutex_unlock(&acl_cache->lock));
+ return 0; // Host ok
+ }
+ }
+ VOID(pthread_mutex_unlock(&acl_cache->lock));
+ return 1; // Host is not allowed
+}
+
+/*****************************************************************************
+** 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)
+{
+ uint length=0;
+ if (!user[0])
+ {
+ send_error(&thd->net, ER_PASSWORD_ANONYMOUS_USER);
+ return 1;
+ }
+ if (!initialized)
+ {
+ send_error(&thd->net, ER_PASSWORD_NOT_ALLOWED); /* 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=strlen(new_password);
+ new_password[length & 16]=0;
+
+ if (strcmp(thd->user,user) ||
+ my_strcasecmp(host,thd->host ? thd->host : thd->ip))
+ {
+ if (check_access(thd, UPDATE_ACL, "mysql",0,1))
+ return 1;
+ }
+ VOID(pthread_mutex_lock(&acl_cache->lock));
+ ACL_USER *acl_user;
+ if (!(acl_user= find_acl_user(host,user)) || !acl_user->user)
+ {
+ send_error(&thd->net, ER_PASSWORD_NO_MATCH);
+ VOID(pthread_mutex_unlock(&acl_cache->lock));
+ return 1;
+ }
+ if (update_user_table(thd,
+ acl_user->host.hostname ? acl_user->host.hostname : "",
+ acl_user->user, new_password))
+ {
+ VOID(pthread_mutex_unlock(&acl_cache->lock)); /* purecov: deadcode */
+ send_error(&thd->net,0); /* purecov: deadcode */
+ return 1; /* purecov: deadcode */
+ }
+ get_salt_from_password(acl_user->salt,new_password);
+ if (!new_password[0])
+ acl_user->password=0;
+ else
+ acl_user->password=(char*) ""; // Point at something
+ acl_cache->clear(1); // Clear locked hostname cache
+ VOID(pthread_mutex_unlock(&acl_cache->lock));
+
+ char buff[460];
+
+ Query_log_event qinfo(thd, buff);
+ qinfo.q_len =
+ my_sprintf(buff,
+ (buff,"SET PASSWORD FOR \"%-.120s\"@\"%-.120s\"=\"%-.120s\"",
+ acl_user->user,
+ acl_user->host.hostname ? acl_user->host.hostname : "",
+ new_password));
+ mysql_update_log.write(buff,strlen(buff));
+ mysql_bin_log.write(&qinfo);
+ return 0;
+}
+
+
+/*
+ Find first entry that matches the current user
+*/
+
+static ACL_USER *
+find_acl_user(const char *host, const char *user)
+{
+ for (uint i=0 ; i < acl_users.elements ; i++)
+ {
+ ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*);
+ 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;
+ }
+ }
+ return 0;
+}
+
+/*****************************************************************************
+ Handle comparing of hostname
+ A hostname may be of type:
+ hostname (May include wildcards); monty.pp.sci.fi
+ ip (May include wildcards); 192.168.0.0
+ ip/netmask 192.168.0.0/255.255.255.0
+
+ A net mask of 0.0.0.0 is not allowed.
+*****************************************************************************/
+
+static const char *calc_ip(const char *ip, long *val, char end)
+{
+ long ip_val,tmp;
+ if (!(ip=str2int(ip,10,0,255,&ip_val)) || *ip != '.')
+ return 0;
+ ip_val<<=24;
+ if (!(ip=str2int(ip+1,10,0,255,&tmp)) || *ip != '.')
+ return 0;
+ ip_val+=tmp<<16;
+ if (!(ip=str2int(ip+1,10,0,255,&tmp)) || *ip != '.')
+ return 0;
+ ip_val+=tmp<<8;
+ if (!(ip=str2int(ip+1,10,0,255,&tmp)) || *ip != end)
+ return 0;
+ *val=ip_val+tmp;
+ return ip;
+}
+
+
+static void update_hostname(acl_host_and_ip *host, const char *hostname)
+{
+ host->hostname=(char*) hostname; // This will not be modified!
+ if (hostname &&
+ (!(hostname=calc_ip(hostname,&host->ip,'/')) ||
+ !(hostname=calc_ip(hostname+1,&host->ip_mask,'\0'))))
+ {
+ host->ip=host->ip_mask=0; // Not a masked ip
+ }
+}
+
+
+static bool compare_hostname(const acl_host_and_ip *host, const char *hostname,
+ const char *ip)
+{
+ long tmp;
+ if (host->ip_mask && ip && calc_ip(ip,&tmp,'\0'))
+ {
+ return (tmp & host->ip_mask) == host->ip;
+ }
+ return (!host->hostname ||
+ (hostname && !wild_case_compare(hostname,host->hostname)) ||
+ (ip && !wild_compare(ip,host->hostname)));
+}
+
+
+/****************************************************************************
+** Code to update grants in the user and database privilege tables
+****************************************************************************/
+
+static bool update_user_table(THD *thd, const char *host, const char *user,
+ const char *new_password)
+{
+ TABLE_LIST tables;
+ TABLE *table;
+ bool error=1;
+ DBUG_ENTER("update_user_table");
+ DBUG_PRINT("enter",("user: %s host: %s",user,host));
+
+ bzero((char*) &tables,sizeof(tables));
+ tables.name=tables.real_name=(char*) "user";
+ tables.db=(char*) "mysql";
+ if (!(table=open_ltable(thd,&tables,TL_WRITE)))
+ DBUG_RETURN(1); /* purecov: deadcode */
+ table->field[0]->store(host,strlen(host));
+ table->field[1]->store(user,strlen(user));
+
+ if (table->file->index_read_idx(table->record[0],0,
+ (byte*) table->field[0]->ptr,0,
+ HA_READ_KEY_EXACT))
+ {
+ my_error(ER_PASSWORD_NO_MATCH,MYF(0)); /* purecov: deadcode */
+ DBUG_RETURN(1); /* purecov: deadcode */
+ }
+ store_record(table,1);
+ table->field[2]->store(new_password,strlen(new_password));
+ if ((error=table->file->update_row(table->record[1],table->record[0])))
+ {
+ table->file->print_error(error,MYF(0)); /* purecov: deadcode */
+ goto end; /* purecov: deadcode */
+ }
+ error=0; // Record updated
+
+end:
+ close_thread_tables(thd);
+ DBUG_RETURN(error);
+}
+
+/****************************************************************************
+** Handle GRANT commands
+****************************************************************************/
+
+static int replace_user_table(TABLE *table, const LEX_USER &combo,
+ uint rights, char what)
+{
+ int error = -1;
+ uint i,j;
+ bool ima=0;
+ char *password,empty_string[1];
+ DBUG_ENTER("replace_user_table");
+
+ if (combo.password.str && combo.password.str[0])
+ password=combo.password.str;
+ else
+ {
+ password=empty_string;
+ empty_string[0]=0;
+ }
+
+ table->field[0]->store(combo.host.str,combo.host.length);
+ table->field[1]->store(combo.user.str,combo.user.length);
+ table->file->index_init(0);
+ if (table->file->index_read(table->record[0],
+ (byte*) table->field[0]->ptr,0,
+ HA_READ_KEY_EXACT))
+ {
+ if (what == 'N')
+ {
+ my_printf_error(ER_NONEXISTING_GRANT,ER(ER_NONEXISTING_GRANT),
+ MYF(0),combo.user.str,combo.host.str);
+ error= -1;
+ goto end;
+ }
+ ima = 0; // no row; ima on Serbian means 'there is something'
+ restore_record(table,2); // cp empty row from record[2]
+ table->field[0]->store(combo.host.str,combo.host.length);
+ table->field[1]->store(combo.user.str,combo.user.length);
+ table->field[2]->store(password,strlen(password));
+ }
+ else
+ {
+ ima = 1;
+ store_record(table,1); // Save copy for update
+ if (combo.password.str) // If password given
+ table->field[2]->store(password,strlen(password));
+ }
+
+ for (i = 3, j = SELECT_ACL; // starting from reload
+ i < table->fields;
+ i++, j <<= 1)
+ {
+ if (j & rights) // set requested privileges
+ table->field[i]->store(&what,1);
+ }
+ rights=get_access(table,3);
+
+ if (ima) // there is a row, therefore go to update, instead of insert
+ {
+ /*
+ We should NEVER delete from the user table, as a uses can still
+ use mysqld even if he doesn't have any privileges in the user table!
+ */
+ if (cmp_record(table,1) &&
+ (error=table->file->update_row(table->record[1],table->record[0])))
+ { // This should never happen
+ table->file->print_error(error,MYF(0)); /* purecov: deadcode */
+ error= -1; /* purecov: deadcode */
+ goto end; /* purecov: deadcode */
+ }
+ }
+ else if ((error=table->file->write_row(table->record[0]))) // insert
+ { // This should never happen
+ if (error && error != HA_ERR_FOUND_DUPP_KEY &&
+ error != HA_ERR_FOUND_DUPP_UNIQUE) /* purecov: inspected */
+ {
+ table->file->print_error(error,MYF(0)); /* purecov: deadcode */
+ error= -1; /* purecov: deadcode */
+ goto end; /* purecov: deadcode */
+ }
+ }
+ error=0; // Privileges granted / revoked
+
+ end:
+ if (!error)
+ {
+ acl_cache->clear(1); // Clear privilege cache
+ if (!combo.password.str)
+ password=0; // No password given on command
+ if (ima)
+ acl_update_user(combo.user.str,combo.host.str,password,rights);
+ else
+ acl_insert_user(combo.user.str,combo.host.str,password,rights);
+ }
+ table->file->index_end();
+ DBUG_RETURN(error);
+}
+
+
+/*
+** 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)
+{
+ uint i,j,store_rights;
+ bool ima=0;
+ int error;
+ DBUG_ENTER("replace_db_table");
+
+ // is there such a user in user table in memory ????
+ if (!initialized || !find_acl_user(combo.host.str,combo.user.str))
+ {
+ my_error(ER_PASSWORD_NO_MATCH,MYF(0));
+ DBUG_RETURN(-1);
+ }
+
+ table->field[0]->store(combo.host.str,combo.host.length);
+ table->field[1]->store(db,strlen(db));
+ table->field[2]->store(combo.user.str,combo.user.length);
+ table->file->index_init(0);
+ if (table->file->index_read(table->record[0],(byte*) table->field[0]->ptr,0,
+ HA_READ_KEY_EXACT))
+ {
+ if (what == 'N')
+ { // no row, no revoke
+ my_printf_error(ER_NONEXISTING_GRANT,ER(ER_NONEXISTING_GRANT),MYF(0),
+ combo.user.str,combo.host.str);
+ goto abort;
+ }
+ ima = 0; // no row
+ restore_record(table,2); // cp empty row from record[2]
+ table->field[0]->store(combo.host.str,combo.host.length);
+ table->field[1]->store(db,strlen(db));
+ table->field[2]->store(combo.user.str,combo.user.length);
+ }
+ else
+ {
+ ima = 1;
+ store_record(table,1);
+ }
+
+ store_rights=get_rights_for_db(rights);
+ for (i = 3, j = 1; i < table->fields; i++, j <<= 1)
+ {
+ if (j & store_rights) // do it if priv is chosen
+ table->field [i]->store(&what,1); // set requested privileges
+ }
+ rights=get_access(table,3);
+ rights=fix_rights_for_db(rights);
+
+ if (ima) // there is a row, therefore go update, else insert
+ {
+ if (rights)
+ {
+ if ((error=table->file->update_row(table->record[1],table->record[0])))
+ goto table_error; /* purecov: deadcode */
+ }
+ else /* must have been a revoke of all privileges */
+ {
+ if ((error = table->file->delete_row(table->record[1])))
+ goto table_error; /* purecov: deadcode */
+ }
+ }
+ else if ((error=table->file->write_row(table->record[0])))
+ {
+ if (error && error != HA_ERR_FOUND_DUPP_KEY) /* purecov: inspected */
+ goto table_error; /* purecov: deadcode */
+ }
+
+ acl_cache->clear(1); // Clear privilege cache
+ if (ima)
+ acl_update_db(combo.user.str,combo.host.str,db,rights);
+ else
+ acl_insert_db(combo.user.str,combo.host.str,db,rights);
+ table->file->index_end();
+ DBUG_RETURN(0);
+
+ /* This could only happen if the grant tables got corrupted */
+ table_error:
+ table->file->print_error(error,MYF(0)); /* purecov: deadcode */
+ table->file->index_end();
+
+ abort:
+ DBUG_RETURN(-1);
+}
+
+
+class GRANT_COLUMN :public Sql_alloc
+{
+public:
+ char *column;
+ uint rights, key_length;
+ GRANT_COLUMN(String &c, uint y) :rights (y)
+ {
+ 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)))
+{
+ *length=buff->key_length;
+ return (byte*) buff->column;
+}
+
+class GRANT_TABLE :public Sql_alloc
+{
+public:
+ char *host,*db,*user,*tname, *hash_key;
+ uint privs, cols, key_length;
+ HASH hash_columns;
+ GRANT_TABLE (const char *h, const char *d,const char *u, const char *t,
+ uint p,uint 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);
+ key_length =strlen(d)+strlen(u)+strlen(t)+3;
+ hash_key = (char*) alloc_root(&memex,key_length);
+ strmov(strmov(strmov(hash_key,user)+1,db)+1,tname);
+ (void) hash_init(&hash_columns,0,0,0, (hash_get_key) get_key_column,0,
+ HASH_CASE_INSENSITIVE);
+ }
+
+ GRANT_TABLE (TABLE *form, TABLE *col_privs)
+ {
+ byte key[MAX_KEY_LENGTH];
+
+ host = get_field(&memex,form,0);
+ db = get_field(&memex,form,1);
+ user = get_field(&memex,form,2); if (!user) user=(char*) "";
+ tname = get_field(&memex,form,3);
+ if (!host || !db || !tname)
+ {
+ /* Wrong table row; Ignore it */
+ privs = cols = 0; /* purecov: inspected */
+ return; /* purecov: inspected */
+ }
+ key_length = strlen(db) + strlen(user) + 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 = fix_rights_for_table(privs);
+ cols = fix_rights_for_column(cols);
+
+ (void) hash_init(&hash_columns,0,0,0, (hash_get_key) get_key_column,0,
+ HASH_CASE_INSENSITIVE);
+ if (cols)
+ {
+ int key_len;
+ col_privs->field[0]->store(host,strlen(host));
+ col_privs->field[1]->store(db,strlen(db));
+ col_privs->field[2]->store(user,strlen(user));
+ col_privs->field[3]->store(tname,strlen(tname));
+ key_len=(col_privs->field[0]->pack_length()+
+ col_privs->field[1]->pack_length()+
+ col_privs->field[2]->pack_length()+
+ col_privs->field[3]->pack_length());
+ key_copy(key,col_privs,0,key_len);
+ col_privs->field[4]->store("",0);
+ col_privs->file->index_init(0);
+ if (col_privs->file->index_read(col_privs->record[0],
+ (byte*) col_privs->field[0]->ptr,
+ key_len, HA_READ_KEY_EXACT))
+ {
+ cols = 0; /* purecov: deadcode */
+ return;
+ }
+ do
+ {
+ String *res,column_name;
+ 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();
+ if (!(mem_check = new GRANT_COLUMN(*res,
+ fix_rights_for_column(priv))))
+ {
+ // Don't use this entry
+ privs = cols = 0; /* purecov: deadcode */
+ return; /* purecov: deadcode */
+ }
+ hash_insert(&hash_columns, (byte *) mem_check);
+ } while (!col_privs->file->index_next(col_privs->record[0]) &&
+ !key_cmp(col_privs,key,0,key_len));
+ }
+ }
+ bool ok() { return privs != 0 || cols != 0; }
+};
+
+static byte* get_grant_table(GRANT_TABLE *buff,uint *length,
+ my_bool not_used __attribute__((unused)))
+{
+ *length=buff->key_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,
+ const char *db,
+ const char *user, const char *tname,
+ bool exact)
+{
+ char helping [NAME_LEN*2+USERNAME_LENGTH+3];
+ uint len;
+ GRANT_TABLE *grant_table,*found=0;
+
+ len = (uint) (strmov(strmov(strmov(helping,user)+1,db)+1,tname)-helping)+ 1;
+ for (grant_table=(GRANT_TABLE*) hash_search(&hash_tables,(byte*) helping,
+ len) ;
+ grant_table ;
+ grant_table= (GRANT_TABLE*) hash_next(&hash_tables,(byte*) helping,len))
+ {
+ if (exact)
+ {
+ if ((host && !strcmp(host,grant_table->host)) ||
+ (ip && !strcmp(ip,grant_table->host)))
+ return grant_table;
+ }
+ else
+ {
+ if ((host && !wild_case_compare(host,grant_table->host)) ||
+ (ip && !wild_case_compare(ip,grant_table->host)))
+ found=grant_table; // Host ok
+ }
+ }
+ return found;
+}
+
+
+
+static inline GRANT_COLUMN *
+column_hash_search(GRANT_TABLE *t, const char *cname,
+ uint length)
+{
+ return (GRANT_COLUMN*) hash_search(&t->hash_columns, (byte*) cname,length);
+}
+
+
+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)
+{
+ int error=0,result=0;
+ uint key_length;
+ byte key[MAX_KEY_LENGTH];
+ DBUG_ENTER("replace_column_table");
+
+ table->field[0]->store(combo.host.str,combo.host.length);
+ table->field[1]->store(db,strlen(db));
+ table->field[2]->store(combo.user.str,combo.user.length);
+ table->field[3]->store(table_name,strlen(table_name));
+ key_length=(table->field[0]->pack_length()+ table->field[1]->pack_length()+
+ table->field[2]->pack_length()+ table->field[3]->pack_length());
+ key_copy(key,table,0,key_length);
+
+ rights &= COL_ACLS; // Only ACL for columns
+
+ /* first fix privileges for all columns in column list */
+
+ List_iterator <LEX_COLUMN> iter(columns);
+ class LEX_COLUMN *xx;
+ table->file->index_init(0);
+ while ((xx=iter++))
+ {
+ uint privileges = xx->rights;
+ bool ima=0;
+ key_restore(table,key,0,key_length);
+ table->field[4]->store(xx->column.ptr(),xx->column.length());
+
+ if (table->file->index_read(table->record[0],(byte*) table->field[0]->ptr,
+ 0, HA_READ_KEY_EXACT))
+ {
+ if (revoke_grant)
+ {
+ my_printf_error(ER_NONEXISTING_TABLE_GRANT,
+ ER(ER_NONEXISTING_TABLE_GRANT),MYF(0),
+ combo.user.str, combo.host.str,table_name); /* purecov: inspected */
+ result= -1; /* purecov: inspected */
+ continue; /* purecov: inspected */
+ }
+ ima = 0;
+ restore_record(table,2); // Get empty record
+ key_restore(table,key,0,key_length);
+ table->field[4]->store(xx->column.ptr(),xx->column.length());
+ }
+ else
+ {
+ uint tmp= (uint) table->field[6]->val_int();
+ tmp=fix_rights_for_column(tmp);
+
+ if (revoke_grant)
+ privileges = tmp & ~(privileges | rights);
+ else
+ privileges |= tmp;
+ ima = 1;
+ store_record(table,1); // copy original row
+ }
+
+ table->field[6]->store((longlong) get_rights_for_column(privileges));
+
+ if (ima) // there is a row, therefore go update, else insert
+ {
+ if (privileges)
+ error=table->file->update_row(table->record[1],table->record[0]);
+ else
+ error=table->file->delete_row(table->record[1]);
+ if (error)
+ {
+ table->file->print_error(error,MYF(0)); /* purecov: inspected */
+ result= -1; /* purecov: inspected */
+ goto end; /* purecov: inspected */
+ }
+ GRANT_COLUMN *grant_column = column_hash_search(g_t,
+ xx->column.ptr(),
+ xx->column.length());
+ if (grant_column) // Should always be true
+ grant_column->rights = privileges; // Update hash
+ }
+ else // new grant
+ {
+ if ((error=table->file->write_row(table->record[0])))
+ {
+ table->file->print_error(error,MYF(0)); /* purecov: inspected */
+ result= -1; /* purecov: inspected */
+ goto end; /* purecov: inspected */
+ }
+ GRANT_COLUMN *grant_column = new GRANT_COLUMN(xx->column,privileges);
+ hash_insert(&g_t->hash_columns,(byte*) grant_column);
+ }
+ }
+ table->file->index_end();
+
+ /*
+ If revoke of privileges on the table level, remove all such privileges
+ for all columns
+ */
+
+ if (revoke_grant)
+ {
+ table->file->index_init(0);
+ if (table->file->index_read(table->record[0], (byte*) table->field[0]->ptr,
+ key_length, HA_READ_KEY_EXACT))
+ goto end;
+
+ // Scan trough all rows with the same host,db,user and table
+ do
+ {
+ uint privileges = (uint) table->field[6]->val_int();
+ privileges=fix_rights_for_column(privileges);
+ store_record(table,1);
+
+ if (privileges & rights) // is in this record the priv to be revoked ??
+ {
+ GRANT_COLUMN *grant_column = NULL;
+ char colum_name_buf[HOSTNAME_LENGTH+1];
+ String column_name(colum_name_buf,sizeof(colum_name_buf));
+
+ privileges&= ~rights;
+ table->field[6]->store((longlong)
+ get_rights_for_column(privileges));
+ table->field[4]->val_str(&column_name,&column_name);
+ grant_column = column_hash_search(g_t,
+ column_name.ptr(),
+ column_name.length());
+ if (privileges)
+ {
+ int tmp_error;
+ if ((tmp_error=table->file->update_row(table->record[1],
+ table->record[0])))
+ { /* purecov: deadcode */
+ table->file->print_error(tmp_error,MYF(0)); /* purecov: deadcode */
+ result= -1; /* purecov: deadcode */
+ goto end; /* purecov: deadcode */
+ }
+ if (grant_column)
+ grant_column->rights = privileges; // Update hash
+ }
+ else
+ {
+ int tmp_error;
+ if ((tmp_error = table->file->delete_row(table->record[1])))
+ { /* purecov: deadcode */
+ table->file->print_error(tmp_error,MYF(0)); /* purecov: deadcode */
+ result= -1; /* purecov: deadcode */
+ goto end; /* purecov: deadcode */
+ }
+ if (grant_column)
+ hash_delete(&g_t->hash_columns,(byte*) grant_column);
+ }
+ }
+ } while (!table->file->index_next(table->record[0]) &&
+ !key_cmp(table,key,0,key_length));
+ }
+
+ end:
+ table->file->index_end();
+ DBUG_RETURN(result);
+}
+
+
+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)
+{
+ char grantor[HOSTNAME_LENGTH+1+USERNAME_LENGTH];
+ int ima = 1;
+ int error=0;
+ uint store_table_rights,store_col_rights;
+ DBUG_ENTER("replace_table_table");
+
+ strxmov(grantor,thd->user,"@",thd->host ? thd->host : thd->ip ? thd->ip :"",
+ NullS);
+
+ // 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 */
+ DBUG_RETURN(-1); /* purecov: deadcode */
+ }
+
+ restore_record(table,2); // Get empty record
+ table->field[0]->store(combo.host.str,combo.host.length);
+ table->field[1]->store(db,strlen(db));
+ table->field[2]->store(combo.user.str,combo.user.length);
+ table->field[3]->store(table_name,strlen(table_name));
+ store_record(table,1); // store at pos 1
+
+ if (table->file->index_read_idx(table->record[0],0,
+ (byte*) table->field[0]->ptr,0,
+ HA_READ_KEY_EXACT))
+ {
+ /*
+ The following should never happen as we first check the in memory
+ grant tables for the user. There is however always a small change that
+ the user has modified the grant tables directly.
+ */
+ if (revoke_grant)
+ { // no row, no revoke
+ my_printf_error(ER_NONEXISTING_TABLE_GRANT,
+ ER(ER_NONEXISTING_TABLE_GRANT),MYF(0),
+ combo.user.str,combo.host.str,
+ table_name); /* purecov: deadcode */
+ DBUG_RETURN(-1); /* purecov: deadcode */
+ }
+ ima = 0; // no row
+ restore_record(table,1); // Get saved record
+ }
+
+ store_table_rights=get_rights_for_table(rights);
+ store_col_rights=get_rights_for_column(kolone);
+ if (ima)
+ {
+ uint j,k;
+ store_record(table,1);
+ j = (uint) table->field[6]->val_int();
+ k = (uint) table->field[7]->val_int();
+
+ if (revoke_grant)
+ {
+ // column rights are already fixed in mysql_table_grant !
+ store_table_rights=j & ~store_table_rights;
+ }
+ else
+ {
+ store_table_rights|=j;
+ store_col_rights|=k;
+ }
+ }
+
+ table->field[4]->store(grantor,strlen(grantor));
+ 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);
+
+ if (ima) // there is a row, therefore go update, else insert
+ {
+ if (store_table_rights || store_col_rights)
+ {
+ if ((error=table->file->update_row(table->record[1],table->record[0])))
+ goto table_error; /* purecov: deadcode */
+ }
+ else if ((error = table->file->delete_row(table->record[1])))
+ goto table_error; /* purecov: deadcode */
+ }
+ else
+ {
+ error=table->file->write_row(table->record[0]);
+ if (error && error != HA_ERR_FOUND_DUPP_KEY)
+ goto table_error; /* purecov: deadcode */
+ }
+
+ if (rights | kolone)
+ {
+ grant_table->privs = rights;
+ grant_table->cols = kolone;
+ }
+ else
+ {
+ hash_delete(&hash_tables,(byte*) grant_table);
+ }
+ DBUG_RETURN(0);
+
+ /* This should never happen */
+ table_error:
+ table->file->print_error(error,MYF(0)); /* purecov: deadcode */
+ DBUG_RETURN(-1); /* purecov: deadcode */
+}
+
+
+int mysql_table_grant (THD *thd, TABLE_LIST *table_list,
+ List <LEX_USER> &user_list,
+ List <LEX_COLUMN> &columns, uint rights,
+ bool revoke_grant)
+{
+ uint column_priv = 0;
+ List_iterator <LEX_USER> str_list (user_list);
+ LEX_USER *Str;
+ TABLE_LIST tables[3];
+ DBUG_ENTER("mysql_table_grant");
+
+ if (!initialized)
+ {
+ send_error(&(thd->net), ER_UNKNOWN_COM_ERROR); /* purecov: inspected */
+ return 1; /* purecov: inspected */
+ }
+ if (rights & ~TABLE_ACLS)
+ {
+ my_error(ER_ILLEGAL_GRANT_FOR_TABLE,MYF(0));
+ DBUG_RETURN(-1);
+ }
+
+ if (columns.elements && !revoke_grant)
+ {
+ TABLE *table;
+ class LEX_COLUMN *check;
+ List_iterator <LEX_COLUMN> iter(columns);
+
+ if (!(table=open_ltable(thd,table_list,TL_READ)))
+ DBUG_RETURN(-1);
+ while ((check = iter++))
+ {
+ if (!find_field_in_table(thd,table,check->column.ptr(),
+ 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->name);
+ DBUG_RETURN(-1);
+ }
+ column_priv |= check->rights | (rights & COL_ACLS);
+ }
+ close_thread_tables(thd);
+ }
+ else if (!(rights & CREATE_ACL) && !revoke_grant)
+ {
+ char buf[FN_REFLEN];
+ sprintf(buf,"%s/%s/%s.frm",mysql_data_home,table_list->db,
+ table_list->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->name);
+ DBUG_RETURN(-1);
+ }
+ }
+
+ /* open the mysql.tables_priv and mysql.columns_priv tables */
+
+ bzero((char*) &tables,sizeof(tables));
+ tables[0].name=tables[0].real_name= (char*) "user";
+ tables[1].name=tables[1].real_name= (char*) "tables_priv";
+ tables[2].name=tables[2].real_name= (char*) "columns_priv";
+ tables[0].next=tables+1;
+ /* Don't open column table if we don't need it ! */
+ tables[1].next=((column_priv ||
+ (revoke_grant && ((rights & COL_ACLS) || columns.elements)))
+ ? tables+2 : 0);
+ tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_WRITE;
+ tables[0].db=tables[1].db=tables[2].db=(char*) "mysql";
+
+ if (open_and_lock_tables(thd,tables))
+ { // Should never happen
+ close_thread_tables(thd); /* purecov: deadcode */
+ DBUG_RETURN(-1); /* purecov: deadcode */
+ }
+
+ int result=0;
+ pthread_mutex_lock(&LOCK_grant);
+ MEM_ROOT *old_root=my_pthread_getspecific_ptr(MEM_ROOT*,THR_MALLOC);
+ my_pthread_setspecific_ptr(THR_MALLOC,&memex);
+
+ while ((Str = str_list++))
+ {
+ GRANT_TABLE *grant_table;
+ if (!Str->host.str)
+ {
+ Str->host.str=(char*) "%";
+ Str->host.length=1;
+ }
+ if (Str->host.length > HOSTNAME_LENGTH ||
+ Str->user.length > USERNAME_LENGTH)
+ {
+ my_error(ER_GRANT_WRONG_HOST_OR_USER,MYF(0));
+ result= -1;
+ continue;
+ }
+ /* Create user if needed */
+ if ((replace_user_table(tables[0].table,
+ *Str,
+ 0,
+ revoke_grant ? 'N' : 'Y')))
+ {
+ result= -1; // Remember error
+ continue; // Add next user
+ }
+
+ /* Find/create cached table grant */
+ grant_table= table_hash_search(Str->host.str,NullS,table_list->db,
+ Str->user.str,
+ table_list->name,1);
+ if (!grant_table)
+ {
+ if (revoke_grant)
+ {
+ my_printf_error(ER_NONEXISTING_TABLE_GRANT,
+ ER(ER_NONEXISTING_TABLE_GRANT),MYF(0),
+ Str->user.str, Str->host.str,table_list->name);
+ result= -1;
+ continue;
+ }
+ grant_table = new GRANT_TABLE (Str->host.str,table_list->db,
+ Str->user.str,
+ table_list->name,
+ rights,
+ column_priv);
+ if (!grant_table) // end of memory
+ {
+ result= -1; /* purecov: deadcode */
+ continue; /* purecov: deadcode */
+ }
+ hash_insert(&hash_tables,(byte*) grant_table);
+ }
+
+ /* If revoke_grant, calculate the new column privilege for tables_priv */
+ if (revoke_grant)
+ {
+ class LEX_COLUMN *check;
+ List_iterator <LEX_COLUMN> iter(columns);
+ GRANT_COLUMN *grant_column;
+
+ /* Fix old grants */
+ while ((check = iter++))
+ {
+ grant_column = column_hash_search(grant_table,
+ check->column.ptr(),
+ check->column.length());
+ if (grant_column)
+ grant_column->rights&= ~(check->rights | rights);
+ }
+ /* scan trough all columns to get new column grant */
+ column_priv=0;
+ for (uint idx=0 ; idx < grant_table->hash_columns.records ; idx++)
+ {
+ grant_column= (GRANT_COLUMN*) hash_element(&grant_table->hash_columns,
+ idx);
+ grant_column->rights&= ~rights; // Fix other columns
+ column_priv|= grant_column->rights;
+ }
+ }
+ else
+ {
+ column_priv|= grant_table->cols;
+ }
+
+
+ /* update table and columns */
+
+ if (replace_table_table(thd,grant_table,tables[1].table,*Str,
+ table_list->db,
+ table_list->name,
+ rights, column_priv, revoke_grant))
+ { // Crashend table ??
+ result= -1; /* purecov: deadcode */
+ }
+ else if (tables[2].table)
+ {
+ if ((replace_column_table(grant_table,tables[2].table, *Str,
+ columns,
+ table_list->db,
+ table_list->name,
+ rights, revoke_grant)))
+ {
+ result= -1;
+ }
+ }
+ }
+ grant_option=TRUE;
+ my_pthread_setspecific_ptr(THR_MALLOC,old_root);
+ pthread_mutex_unlock(&LOCK_grant);
+ if (!result)
+ send_ok(&thd->net);
+ /* Tables are automaticly closed */
+ DBUG_RETURN(result);
+}
+
+
+int mysql_grant (THD *thd, const char *db, List <LEX_USER> &list, uint rights,
+ bool revoke_grant)
+{
+ List_iterator <LEX_USER> str_list (list);
+ LEX_USER *Str;
+ char what;
+ TABLE_LIST tables[2];
+ DBUG_ENTER("mysql_grant");
+
+ if (!initialized)
+ {
+ send_error(&(thd->net), ER_UNKNOWN_COM_ERROR); /* purecov: tested */
+ return 1; /* purecov: tested */
+ }
+
+ what = (revoke_grant) ? 'N' : 'Y';
+
+ /* open the mysql.user and mysql.db tables */
+
+ tables[0].name=tables[0].real_name=(char*) "user";
+ tables[1].name=tables[1].real_name=(char*) "db";
+ tables[0].next=tables+1;
+ tables[1].next=0;
+ tables[0].lock_type=tables[1].lock_type=TL_WRITE;
+ tables[0].db=tables[1].db=(char*) "mysql";
+ tables[0].table=tables[1].table=0;
+ if (open_and_lock_tables(thd,tables))
+ { // This should never happen
+ close_thread_tables(thd); /* purecov: deadcode */
+ DBUG_RETURN(-1); /* purecov: deadcode */
+ }
+
+ // go through users in user_list
+
+ pthread_mutex_lock(&LOCK_grant);
+ VOID(pthread_mutex_lock(&acl_cache->lock));
+ grant_version++;
+
+ int result=0;
+ while ((Str = str_list++))
+ {
+ if (!Str->host.str)
+ {
+ Str->host.str=(char*) "%";
+ Str->host.length=1;
+ }
+ if (Str->host.length > HOSTNAME_LENGTH ||
+ Str->user.length > USERNAME_LENGTH)
+ {
+ my_error(ER_GRANT_WRONG_HOST_OR_USER,MYF(0));
+ result= -1;
+ continue;
+ }
+ if ((replace_user_table(tables[0].table,
+ *Str,
+ (!db ? rights : 0), what)))
+ result= -1;
+ if (db)
+ if (( replace_db_table(tables[1].table, db, *Str, rights,
+ what)))
+ result= -1;
+ }
+ VOID(pthread_mutex_unlock(&acl_cache->lock));
+ pthread_mutex_unlock(&LOCK_grant);
+ close_thread_tables(thd);
+
+ if (!result)
+ send_ok(&thd->net);
+ DBUG_RETURN(result);
+}
+
+ /* Free grant array if possible */
+
+void grant_free(void)
+{
+ DBUG_ENTER("grant_free");
+ grant_option = FALSE;
+ hash_free(&hash_tables);
+ free_root(&memex);
+ DBUG_VOID_RETURN;
+}
+
+
+/* Init grant array if possible */
+
+int grant_init (void)
+{
+ THD *thd;
+ TABLE_LIST tables[2];
+ int error = 0;
+ TABLE *t_table, *c_table;
+ DBUG_ENTER("grant_init");
+
+ grant_option = FALSE;
+ (void) hash_init(&hash_tables,0,0,0, (hash_get_key) get_grant_table,
+ (hash_free_key) free_grant_table,0);
+ init_sql_alloc(&memex,1024);
+
+ 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));
+ bzero((char*) &tables,sizeof(tables));
+ tables[0].name=tables[0].real_name= (char*) "tables_priv";
+ tables[1].name=tables[1].real_name= (char*) "columns_priv";
+ tables[0].next=tables+1;
+ tables[0].lock_type=tables[1].lock_type=TL_READ;
+ 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 */
+ }
+ 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 */
+ }
+
+ 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);
+ close_thread_tables(thd);
+ delete thd;
+ DBUG_RETURN(0); // Empty table is ok!
+ }
+ grant_option = TRUE;
+ t_table->file->index_end();
+
+ MEM_ROOT *old_root=my_pthread_getspecific_ptr(MEM_ROOT*,THR_MALLOC);
+ my_pthread_setspecific_ptr(THR_MALLOC,&memex);
+ while (!error)
+ {
+ 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 */
+ }
+ error = t_table->file->index_next(t_table->record[0]);
+ }
+ my_pthread_setspecific_ptr(THR_MALLOC,old_root);
+ mysql_unlock_tables(thd, lock);
+ thd->version--; // Force close to free memory
+ close_thread_tables(thd);
+ delete thd;
+ DBUG_RETURN(0);
+}
+
+
+/* Reload grant array if possible */
+
+void grant_reload(void)
+{
+ HASH old_hash_tables;bool old_grant_option;
+ MEM_ROOT old_mem;
+ DBUG_ENTER("grant_reload");
+
+ // Locked tables are checked by acl_init and doesn't have to be checked here
+
+ pthread_mutex_lock(&LOCK_grant);
+ grant_version++;
+ old_hash_tables=hash_tables;
+ old_grant_option = grant_option;
+ old_mem = memex;
+
+ if (grant_init())
+ { // Error. Revert to old hash
+ grant_free(); /* purecov: deadcode */
+ hash_tables=old_hash_tables; /* purecov: deadcode */
+ grant_option = old_grant_option; /* purecov: deadcode */
+ memex = old_mem; /* purecov: deadcode */
+ }
+ else
+ {
+ hash_free(&old_hash_tables);
+ free_root(&old_mem);
+ }
+ pthread_mutex_unlock(&LOCK_grant);
+ DBUG_VOID_RETURN;
+}
+
+
+/****************************************************************************
+** 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,
+ uint show_table)
+{
+ TABLE_LIST *table;
+ char *user = thd->priv_user;
+
+ want_access &= ~thd->master_access;
+ if (!want_access)
+ return 0; // ok
+
+ pthread_mutex_lock(&LOCK_grant);
+ for (table=tables; table ;table=table->next)
+ {
+ if (!(~table->grant.privilege & want_access))
+ {
+ 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,
+ table->real_name,0);
+ if (!grant_table)
+ {
+ want_access &= ~table->grant.privilege;
+ goto err; // No grants
+ }
+
+ table->grant.grant_table=grant_table; // Remember for column test
+ table->grant.version=grant_version;
+ table->grant.privilege|= grant_table->privs;
+ table->grant.want_privilege= ((want_access & COL_ACLS)
+ & ~table->grant.privilege);
+
+ if (!(~table->grant.privilege & want_access))
+ continue;
+ if (show_table && table->grant.privilege)
+ continue; // Test from show tables
+
+ if (want_access & ~(grant_table->cols | table->grant.privilege))
+ {
+ want_access &= ~(grant_table->cols | table->grant.privilege);
+ goto err; // impossible
+ }
+ }
+ pthread_mutex_unlock(&LOCK_grant);
+ return 0;
+
+ err:
+ pthread_mutex_unlock(&LOCK_grant);
+ if (show_table != 1) // Not a silent skip of table
+ {
+ 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";
+ else if (want_access & DELETE_ACL)
+ command = "delete";
+ else if (want_access & DROP_ACL)
+ command = "drop";
+ else if (want_access & CREATE_ACL)
+ command = "create";
+ else if (want_access & ALTER_ACL)
+ command = "alter";
+ else if (want_access & INDEX_ACL)
+ command = "index";
+ else if (want_access & GRANT_ACL)
+ command = "grant";
+ net_printf(&thd->net,ER_TABLEACCESS_DENIED_ERROR,
+ command,
+ thd->priv_user,
+ thd->host ? thd->host : (thd->ip ? thd->ip : "unknown"),
+ table ? table->real_name : "unknown");
+ }
+ return 1;
+}
+
+
+bool check_grant_column (THD *thd,TABLE *table, const char *name,
+ uint length, uint show_tables)
+{
+ GRANT_TABLE *grant_table;
+ GRANT_COLUMN *grant_column;
+
+ uint want_access=table->grant.want_privilege;
+ if (!want_access)
+ return 0; // Already checked
+
+ pthread_mutex_lock(&LOCK_grant);
+
+ // reload table if someone has modified any grants
+
+ if (table->grant.version != grant_version)
+ {
+ table->grant.grant_table=
+ table_hash_search(thd->host,thd->ip,thd->db,
+ thd->priv_user,
+ table->real_name,0); /* purecov: inspected */
+ table->grant.version=grant_version; /* purecov: inspected */
+ }
+ if (!(grant_table=table->grant.grant_table))
+ goto err; /* purecov: deadcode */
+
+ grant_column=column_hash_search(grant_table, name, length);
+ if (grant_column && !(~grant_column->rights & want_access))
+ {
+ pthread_mutex_unlock(&LOCK_grant);
+ return 0;
+ }
+#ifdef NOT_USED
+ if (show_tables && (grant_column || table->grant.privilege & COL_ACLS))
+ {
+ pthread_mutex_unlock(&LOCK_grant); /* purecov: deadcode */
+ return 0; /* purecov: deadcode */
+ }
+#endif
+
+ /* We must use my_printf_error() here! */
+ err:
+ 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";
+ 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"),
+ name,
+ table ? table->real_name : "unknown");
+ }
+ return 1;
+}
+
+
+bool check_grant_all_columns(THD *thd,uint want_access, TABLE *table)
+{
+ GRANT_TABLE *grant_table;
+ GRANT_COLUMN *grant_column;
+ Field *field=0,**ptr;
+
+ want_access &= ~table->grant.privilege;
+ if (!want_access)
+ return 0; // Already checked
+
+ pthread_mutex_lock(&LOCK_grant);
+
+ // reload table if someone has modified any grants
+
+ if (table->grant.version != grant_version)
+ {
+ table->grant.grant_table=
+ table_hash_search(thd->host,thd->ip,thd->db,
+ thd->priv_user,
+ table->real_name,0); /* purecov: inspected */
+ table->grant.version=grant_version; /* purecov: inspected */
+ }
+ // The following should always be true
+ if (!(grant_table=table->grant.grant_table))
+ goto err; /* purecov: inspected */
+
+ for (ptr=table->field; (field= *ptr) ; ptr++)
+ {
+ grant_column=column_hash_search(grant_table, field->field_name,
+ strlen(field->field_name));
+ if (!grant_column || (~grant_column->rights & want_access))
+ goto err;
+ }
+ pthread_mutex_unlock(&LOCK_grant);
+ return 0;
+
+ /* We must use my_printf_error() here! */
+ err:
+ pthread_mutex_unlock(&LOCK_grant);
+
+ const char *command="";
+ if (want_access & SELECT_ACL)
+ command ="select";
+ else if (want_access & INSERT_ACL)
+ command = "insert";
+ 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"),
+ field ? field->field_name : "unknown",
+ table->real_name);
+ return 1;
+}
+
+
+/****************************************************************************
+** 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)
+{
+ char helping [NAME_LEN+USERNAME_LENGTH+2];
+ uint len;
+ bool error=1;
+
+ len = (uint) (strmov(strmov(helping,thd->priv_user)+1,db)-helping)+ 1;
+ pthread_mutex_lock(&LOCK_grant);
+
+ for (uint idx=0 ; idx < hash_tables.records ; idx++)
+ {
+ GRANT_TABLE *grant_table = (GRANT_TABLE*) hash_element(&hash_tables,idx);
+ if (len < grant_table->key_length &&
+ !memcmp(grant_table->hash_key,helping,len) &&
+ (thd->host && !wild_case_compare(thd->host,grant_table->host) ||
+ (thd->ip && !wild_case_compare(thd->ip,grant_table->host))))
+ {
+ error=0; // Found match
+ break;
+ }
+ }
+ pthread_mutex_unlock(&LOCK_grant);
+ return error;
+}
+
+/*****************************************************************************
+** Functions to retrieve the grant for a table/column (for SHOW functions)
+*****************************************************************************/
+
+uint get_table_grant(THD *thd, TABLE_LIST *table)
+{
+ char *user = thd->priv_user;
+ const char *db = table->db ? table->db : thd->db;
+ GRANT_TABLE *grant_table;
+
+ pthread_mutex_lock(&LOCK_grant);
+ grant_table = table_hash_search(thd->host,thd->ip,db,user,
+ table->real_name,0);
+ table->grant.grant_table=grant_table; // Remember for column test
+ table->grant.version=grant_version;
+ if (grant_table)
+ table->grant.privilege|= grant_table->privs;
+ pthread_mutex_unlock(&LOCK_grant);
+ return table->grant.privilege;
+}
+
+
+uint get_column_grant(THD *thd, TABLE_LIST *table, Field *field)
+{
+ GRANT_TABLE *grant_table;
+ GRANT_COLUMN *grant_column;
+ uint priv;
+
+ pthread_mutex_lock(&LOCK_grant);
+ // reload table if someone has modified any grants
+ if (table->grant.version != grant_version)
+ {
+ table->grant.grant_table=
+ table_hash_search(thd->host,thd->ip,thd->db,
+ thd->priv_user,
+ table->real_name,0); /* purecov: inspected */
+ table->grant.version=grant_version; /* purecov: inspected */
+ }
+
+ if (!(grant_table=table->grant.grant_table))
+ priv=table->grant.privilege;
+ else
+ {
+ grant_column=column_hash_search(grant_table, field->field_name,
+ strlen(field->field_name));
+ if (!grant_column)
+ priv=table->grant.privilege;
+ else
+ priv=table->grant.privilege | grant_column->rights;
+ }
+ pthread_mutex_unlock(&LOCK_grant);
+ return priv;
+}
+
+
+/*****************************************************************************
+** 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,9,5,5};
+
+int mysql_show_grants(THD *thd,LEX_USER *lex_user)
+{
+ uint counter, want_access,index;
+ int error = 0;
+ ACL_USER *acl_user; ACL_DB *acl_db;
+ char buff[1024];
+ DBUG_ENTER("mysql_grant");
+
+ LINT_INIT(acl_user);
+ if (!initialized)
+ {
+ send_error(&(thd->net), ER_UNKNOWN_COM_ERROR);
+ DBUG_RETURN(-1);
+ }
+ if (!lex_user->host.str)
+ {
+ lex_user->host.str=(char*) "%";
+ lex_user->host.length=1;
+ }
+ if (lex_user->host.length > HOSTNAME_LENGTH ||
+ lex_user->user.length > USERNAME_LENGTH)
+ {
+ my_error(ER_GRANT_WRONG_HOST_OR_USER,MYF(0));
+ DBUG_RETURN(-1);
+ }
+
+ for (counter=0 ; counter < acl_users.elements ; counter++)
+ {
+ const char *user,*host;
+ acl_user=dynamic_element(&acl_users,counter,ACL_USER*);
+ if (!(user=acl_user->user))
+ user="";
+ if (!(host=acl_user->host.hostname))
+ host="%";
+ if (!strcmp(lex_user->user.str,user) &&
+ !strcmp(lex_user->host.str,host))
+ break;
+ }
+ if (counter == acl_users.elements)
+ {
+ my_printf_error(ER_NONEXISTING_GRANT,ER(ER_NONEXISTING_GRANT),
+ MYF(0),lex_user->user.str,lex_user->host.str);
+ DBUG_RETURN(-1);
+ }
+
+ Item_string *field=new Item_string("",0);
+ List<Item> field_list;
+ field->name=buff;
+ field->max_length=1024;
+ strxmov(buff,"Grants for ",lex_user->user.str,"@",
+ lex_user->host.str,NullS);
+ field_list.push_back(field);
+ if (send_fields(thd,field_list,1))
+ DBUG_RETURN(-1);
+
+ 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));
+ global.length(0);
+ global.append("GRANT ",6);
+
+ if (test_all_bits(want_access, (GLOBAL_ACLS & ~ GRANT_ACL)))
+ global.append("ALL PRIVILEGES",14);
+ else if (!(want_access & ~GRANT_ACL))
+ global.append("USAGE",5);
+ else
+ {
+ bool found=0;
+ uint j,test_access= want_access & ~GRANT_ACL;
+ for (counter=0, j = SELECT_ACL;j <= GLOBAL_ACLS;counter++,j <<= 1)
+ {
+ if (test_access & j)
+ {
+ if (found)
+ global.append(", ",2);
+ found=1;
+ global.append(command_array[counter],command_lengths[counter]);
+ }
+ }
+ }
+ global.append (" ON *.* TO '",12);
+ global.append(lex_user->user.str,lex_user->user.length);
+ global.append ("'@'",3);
+ global.append(lex_user->host.str,lex_user->host.length);
+ global.append ('\'');
+ if (acl_user->password)
+ {
+ char passd_buff[HASH_PASSWORD_LENGTH+1];
+ make_password_from_salt(passd_buff,acl_user->salt);
+ global.append(" IDENTIFIED BY PASSWORD '",25);
+ global.append(passd_buff);
+ global.append('\'');
+ }
+ if (want_access & GRANT_ACL)
+ global.append(" WITH GRANT OPTION",18);
+ thd->packet.length(0);
+ net_store_data(&thd->packet,global.ptr(),global.length());
+ if (my_net_write(&thd->net,(char*) thd->packet.ptr(),
+ thd->packet.length()))
+ {
+ error=-1; goto end;
+ }
+ }
+
+ /* Add database access */
+ for (counter=0 ; counter < acl_dbs.elements ; counter++)
+ {
+ const char *user,*host;
+
+ acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*);
+ if (!(user=acl_db->user))
+ user="";
+ if (!(host=acl_db->host.hostname))
+ host="";
+
+ if (!strcmp(lex_user->user.str,user) &&
+ !strcmp(lex_user->host.str,host))
+ {
+ want_access=acl_db->access;
+ if (want_access)
+ {
+ String db(buff,sizeof(buff));
+ db.length(0);
+ db.append("GRANT ",6);
+
+ if (test_all_bits(want_access,(DB_ACLS & ~GRANT_ACL)))
+ db.append("ALL PRIVILEGES",14);
+ else
+ {
+ int found=0, cnt;
+ uint j,test_access= want_access & ~GRANT_ACL;
+ for (cnt=0, j = SELECT_ACL; j <= DB_ACLS; cnt++,j <<= 1)
+ {
+ if (test_access & j)
+ {
+ if (found)
+ db.append(", ",2);
+ found = 1;
+ db.append(command_array[cnt],command_lengths[cnt]);
+ }
+ }
+ }
+ db.append (" ON ",4);
+ db.append(acl_db->db);
+ db.append (".* TO '",7);
+ db.append(lex_user->user.str,lex_user->user.length);
+ db.append ("'@'",3);
+ db.append(lex_user->host.str, lex_user->host.length);
+ db.append ('\'');
+ if (want_access & GRANT_ACL)
+ 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(),
+ thd->packet.length()))
+ {
+ error=-1;
+ goto end;
+ }
+ }
+ }
+ }
+
+ /* Add column access */
+ for (index=0 ; index < hash_tables.records ; index++)
+ {
+ const char *user,*host;
+ GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&hash_tables,index);
+
+ if (!(user=grant_table->user))
+ user="";
+ if (!(host=grant_table->host))
+ host="";
+
+ if (!strcmp(lex_user->user.str,user) &&
+ !strcmp(lex_user->host.str,host))
+ {
+ want_access=grant_table->privs;
+ if (want_access)
+ {
+ String global(buff,sizeof(buff));
+ global.length(0);
+ global.append("GRANT ",6);
+
+ if (test_all_bits(want_access,(TABLE_ACLS & ~GRANT_ACL)))
+ global.append("ALL PRIVILEGES",14);
+ else
+ {
+ int found=0;
+ uint j,test_access= want_access & ~GRANT_ACL;
+
+ for (counter=0, j = SELECT_ACL;j <= TABLE_ACLS; counter++,j <<= 1)
+ {
+ if (test_access & j)
+ {
+ if (found)
+ global.append(", ",2);
+ found = 1;
+ global.append(command_array[counter],command_lengths[counter]);
+
+ if (grant_table->cols)
+ {
+ uint found_col=0;
+ for (uint col_index=0 ;
+ col_index < grant_table->hash_columns.records ;
+ col_index++)
+ {
+ GRANT_COLUMN *grant_column = (GRANT_COLUMN*)
+ hash_element(&grant_table->hash_columns,col_index);
+ if (grant_column->rights & j)
+ {
+ if (!found_col)
+ {
+ global.append(" (",2);
+ found_col=1;
+ }
+ else
+ global.append(", ",2);
+ global.append(grant_column->column,
+ grant_column->key_length);
+ }
+ }
+ if (found_col)
+ global.append(')');
+ }
+ }
+ }
+ }
+ global.append(" ON ",4);
+ global.append(grant_table->db);
+ global.append(".",1);
+ global.append(grant_table->tname);
+ global.append(" TO '",5);
+ global.append(lex_user->user.str,lex_user->user.length);
+ global.append("'@'",3);
+ global.append(lex_user->host.str,lex_user->host.length);
+ global.append('\'');
+ if (want_access & GRANT_ACL)
+ global.append(" WITH GRANT OPTION",18);
+ thd->packet.length(0);
+ net_store_data(&thd->packet,global.ptr(),global.length());
+ if (my_net_write(&thd->net,(char*) thd->packet.ptr(),
+ thd->packet.length()))
+ {
+ error=-1;
+ goto end;
+ }
+ }
+ }
+ }
+ end:
+ VOID(pthread_mutex_unlock(&acl_cache->lock));
+ send_eof(&thd->net);
+ DBUG_RETURN(error);
+}
+
+
+/*****************************************************************************
+** Instantiate used templates
+*****************************************************************************/
+
+#ifdef __GNUC__
+template class List_iterator<LEX_COLUMN>;
+template class List_iterator<LEX_USER>;
+template class List<LEX_COLUMN>;
+template class List<LEX_USER>;
+#endif
diff --git a/sql/sql_acl.h b/sql/sql_acl.h
new file mode 100644
index 00000000000..ff9a105d76b
--- /dev/null
+++ b/sql/sql_acl.h
@@ -0,0 +1,84 @@
+/* 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 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))
+#define get_rights_for_column(A) (((A) & COL_ACLS) | ((A & ~COL_ACLS) >> 7))
+
+/* prototypes */
+
+int acl_init(bool dont_read_acl_tables);
+void acl_reload(void);
+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);
+bool acl_check_host(const char *host, const char *ip);
+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);
+int mysql_table_grant(THD *thd, TABLE_LIST *table, List <LEX_USER> &user_list,
+ List <LEX_COLUMN> &column_list, uint rights,
+ bool revoke);
+int grant_init(void);
+void grant_free(void);
+void grant_reload(void);
+bool check_grant(THD *thd, uint want_access, TABLE_LIST *tables,
+ uint show_command=0);
+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_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);
+int mysql_show_grants(THD *thd, LEX_USER *user);
diff --git a/sql/sql_analyse.cc b/sql/sql_analyse.cc
new file mode 100644
index 00000000000..4794a23976b
--- /dev/null
+++ b/sql/sql_analyse.cc
@@ -0,0 +1,981 @@
+/* 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 */
+
+
+/* Analyse database */
+
+/* TODO: - Check if any character fields can be of any date type
+** (date, datetime, year, time, timestamp, newdate)
+** - Check if any number field should be a timestamp
+** - type set is out of optimization yet
+*/
+
+#ifdef __GNUC__
+#pragma implementation // gcc: Class implementation
+#endif
+
+#include "mysql_priv.h"
+#include "procedure.h"
+#include "sql_analyse.h"
+#include <m_ctype.h>
+
+#define MAX_TREEMEM 8192
+#define MAX_TREE_ELEMENTS 256
+#define UINT_MAX16 0xffff
+#define UINT_MAX24 0xffffff
+#define UINT_MAX32 0xffffffff
+
+Procedure *
+proc_analyse_init(THD *thd, ORDER *param, select_result *result,
+ List<Item> &field_list)
+{
+ char *proc_name = (*param->item)->name;
+ analyse *pc = new analyse(result);
+ field_info **f_info;
+ DBUG_ENTER("proc_analyse_init");
+
+ if (!(param = param->next))
+ {
+ pc->max_tree_elements = MAX_TREE_ELEMENTS;
+ pc->max_treemem = MAX_TREEMEM;
+ }
+ else if (param->next)
+ {
+ // first parameter
+ if ((*param->item)->type() != Item::INT_ITEM ||
+ (*param->item)->val() < 0)
+ {
+ net_printf(&thd->net, ER_WRONG_PARAMETERS_TO_PROCEDURE, proc_name);
+ return 0;
+ }
+ pc->max_tree_elements = (uint) (*param->item)->val_int();
+ param = param->next;
+ if (param->next) // no third parameter possible
+ {
+ net_printf(&thd->net, ER_WRONG_PARAMCOUNT_TO_PROCEDURE, proc_name);
+ return 0;
+ }
+ // second parameter
+ if ((*param->item)->type() != Item::INT_ITEM ||
+ (*param->item)->val() < 0)
+ {
+ net_printf(&thd->net, ER_WRONG_PARAMETERS_TO_PROCEDURE, proc_name);
+ return 0;
+ }
+ pc->max_treemem = (uint) (*param->item)->val_int();
+ }
+ else if ((*param->item)->type() != Item::INT_ITEM ||
+ (*param->item)->val() < 0)
+ {
+ net_printf(&thd->net, ER_WRONG_PARAMETERS_TO_PROCEDURE, proc_name);
+ return 0;
+ }
+ // if only one parameter was given, it will be the value of max_tree_elements
+ else
+ {
+ pc->max_tree_elements = (uint) (*param->item)->val_int();
+ pc->max_treemem = MAX_TREEMEM;
+ }
+
+ if (!pc || !(pc->f_info = (field_info**)
+ sql_alloc(sizeof(field_info*)*field_list.elements)))
+ DBUG_RETURN(0);
+ pc->f_end = pc->f_info + field_list.elements;
+ pc->fields = field_list;
+
+ List_iterator<Item> it(pc->fields);
+ f_info = pc->f_info;
+
+ Item *item;
+ while ((item = it++))
+ {
+ if (item->result_type() == INT_RESULT)
+ {
+ // Check if fieldtype is ulonglong
+ if (item->type() == Item::FIELD_ITEM &&
+ ((Item_field*) item)->field->type() == FIELD_TYPE_LONGLONG &&
+ ((Field_longlong*) ((Item_field*) item)->field)->unsigned_flag)
+ *f_info++ = new field_ulonglong(item, pc);
+ else
+ *f_info++ = new field_longlong(item, pc);
+ }
+ if (item->result_type() == REAL_RESULT)
+ *f_info++ = new field_real(item, pc);
+ if (item->result_type() == STRING_RESULT)
+ *f_info++ = new field_str(item, pc);
+ }
+ return pc;
+} // proc_analyse_init
+
+
+// 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
+ for (; str != end && isspace(*str); str++) ;
+ if (str == end)
+ return 0;
+
+ if (*str == '-')
+ {
+ info->negative = 1;
+ if (++str == end || *str == '0') // converting -0 to a number
+ return 0; // might lose information
+ }
+ else
+ info->negative = 0;
+ begin = str;
+ for (; str != end && isdigit(*str); str++)
+ {
+ if (!info->integers && *str == '0' && (str + 1) != end &&
+ isdigit(*(str + 1)))
+ info->zerofill = 1; // could be a postnumber for example
+ info->integers++;
+ }
+ if (str == end && info->integers)
+ {
+ info->ullval = (ulonglong) strtoull(begin ,NULL, 10);
+ if (info->integers == 1)
+ return 0; // a single number can't be zerofill
+ info->maybe_zerofill = 1;
+ return 1; // a zerofill number, or an integer
+ }
+ if (*str == '.' || *str == 'e' || *str == 'E')
+ {
+ if (info->zerofill) // can't be zerofill anymore
+ return 0;
+ if ((str + 1) == end) // number was something like '123[.eE]'
+ {
+ info->ullval = (ulonglong) strtoull(begin, NULL, 10);
+ return 1;
+ }
+ if (*str == 'e' || *str == 'E') // number may be something like '1e+50'
+ {
+ str++;
+ if (*str != '-' && *str != '+')
+ return 0;
+ for (str++; str != end && isdigit(*str); str++) ;
+ if (str == end)
+ {
+ info->is_float = 1; // we can't use variable decimals here
+ return 1;
+ }
+ else
+ return 0;
+ }
+ for (str++; *(end - 1) == '0'; end--); // jump over zeros at the end
+ if (str == end) // number was something like '123.000'
+ {
+ info->ullval = (ulonglong) strtoull(begin, NULL, 10);
+ return 1;
+ }
+ for (; str != end && isdigit(*str); str++)
+ info->decimals++;
+ if (str == end)
+ {
+ info->dval = atod(begin);
+ return 1;
+ }
+ else
+ return 0;
+ }
+ else
+ return 0;
+} //test_if_number
+
+
+// Stores the biggest and the smallest value from current 'info'
+// to ev_num_info
+// If info contains an ulonglong number, which is bigger than
+// biggest positive number able to be stored in a longlong variable
+// and is marked as negative, function will return 0, else 1.
+
+bool get_ev_num_info(EV_NUM_INFO *ev_info, NUM_INFO *info, const char *num)
+{
+ if (info->negative)
+ {
+ if (((longlong) info->ullval) < 0)
+ return 0; // Impossible to store as a negative number
+ ev_info->llval = -(longlong) max((ulonglong) -ev_info->llval,
+ info->ullval);
+ ev_info->min_dval = (double) -max(-ev_info->min_dval, info->dval);
+ }
+ else // ulonglong is as big as bigint in MySQL
+ {
+ if ((check_ulonglong(num, info->integers) == REAL_NUM))
+ return 0;
+ ev_info->ullval = (ulonglong) max(ev_info->ullval, info->ullval);
+ ev_info->max_dval = (double) max(ev_info->max_dval, info->dval);
+ }
+ return 1;
+} // get_ev_num_info
+
+
+void free_string(String *s)
+{
+ s->free();
+}
+
+void field_str::add()
+{
+ char buff[MAX_FIELD_WIDTH], *ptr;
+ String s(buff, sizeof(buff)), *res;
+ ulong length;
+
+ if (!(res = item->val_str(&s)))
+ {
+ nulls++;
+ return;
+ }
+
+ if (!(length = res->length()))
+ empty++;
+ else
+ {
+ ptr = (char*) res->ptr();
+ if (*(ptr + (length - 1)) == ' ')
+ must_be_blob = 1;
+ }
+
+ if (can_be_still_num)
+ {
+ bzero((char*) &num_info, sizeof(num_info));
+ if (!test_if_number(&num_info, res->ptr(), (uint) length))
+ can_be_still_num = 0;
+ if (!found)
+ {
+ bzero((char*) &ev_num_info, sizeof(ev_num_info));
+ was_zero_fill = num_info.zerofill;
+ }
+ else if (num_info.zerofill != was_zero_fill && !was_maybe_zerofill)
+ can_be_still_num = 0; // one more check needed, when length is counted
+ if (can_be_still_num)
+ can_be_still_num = get_ev_num_info(&ev_num_info, &num_info, res->ptr());
+ 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;
+ min_arg.copy(*res);
+ max_arg.copy(*res);
+ min_length = max_length = length; sum=length;
+ }
+ else if (length)
+ {
+ sum += length;
+ if (length < min_length)
+ min_length = length;
+ if (length > max_length)
+ max_length = length;
+
+ if (item->binary)
+ {
+ if (stringcmp(res, &min_arg) < 0)
+ min_arg.copy(*res);
+ if (stringcmp(res, &max_arg) > 0)
+ max_arg.copy(*res);
+ }
+ else
+ {
+ if (sortcmp(res, &min_arg) < 0)
+ min_arg.copy(*res);
+ if (sortcmp(res, &max_arg) > 0)
+ max_arg.copy(*res);
+ }
+ }
+ 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
+} // field_str::add
+
+
+void field_real::add()
+{
+ char buff[MAX_FIELD_WIDTH], *ptr, *end;
+ double num = item->val();
+ uint length, zero_count, decs;
+ TREE_ELEMENT *element;
+
+ if (item->null_value)
+ {
+ nulls++;
+ return;
+ }
+ if (num == 0.0)
+ empty++;
+
+ if ((decs = decimals()) == NOT_FIXED_DEC)
+ {
+ sprintf(buff, "%g", num);
+ length = strlen(buff);
+ if (rint(num) != num)
+ max_notzero_dec_len = 1;
+ }
+ else
+ {
+ sprintf(buff, "%-.*f", (int) decs, num);
+ length = strlen(buff);
+
+ // We never need to check further than this
+ end = buff + length - 1 - decs + max_notzero_dec_len;
+
+ zero_count = 0;
+ for (ptr = buff + length - 1; ptr > end && *ptr == '0'; ptr--)
+ zero_count++;
+
+ if ((decs - zero_count > max_notzero_dec_len))
+ max_notzero_dec_len = decs - zero_count;
+ }
+
+ if (room_in_tree)
+ {
+ if (!(element = tree_insert(&tree, (void*) &num, 0)))
+ {
+ room_in_tree = 0; // Remove tree, out of RAM ?
+ delete_tree(&tree);
+ }
+ // if element->count == 1, this element can be found only once from tree
+ // if element->count == 2, or more, this element is already in tree
+ else if (element->count == 1 && (tree_elements++) > pc->max_tree_elements)
+ {
+ room_in_tree = 0; // Remove tree, too many elements
+ delete_tree(&tree);
+ }
+ }
+
+ if (!found)
+ {
+ found = 1;
+ min_arg = max_arg = sum = num;
+ sum_sqr = num * num;
+ min_length = max_length = length;
+ }
+ else if (num != 0.0)
+ {
+ sum += num;
+ sum_sqr += num * num;
+ if (length < min_length)
+ min_length = length;
+ if (length > max_length)
+ max_length = length;
+ if (compare_double(&num, &min_arg) < 0)
+ min_arg = num;
+ if (compare_double(&num, &max_arg) > 0)
+ max_arg = num;
+ }
+} // field_real::add
+
+void field_longlong::add()
+{
+ char buff[MAX_FIELD_WIDTH];
+ longlong num = item->val_int();
+ uint length = (uint) (longlong10_to_str(num, buff, -10) - buff);
+ TREE_ELEMENT *element;
+
+ if (item->null_value)
+ {
+ nulls++;
+ return;
+ }
+ if (num == 0)
+ empty++;
+
+ if (room_in_tree)
+ {
+ if (!(element = tree_insert(&tree, (void*) &num, 0)))
+ {
+ room_in_tree = 0; // Remove tree, out of RAM ?
+ delete_tree(&tree);
+ }
+ // if element->count == 1, this element can be found only once from tree
+ // if element->count == 2, or more, this element is already in tree
+ else if (element->count == 1 && (tree_elements++) > pc->max_tree_elements)
+ {
+ room_in_tree = 0; // Remove tree, too many elements
+ delete_tree(&tree);
+ }
+ }
+
+ if (!found)
+ {
+ found = 1;
+ min_arg = max_arg = sum = num;
+ sum_sqr = num * num;
+ min_length = max_length = length;
+ }
+ else if (num != 0)
+ {
+ sum += num;
+ sum_sqr += num * num;
+ if (length < min_length)
+ min_length = length;
+ if (length > max_length)
+ max_length = length;
+ if (compare_longlong(&num, &min_arg) < 0)
+ min_arg = num;
+ if (compare_longlong(&num, &max_arg) > 0)
+ max_arg = num;
+ }
+} // field_longlong::add
+
+
+void field_ulonglong::add()
+{
+ char buff[MAX_FIELD_WIDTH];
+ longlong num = item->val_int();
+ uint length = (uint) (longlong10_to_str(num, buff, 10) - buff);
+ TREE_ELEMENT *element;
+
+ if (item->null_value)
+ {
+ nulls++;
+ return;
+ }
+ if (num == 0)
+ empty++;
+
+ if (room_in_tree)
+ {
+ if (!(element = tree_insert(&tree, (void*) &num, 0)))
+ {
+ room_in_tree = 0; // Remove tree, out of RAM ?
+ delete_tree(&tree);
+ }
+ // if element->count == 1, this element can be found only once from tree
+ // if element->count == 2, or more, this element is already in tree
+ else if (element->count == 1 && (tree_elements++) > pc->max_tree_elements)
+ {
+ room_in_tree = 0; // Remove tree, too many elements
+ delete_tree(&tree);
+ }
+ }
+
+ if (!found)
+ {
+ found = 1;
+ min_arg = max_arg = sum = num;
+ sum_sqr = num * num;
+ min_length = max_length = length;
+ }
+ else if (num != 0)
+ {
+ sum += num;
+ sum_sqr += num * num;
+ if (length < min_length)
+ min_length = length;
+ if (length > max_length)
+ max_length = length;
+ if (compare_ulonglong((ulonglong*) &num, &min_arg) < 0)
+ min_arg = num;
+ if (compare_ulonglong((ulonglong*) &num, &max_arg) > 0)
+ max_arg = num;
+ }
+} // field_ulonglong::add
+
+
+int analyse::send_row(List<Item> &field_list __attribute__((unused)))
+{
+ field_info **f = f_info;
+
+ rows++;
+
+ for (;f != f_end; f++)
+ {
+ (*f)->add();
+ }
+ return 0;
+} // analyse::send_row
+
+
+bool analyse::end_of_records()
+{
+ field_info **f = f_info;
+ char buff[MAX_FIELD_WIDTH];
+ String *res, s_min(buff, sizeof(buff)), s_max(buff, sizeof(buff)),
+ ans(buff, sizeof(buff));
+
+ for (; f != f_end; f++)
+ {
+ func_items[0]->set((*f)->item->full_name());
+ if (!(*f)->found)
+ {
+ func_items[1]->null_value = 1;
+ func_items[2]->null_value = 1;
+ }
+ else
+ {
+ func_items[1]->null_value = 0;
+ res = (*f)->get_min_arg(&s_min);
+ func_items[1]->set(res->ptr(), res->length());
+ func_items[2]->null_value = 0;
+ res = (*f)->get_max_arg(&s_max);
+ func_items[2]->set(res->ptr(), res->length());
+ }
+ func_items[3]->set((longlong) (*f)->min_length);
+ func_items[4]->set((longlong) (*f)->max_length);
+ func_items[5]->set((longlong) (*f)->empty);
+ func_items[6]->set((longlong) (*f)->nulls);
+ res = (*f)->avg(&s_max, rows);
+ func_items[7]->set(res->ptr(), res->length());
+ func_items[8]->null_value = 0;
+ res = (*f)->std(&s_max, rows);
+ if (!res)
+ func_items[8]->null_value = 1;
+ else
+ func_items[8]->set(res->ptr(), res->length());
+ // count the dots, quotas, etc. in (ENUM("a","b","c"...))
+ // if tree has been removed, don't suggest ENUM.
+ // treemem is used to measure the size of tree for strings,
+ // tree_elements is used to count the elements in tree in case of numbers.
+ // max_treemem tells how long the string starting from ENUM("... and
+ // ending to ..") shall at maximum be. If case is about numbers,
+ // max_tree_elements will tell the length of the above, now
+ // every number is considered as length 1
+ if (((*f)->treemem || (*f)->tree_elements) &&
+ (*f)->tree.elements_in_tree &&
+ (((*f)->treemem ? max_treemem : max_tree_elements) >
+ (((*f)->treemem ? (*f)->treemem : (*f)->tree_elements) +
+ ((*f)->tree.elements_in_tree * 3 - 1 + 6))))
+ {
+ char tmp[331]; //331, because one double prec. num. can be this long
+ String tmp_str(tmp, sizeof(tmp));
+ TREE_INFO tree_info;
+
+ tree_info.str = &tmp_str;
+ tree_info.found = 0;
+ tree_info.item = (*f)->item;
+
+ tmp_str.set("ENUM(", 5);
+ tree_walk(&(*f)->tree, (*f)->collect_enum(), (char*) &tree_info,
+ left_root_right);
+ tmp_str.append(')');
+
+ if (!(*f)->nulls)
+ tmp_str.append(" NOT NULL");
+ output_str_length = tmp_str.length();
+ func_items[9]->set(tmp_str.ptr(), tmp_str.length());
+ if (result->send_data(result_fields))
+ return -1;
+ continue;
+ }
+
+ ans.length(0);
+ if (!(*f)->treemem && !(*f)->tree_elements)
+ ans.append("CHAR(0)", 7);
+ else if ((*f)->item->type() == Item::FIELD_ITEM)
+ {
+ switch (((Item_field*) (*f)->item)->field->real_type())
+ {
+ case FIELD_TYPE_TIMESTAMP:
+ ans.append("TIMESTAMP", 9);
+ break;
+ case FIELD_TYPE_DATETIME:
+ ans.append("DATETIME", 8);
+ break;
+ case FIELD_TYPE_DATE:
+ ans.append("DATE", 4);
+ break;
+ case FIELD_TYPE_SET:
+ ans.append("SET", 3);
+ break;
+ case FIELD_TYPE_YEAR:
+ ans.append("YEAR", 4);
+ break;
+ case FIELD_TYPE_TIME:
+ ans.append("TIME", 4);
+ break;
+ case FIELD_TYPE_NEWDATE:
+ ans.append("NEWDATE", 7);
+ break;
+ case FIELD_TYPE_DECIMAL:
+ ans.append("DECIMAL", 7);
+ // if item is FIELD_ITEM, it _must_be_ Field_num in this case
+ if (((Field_num*) (*f)->item)->zerofill)
+ ans.append(" ZEROFILL");
+ break;
+ default:
+ (*f)->get_opt_type(&ans, rows);
+ break;
+ }
+ }
+ if (!(*f)->nulls)
+ ans.append(" NOT NULL");
+ func_items[9]->set(ans.ptr(), ans.length());
+ if (result->send_data(result_fields))
+ return -1;
+ }
+ return 0;
+} // analyse::end_of_records
+
+
+void field_str::get_opt_type(String *answer, ha_rows total_rows)
+{
+ char buff[MAX_FIELD_WIDTH];
+
+ if (can_be_still_num)
+ {
+ if (num_info.is_float)
+ sprintf(buff, "DOUBLE"); // number was like 1e+50... TODO:
+ else if (num_info.decimals) // DOUBLE(%d,%d) sometime
+ {
+ if (num_info.dval > -FLT_MAX && num_info.dval < FLT_MAX)
+ sprintf(buff, "FLOAT(%d,%d)", num_info.integers, num_info.decimals);
+ else
+ sprintf(buff, "DOUBLE(%d,%d)", num_info.integers, num_info.decimals);
+ }
+ else if (ev_num_info.llval >= -128 &&
+ ev_num_info.ullval <=
+ (ulonglong) (ev_num_info.llval >= 0 ? 255 : 127))
+ sprintf(buff, "TINYINT(%d)", num_info.integers);
+ else if (ev_num_info.llval >= INT_MIN16 &&
+ ev_num_info.ullval <= (ulonglong) (ev_num_info.llval >= 0 ?
+ UINT_MAX16 : INT_MAX16))
+ sprintf(buff, "SMALLINT(%d)", num_info.integers);
+ else if (ev_num_info.llval >= INT_MIN24 &&
+ ev_num_info.ullval <= (ulonglong) (ev_num_info.llval >= 0 ?
+ UINT_MAX24 : INT_MAX24))
+ sprintf(buff, "MEDIUMINT(%d)", num_info.integers);
+ else if (ev_num_info.llval >= INT_MIN32 &&
+ ev_num_info.ullval <= (ulonglong) (ev_num_info.llval >= 0 ?
+ UINT_MAX32 : INT_MAX32))
+ sprintf(buff, "INT(%d)", num_info.integers);
+ else
+ sprintf(buff, "BIGINT(%d)", num_info.integers);
+ answer->append(buff, strlen(buff));
+ if (ev_num_info.llval >= 0 && ev_num_info.min_dval >= 0)
+ answer->append(" UNSIGNED");
+ if (num_info.zerofill)
+ answer->append(" ZEROFILL");
+ }
+ else if (max_length < 256)
+ {
+ if (must_be_blob)
+ {
+ if (item->binary)
+ answer->append("TINYBLOB", 8);
+ else
+ answer->append("TINYTEXT", 8);
+ }
+ else if ((max_length * (total_rows - nulls)) < (sum + total_rows))
+ {
+ sprintf(buff, "CHAR(%d)", (int) max_length);
+ answer->append(buff, strlen(buff));
+ }
+ else
+ {
+ sprintf(buff, "VARCHAR(%d)", (int) max_length);
+ answer->append(buff, strlen(buff));
+ }
+ }
+ else if (max_length < (1L << 16))
+ {
+ if (item->binary)
+ answer->append("BLOB", 4);
+ else
+ answer->append("TEXT", 4);
+ }
+ else if (max_length < (1L << 24))
+ {
+ if (item->binary)
+ answer->append("MEDIUMBLOB", 10);
+ else
+ answer->append("MEDIUMTEXT", 10);
+ }
+ else
+ {
+ if (item->binary)
+ answer->append("LONGBLOB", 8);
+ else
+ answer->append("LONGTEXT", 8);
+ }
+} // field_str::get_opt_type
+
+
+void field_real::get_opt_type(String *answer,
+ ha_rows total_rows __attribute__((unused)))
+{
+ char buff[MAX_FIELD_WIDTH];
+
+ if (!max_notzero_dec_len)
+ {
+ if (min_arg >= -128 && max_arg <= (min_arg >= 0 ? 255 : 127))
+ sprintf(buff, "TINYINT(%d)", (int) max_length - (item->decimals + 1));
+ else if (min_arg >= INT_MIN16 && max_arg <= (min_arg >= 0 ?
+ UINT_MAX16 : INT_MAX16))
+ sprintf(buff, "SMALLINT(%d)", (int) max_length - (item->decimals + 1));
+ else if (min_arg >= INT_MIN24 && max_arg <= (min_arg >= 0 ?
+ UINT_MAX24 : INT_MAX24))
+ sprintf(buff, "MEDIUMINT(%d)", (int) max_length - (item->decimals + 1));
+ else if (min_arg >= INT_MIN32 && max_arg <= (min_arg >= 0 ?
+ UINT_MAX32 : INT_MAX32))
+ sprintf(buff, "INT(%d)", (int) max_length - (item->decimals + 1));
+ else
+ sprintf(buff, "BIGINT(%d)", (int) max_length - (item->decimals + 1));
+ answer->append(buff, strlen(buff));
+ if (min_arg >= 0)
+ answer->append(" UNSIGNED");
+ }
+ else
+ {
+ if (min_arg >= -FLT_MAX && max_arg <= FLT_MAX)
+ sprintf(buff, "FLOAT(%d,%d)", (int) max_length - (item->decimals + 1),
+ max_notzero_dec_len);
+ else
+ sprintf(buff, "DOUBLE(%d,%d)", (int) max_length - (item->decimals + 1),
+ max_notzero_dec_len);
+ answer->append(buff, strlen(buff));
+ }
+ // if item is FIELD_ITEM, it _must_be_ Field_num in this class
+ if (item->type() == Item::FIELD_ITEM &&
+ // a single number shouldn't be zerofill
+ (max_length - (item->decimals + 1)) != 1 &&
+ ((Field_num*) ((Item_field*) item)->field)->zerofill)
+ answer->append(" ZEROFILL");
+} // field_real::get_opt_type
+
+
+void field_longlong::get_opt_type(String *answer,
+ ha_rows total_rows __attribute__((unused)))
+{
+ char buff[MAX_FIELD_WIDTH];
+
+ if (min_arg >= -128 && max_arg <= (min_arg >= 0 ? 255 : 127))
+ sprintf(buff, "TINYINT(%d)", (int) max_length);
+ else if (min_arg >= INT_MIN16 && max_arg <= (min_arg >= 0 ?
+ UINT_MAX16 : INT_MAX16))
+ sprintf(buff, "SMALLINT(%d)", (int) max_length);
+ else if (min_arg >= INT_MIN24 && max_arg <= (min_arg >= 0 ?
+ UINT_MAX24 : INT_MAX24))
+ sprintf(buff, "MEDIUMINT(%d)", (int) max_length);
+ else if (min_arg >= INT_MIN32 && max_arg <= (min_arg >= 0 ?
+ UINT_MAX32 : INT_MAX32))
+ sprintf(buff, "INT(%d)", (int) max_length);
+ else
+ sprintf(buff, "BIGINT(%d)", (int) max_length);
+ answer->append(buff, strlen(buff));
+ if (min_arg >= 0)
+ answer->append(" UNSIGNED");
+
+ // if item is FIELD_ITEM, it _must_be_ Field_num in this class
+ if ((item->type() == Item::FIELD_ITEM) &&
+ // a single number shouldn't be zerofill
+ max_length != 1 &&
+ ((Field_num*) ((Item_field*) item)->field)->zerofill)
+ answer->append(" ZEROFILL");
+} // field_longlong::get_opt_type
+
+
+void field_ulonglong::get_opt_type(String *answer,
+ ha_rows total_rows __attribute__((unused)))
+{
+ char buff[MAX_FIELD_WIDTH];
+
+ if (max_arg < 256)
+ sprintf(buff, "TINYINT(%d) UNSIGNED", (int) max_length);
+ else if (max_arg <= ((2 * INT_MAX16) + 1))
+ sprintf(buff, "SMALLINT(%d) UNSIGNED", (int) max_length);
+ else if (max_arg <= ((2 * INT_MAX24) + 1))
+ sprintf(buff, "MEDIUMINT(%d) UNSIGNED", (int) max_length);
+ else if (max_arg < (((ulonglong) 1) << 32))
+ sprintf(buff, "INT(%d) UNSIGNED", (int) max_length);
+ else
+ sprintf(buff, "BIGINT(%d) UNSIGNED", (int) max_length);
+ // if item is FIELD_ITEM, it _must_be_ Field_num in this class
+ answer->append(buff, strlen(buff));
+ if (item->type() == Item::FIELD_ITEM &&
+ // a single number shouldn't be zerofill
+ max_length != 1 &&
+ ((Field_num*) ((Item_field*) item)->field)->zerofill)
+ answer->append(" ZEROFILL");
+} //field_ulonglong::get_opt_type
+
+
+int collect_string(String *element,
+ element_count count __attribute__((unused)),
+ TREE_INFO *info)
+{
+ if (info->found)
+ info->str->append(',');
+ else
+ info->found = 1;
+ info->str->append('\'');
+ info->str->append(*element);
+ info->str->append('\'');
+ return 0;
+} // collect_string
+
+
+int collect_real(double *element, element_count count __attribute__((unused)),
+ TREE_INFO *info)
+{
+ char buff[255];
+ String s(buff, sizeof(buff));
+
+ if (info->found)
+ info->str->append(',');
+ else
+ info->found = 1;
+ info->str->append('\'');
+ s.set(*element, info->item->decimals);
+ info->str->append(s);
+ info->str->append('\'');
+ return 0;
+} // collect_real
+
+
+int collect_longlong(longlong *element,
+ element_count count __attribute__((unused)),
+ TREE_INFO *info)
+{
+ char buff[255];
+ String s(buff, sizeof(buff));
+
+ if (info->found)
+ info->str->append(',');
+ else
+ info->found = 1;
+ info->str->append('\'');
+ s.set(*element);
+ info->str->append(s);
+ info->str->append('\'');
+ return 0;
+} // collect_longlong
+
+
+int collect_ulonglong(ulonglong *element,
+ element_count count __attribute__((unused)),
+ TREE_INFO *info)
+{
+ char buff[255];
+ String s(buff, sizeof(buff));
+
+ if (info->found)
+ info->str->append(',');
+ else
+ info->found = 1;
+ info->str->append('\'');
+ s.set(*element);
+ info->str->append(s);
+ info->str->append('\'');
+ return 0;
+} // collect_ulonglong
+
+
+bool analyse::change_columns(List<Item> &field_list)
+{
+ field_list.empty();
+
+ func_items[0] = new Item_proc_string("Field_name", 255);
+ func_items[1] = new Item_proc_string("Min_value", 255);
+ func_items[1]->maybe_null = 1;
+ func_items[2] = new Item_proc_string("Max_value", 255);
+ func_items[2]->maybe_null = 1;
+ func_items[3] = new Item_proc_int("Min_length");
+ func_items[4] = new Item_proc_int("Max_length");
+ func_items[5] = new Item_proc_int("Empties_or_zeros");
+ func_items[6] = new Item_proc_int("Nulls");
+ func_items[7] = new Item_proc_string("Avg_value_or_avg_length", 255);
+ func_items[8] = new Item_proc_string("Std", 255);
+ func_items[8]->maybe_null = 1;
+ func_items[9] = new Item_proc_string("Optimal_fieldtype",
+ max(64, output_str_length));
+
+ for (uint i = 0; i < array_elements(func_items); i++)
+ field_list.push_back(func_items[i]);
+ result_fields = field_list;
+ return 0;
+} // analyse::change_columns
+
+int compare_double(const double *s, const double *t)
+{
+ return ((*s < *t) ? -1 : *s > *t ? 1 : 0);
+} /* compare_double */
+
+int compare_longlong(const longlong *s, const longlong *t)
+{
+ return ((*s < *t) ? -1 : *s > *t ? 1 : 0);
+} /* compare_longlong */
+
+ int compare_ulonglong(const ulonglong *s, const ulonglong *t)
+{
+ return ((*s < *t) ? -1 : *s > *t ? 1 : 0);
+} /* compare_ulonglong */
+
+
+uint check_ulonglong(const char *str, uint length)
+{
+ const char *long_str = "2147483647", *ulonglong_str = "18446744073709551615";
+ const uint long_len = 10, ulonglong_len = 20;
+
+ while (*str == '0' && length)
+ {
+ str++; length--;
+ }
+ if (length < long_len)
+ return NUM;
+
+ uint smaller, bigger;
+ const char *cmp;
+
+ if (length == long_len)
+ {
+ cmp = long_str;
+ smaller = NUM;
+ bigger = LONG_NUM;
+ }
+ else if (length > ulonglong_len)
+ return REAL_NUM;
+ else
+ {
+ cmp = ulonglong_str;
+ smaller = LONG_NUM;
+ bigger = REAL_NUM;
+ }
+ while (*cmp && *cmp++ == *str++) ;
+ return ((uchar) str[-1] <= (uchar) cmp[-1]) ? smaller : bigger;
+} /* check_ulonlong */
diff --git a/sql/sql_analyse.h b/sql/sql_analyse.h
new file mode 100644
index 00000000000..f9cedcde2f6
--- /dev/null
+++ b/sql/sql_analyse.h
@@ -0,0 +1,292 @@
+/* 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 */
+
+
+/* Analyse database */
+
+#ifdef __GNUC__
+#pragma interface /* gcc class implementation */
+#endif
+
+#include <my_tree.h>
+
+#define DEC_IN_AVG 4
+
+typedef struct st_number_info
+{
+ // if zerofill is true, the number must be zerofill, or string
+ bool negative, is_float, zerofill, maybe_zerofill;
+ int8 integers;
+ int8 decimals;
+ double dval;
+ ulonglong ullval;
+} NUM_INFO;
+
+typedef struct st_extreme_value_number_info
+{
+ ulonglong ullval;
+ longlong llval;
+ double max_dval, min_dval;
+} EV_NUM_INFO;
+
+typedef struct st_tree_info
+{
+ bool found;
+ String *str;
+ Item *item;
+} TREE_INFO;
+
+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 strlen);
+int compare_double(const double *s, const double *t);
+int compare_longlong(const longlong *s, const longlong *t);
+int compare_ulonglong(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*);
+class analyse;
+
+class field_info :public Sql_alloc
+{
+protected:
+ ulong treemem, tree_elements, empty, nulls, min_length, max_length;
+ uint room_in_tree;
+ my_bool found;
+ TREE tree;
+ Item *item;
+ analyse *pc;
+
+public:
+ field_info(Item* a, analyse* b) : treemem(0), tree_elements(0), empty(0),
+ nulls(0), min_length(0), max_length(0), room_in_tree(1),
+ found(0),item(a), pc(b) {};
+
+ virtual ~field_info() { delete_tree(&tree); }
+ virtual void add() = 0;
+ virtual void get_opt_type(String*, ha_rows) = 0;
+ virtual String *get_min_arg(String *) = 0;
+ virtual String *get_max_arg(String *) = 0;
+ virtual String *avg(String*, ha_rows) = 0;
+ virtual String *std(String*, ha_rows) = 0;
+ virtual tree_walk_action collect_enum() = 0;
+ virtual uint decimals() { return 0; }
+ friend class analyse;
+};
+
+
+class field_str :public field_info
+{
+ String min_arg, max_arg;
+ ulonglong sum;
+ bool must_be_blob, was_zero_fill, was_maybe_zerofill,
+ can_be_still_num;
+ NUM_INFO num_info;
+ EV_NUM_INFO ev_num_info;
+
+public:
+ field_str(Item* a, analyse* b) :field_info(a,b), min_arg(""),
+ 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); };
+
+ void add();
+ void get_opt_type(String*, ha_rows);
+ String *get_min_arg(String *not_used __attribute__((unused)))
+ { return &min_arg; }
+ String *get_max_arg(String *not_used __attribute__((unused)))
+ { return &max_arg; }
+ String *avg(String *s, ha_rows rows)
+ {
+ if (!(rows - nulls))
+ s->set((double) 0.0, 1);
+ else
+ s->set((ulonglong2double(sum) / ulonglong2double(rows - nulls)),
+ DEC_IN_AVG);
+ return s;
+ }
+ friend int collect_string(String *element, element_count count,
+ TREE_INFO *info);
+ tree_walk_action collect_enum()
+ { return (tree_walk_action) collect_string; }
+ String *std(String *s __attribute__((unused)),
+ ha_rows rows __attribute__((unused)))
+ { return (String*) 0; }
+};
+
+class field_real: public field_info
+{
+ double min_arg, max_arg;
+ double sum, sum_sqr;
+ uint max_notzero_dec_len;
+
+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); }
+
+ void add();
+ void get_opt_type(String*, ha_rows);
+ String *get_min_arg(String *s) { s->set(min_arg, item->decimals); return s; }
+ String *get_max_arg(String *s) { s->set(max_arg, item->decimals); return s; }
+ String *avg(String *s, ha_rows rows)
+ {
+ if (!(rows - nulls))
+ s->set((double) 0.0, 1);
+ else
+ s->set(((double)sum / (double) (rows - nulls)), item->decimals);
+ return s;
+ }
+ String *std(String *s, ha_rows rows)
+ {
+ double tmp = ulonglong2double(rows);
+ if (!(tmp - nulls))
+ s->set((double) 0.0, 1);
+ else
+ {
+ double tmp2 = ((sum_sqr - sum * sum / (tmp - nulls)) /
+ (tmp - nulls));
+ s->set(((double) tmp2 <= 0.0 ? 0.0 : sqrt(tmp2)), item->decimals);
+ }
+ return s;
+ }
+ uint decimals() { return item->decimals; }
+ friend int collect_real(double *element, element_count count,
+ TREE_INFO *info);
+ tree_walk_action collect_enum()
+ { return (tree_walk_action) collect_real;}
+};
+
+class field_longlong: public field_info
+{
+ longlong min_arg, max_arg;
+ longlong sum, sum_sqr;
+
+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); }
+
+ void add();
+ void get_opt_type(String*, ha_rows);
+ String *get_min_arg(String *s) { s->set(min_arg); return s; }
+ String *get_max_arg(String *s) { s->set(max_arg); return s; }
+ String *avg(String *s, ha_rows rows)
+ {
+ if (!(rows - nulls))
+ s->set((double) 0.0, 1);
+ else
+ s->set(((double) sum / (double) (rows - nulls)), DEC_IN_AVG);
+ return s;
+ }
+ String *std(String *s, ha_rows rows)
+ {
+ double tmp = ulonglong2double(rows);
+ if (!(tmp - nulls))
+ s->set((double) 0.0, 1);
+ else
+ {
+ double tmp2 = ((sum_sqr - sum * sum / (tmp - nulls)) /
+ (tmp - nulls));
+ s->set(((double) tmp2 <= 0.0 ? 0.0 : sqrt(tmp2)), DEC_IN_AVG);
+ }
+ return s;
+ }
+ friend int collect_longlong(longlong *element, element_count count,
+ TREE_INFO *info);
+ tree_walk_action collect_enum()
+ { return (tree_walk_action) collect_longlong;}
+};
+
+
+class field_ulonglong: public field_info
+{
+ ulonglong min_arg, max_arg;
+ ulonglong sum, sum_sqr;
+
+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); }
+ void add();
+ void get_opt_type(String*, ha_rows);
+ String *get_min_arg(String *s) { s->set(min_arg); return s; }
+ String *get_max_arg(String *s) { s->set(max_arg); return s; }
+ String *avg(String *s, ha_rows rows)
+ {
+ if (!(rows - nulls))
+ s->set((double) 0.0, 1);
+ else
+ s->set((ulonglong2double(sum) / ulonglong2double(rows - nulls)),
+ DEC_IN_AVG);
+ return s;
+ }
+ String *std(String *s, ha_rows rows)
+ {
+ double tmp = ulonglong2double(rows);
+ if (!(tmp - nulls))
+ s->set((double) 0.0, 1);
+ else
+ {
+ double tmp2 = ((ulonglong2double(sum_sqr) -
+ ulonglong2double(sum * sum) / (tmp - nulls)) /
+ (tmp - nulls));
+ s->set(((double) tmp2 <= 0.0 ? 0.0 : sqrt(tmp2)), DEC_IN_AVG);
+ }
+ return s;
+ }
+ friend int collect_ulonglong(ulonglong *element, element_count count,
+ TREE_INFO *info);
+ tree_walk_action collect_enum()
+ { return (tree_walk_action) collect_ulonglong; }
+};
+
+
+class analyse: public Procedure
+{
+protected:
+ Item_proc *func_items[10];
+ List<Item> fields, result_fields;
+ field_info **f_info, **f_end;
+ ha_rows rows;
+ uint output_str_length;
+
+public:
+ uint max_tree_elements, max_treemem;
+
+ analyse(select_result *res) :Procedure(res, PROC_NO_SORT), rows(0),
+ output_str_length(0) {}
+
+ ~analyse()
+ {
+ for (field_info **f=f_info; f != f_end; f++)
+ delete (*f);
+ }
+ virtual void add() {}
+ virtual bool change_columns(List<Item> &fields);
+ virtual int send_row(List<Item> &fields);
+ virtual void end_group(void) {}
+ virtual bool end_of_records(void);
+ friend Procedure *proc_analyse_init(THD *thd, ORDER *param,
+ select_result *result,
+ List<Item> &field_list);
+};
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
new file mode 100644
index 00000000000..4b245b29e83
--- /dev/null
+++ b/sql/sql_base.cc
@@ -0,0 +1,2174 @@
+/* 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 */
+
+
+/* Basic functions neaded 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>
+#include <nisam.h>
+#include <assert.h>
+#ifdef __WIN__
+#include <io.h>
+#endif
+
+#define MAX_DBKEY_LENGTH (FN_LEN*2+2)
+
+static int key_cache_used=0;
+TABLE *unused_tables; /* Used by mysql_test */
+HASH open_cache; /* Used by mysql_test */
+
+
+static int open_unireg_entry(TABLE *entry,const char *db,const char *name,
+ const char *alias);
+static bool insert_fields(THD *thd,TABLE_LIST *tables, 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(THD *thd, TABLE *table,
+ List<String> *index_list);
+
+static int send_file(THD *thd)
+{
+ NET* net = &thd->net;
+ int fd = -1,bytes, error = 1;
+ uint packet_len;
+ char fname[FN_REFLEN+1];
+ char buf[IO_SIZE*15];
+ const char *errmsg = 0;
+ int old_timeout;
+ 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;
+
+ // 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";
+ goto err;
+ }
+
+ fn_format(fname, (char*)net->read_pos + 1, "", "", 4);
+ if(!strcmp(fname,"/dev/null")) goto end; // this is needed to make replicate-ignore-db
+ // work on the well-known system that does not have a /dev/null :-)
+
+ if ((fd = my_open(fname, O_RDONLY, MYF(MY_WME))) < 0)
+ {
+ errmsg = "Failed on my_open()";
+ goto err;
+ }
+
+ while ((bytes = (int) my_read(fd, (byte*) buf, sizeof(buf),
+ MYF(MY_WME))) > 0)
+ {
+ if (my_net_write(net, buf, bytes))
+ {
+ errmsg = "Failed on my_net_write()";
+ goto err;
+ }
+ }
+
+ end:
+ if (my_net_write(net, "", 0) || net_flush(net) ||
+ (my_net_read(net) == packet_error))
+ {
+ errmsg = "failed negotiating file transfer close";
+ goto err;
+ }
+ error = 0;
+
+ err:
+ thd->net.timeout = old_timeout;
+ if(fd >= 0)
+ (void) my_close(fd, MYF(MY_WME));
+ if (errmsg)
+ {
+ sql_print_error("failed in send_file() : %s", errmsg);
+ DBUG_PRINT("error", (errmsg));
+ }
+ DBUG_RETURN(error);
+}
+
+static byte *cache_key(const byte *record,uint *length,
+ my_bool not_used __attribute__((unused)))
+{
+ TABLE *entry=(TABLE*) record;
+ *length=entry->key_length;
+ return (byte*) entry->table_cache_key;
+}
+
+void table_cache_init(void)
+{
+ VOID(hash_init(&open_cache,table_cache_size,0,0,cache_key,
+ (void (*)(void*)) free_cache_entry,0));
+ mysql_rm_tmp_tables();
+}
+
+
+void table_cache_free(void)
+{
+ DBUG_ENTER("table_cache_free");
+ close_cached_tables(0);
+ if (!open_cache.records) // Safety first
+ hash_free(&open_cache);
+ DBUG_VOID_RETURN;
+}
+
+
+uint cached_tables(void)
+{
+ return open_cache.records;
+}
+
+#ifdef EXTRA_DEBUG
+static void check_unused(void)
+{
+ uint count=0,idx=0;
+ TABLE *cur_link,*start_link;
+
+ if ((start_link=cur_link=unused_tables))
+ {
+ do
+ {
+ if (cur_link != cur_link->next->prev || cur_link != cur_link->prev->next)
+ {
+ DBUG_PRINT("error",("Unused_links aren't linked properly")); /* purecov: inspected */
+ return; /* purecov: inspected */
+ }
+ } while (count++ < open_cache.records &&
+ (cur_link=cur_link->next) != start_link);
+ if (cur_link != start_link)
+ {
+ DBUG_PRINT("error",("Unused_links aren't connected")); /* purecov: inspected */
+ }
+ }
+ for (idx=0 ; idx < open_cache.records ; idx++)
+ {
+ TABLE *entry=(TABLE*) hash_element(&open_cache,idx);
+ if (!entry->in_use)
+ count--;
+ }
+ if (count != 0)
+ {
+ DBUG_PRINT("error",("Unused_links dosen't match open_cache: diff: %d", /* purecov: inspected */
+ count)); /* purecov: inspected */
+ }
+}
+#else
+#define check_unused()
+#endif
+
+void mysql_binlog_send(THD* thd, char* log_ident, ulong pos, ushort flags)
+{
+ LOG_INFO linfo;
+ char *log_file_name = linfo.log_file_name;
+ char search_file_name[FN_REFLEN];
+ FILE* log = NULL;
+ String* packet = &thd->packet;
+ int error;
+ const char *errmsg = "Unknown error";
+ NET* net = &thd->net;
+
+ DBUG_ENTER("mysql_binlog_send");
+
+ if(!mysql_bin_log.is_open())
+ {
+ errmsg = "Binary log is not open";
+ goto err;
+ }
+
+ if(log_ident[0])
+ mysql_bin_log.make_log_name(search_file_name, log_ident);
+ else
+ search_file_name[0] = 0;
+
+ if(mysql_bin_log.find_first_log(&linfo, search_file_name))
+ {
+ errmsg = "Could not find first log";
+ goto err;
+ }
+ log = my_fopen(log_file_name, O_RDONLY, MYF(MY_WME));
+
+ if(!log)
+ {
+ errmsg = "Could not open log file";
+ goto err;
+ }
+
+ if(my_fseek(log, pos, MY_SEEK_SET, MYF(MY_WME)) == MY_FILEPOS_ERROR )
+ {
+ errmsg = "Error on fseek()";
+ goto err;
+ }
+
+
+ packet->length(0);
+ packet->append("\0", 1); // we need to start a packet with something other than 255
+ // to distiquish it from error
+
+ while(!net->error && net->vio != 0 && !thd->killed)
+ {
+ while(!(error = Log_event::read_log_event(log, packet)))
+ {
+ if(my_net_write(net, (char*)packet->ptr(), packet->length()) )
+ {
+ errmsg = "Failed on my_net_write()";
+ 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))
+ {
+ errmsg = "failed in send_file()";
+ goto err;
+ }
+ }
+ packet->length(0);
+ packet->append("\0",1);
+ }
+ if(error != LOG_READ_EOF)
+ {
+ errmsg = "error reading log event";
+ goto err;
+ }
+
+ 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))
+ {
+ errmsg = "failed on net_flush()";
+ 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
+ {
+ pthread_mutex_t *log_lock = mysql_bin_log.get_log_lock();
+ clearerr(log);
+
+ // tell the kill thread how to wake us up
+ pthread_mutex_lock(&thd->mysys_var->mutex);
+ 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 = "Waiting for update";
+ pthread_mutex_unlock(&thd->mysys_var->mutex);
+
+ bool read_packet = 0, fatal_error = 0;
+
+ pthread_mutex_lock(log_lock); // no one will update the log while we are reading
+ // now, but we'll be quick and just read one record
+
+
+ switch(Log_event::read_log_event(log, packet))
+ {
+ case 0:
+ read_packet = 1; // we read successfully, so we'll need to send it to the
+ // slave
+ break;
+ case LOG_READ_EOF:
+ pthread_cond_wait(&COND_binlog_update, log_lock);
+ break;
+
+ default:
+ 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(my_net_write(net, (char*)packet->ptr(), packet->length()) )
+ {
+ errmsg = "Failed on my_net_write()";
+ goto err;
+ }
+
+ if((*packet)[LOG_EVENT_OFFSET+1] == LOAD_EVENT)
+ {
+ if(send_file(thd))
+ {
+ errmsg = "failed in send_file()";
+ 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
+ }
+
+ if(fatal_error)
+ {
+ errmsg = "error reading log entry";
+ goto err;
+ }
+
+ clearerr(log);
+ }
+ }
+ else
+ {
+ bool loop_breaker = 0; // need this to break out of the for loop from switch
+
+ switch(mysql_bin_log.find_next_log(&linfo))
+ {
+ case LOG_INFO_EOF:
+ loop_breaker = (flags & BINLOG_DUMP_NON_BLOCK);
+ break;
+ case 0:
+ break;
+ default:
+ errmsg = "could not find next log";
+ goto err;
+ }
+
+ if(loop_breaker)
+ break;
+
+ (void) my_fclose(log, MYF(MY_WME));
+ log = my_fopen(log_file_name, O_RDONLY, MYF(MY_WME));
+ if(!log)
+ goto err;
+ // fake Rotate_log event just in case it did not make it to the log
+ // otherwise the slave make get confused about the offset
+ {
+ char header[9];
+ memset(header, 0, 4); // when does not matter
+ header[4] = ROTATE_EVENT;
+ char* p = strrchr(log_file_name, FN_LIBCHAR); // find the last slash
+ if(p)
+ p++;
+ else
+ p = log_file_name;
+
+ uint ident_len = strlen(p);
+ ulong event_len = ident_len + sizeof(header);
+ int4store(header + 5, event_len);
+ packet->append(header, sizeof(header));
+ packet->append(p,ident_len);
+ if(my_net_write(net, (char*)packet->ptr(), packet->length()))
+ {
+ errmsg = "failed on my_net_write()";
+ goto err;
+ }
+ packet->length(0);
+ packet->append("\0",1);
+ }
+ }
+ }
+
+ (void)my_fclose(log, MYF(MY_WME));
+
+ send_eof(&thd->net);
+ DBUG_VOID_RETURN;
+ err:
+ if(log)
+ (void) my_fclose(log, MYF(MY_WME));
+ send_error(&thd->net, 0, errmsg);
+ DBUG_VOID_RETURN;
+}
+
+
+/******************************************************************************
+** Send name and type of result to client.
+** Sum fields has table name empty and field_name.
+******************************************************************************/
+
+bool
+send_fields(THD *thd,List<Item> &list,uint flag)
+{
+ List_iterator<Item> it(list);
+ Item *item;
+ char buff[80];
+ CONVERT *convert=thd->convert_set;
+
+ String tmp((char*) buff,sizeof(buff)),*res,*packet= &thd->packet;
+
+ if (thd->fatal_error) // We have got an error
+ goto err;
+
+ if (flag & 1)
+ { // Packet with number of elements
+ char *pos=net_store_length(buff,(uint) list.elements);
+ (void) my_net_write(&thd->net, buff,(uint) (pos-buff));
+ }
+ while ((item=it++))
+ {
+ char *pos;
+ Send_field field;
+ item->make_field(&field);
+ packet->length(0);
+
+ if (convert)
+ {
+ if (convert->store(packet,field.table_name,strlen(field.table_name)) ||
+ convert->store(packet,field.col_name, strlen(field.col_name)) ||
+ packet->realloc(packet->length()+10))
+ goto err;
+ }
+ else if (net_store_data(packet,field.table_name) ||
+ net_store_data(packet,field.col_name) ||
+ packet->realloc(packet->length()+10))
+ goto err; /* purecov: inspected */
+ pos= (char*) packet->ptr()+packet->length();
+
+ if (!(thd->client_capabilities & CLIENT_LONG_FLAG))
+ {
+ packet->length(packet->length()+9);
+ pos[0]=3; int3store(pos+1,field.length);
+ pos[4]=1; pos[5]=field.type;
+ pos[6]=2; pos[7]=(char) field.flags; pos[8]= (char) field.decimals;
+ }
+ else
+ {
+ packet->length(packet->length()+10);
+ pos[0]=3; int3store(pos+1,field.length);
+ pos[4]=1; pos[5]=field.type;
+ pos[6]=3; int2store(pos+7,field.flags); pos[9]= (char) field.decimals;
+ }
+ if (flag & 2)
+ { // Send default value
+ if (!(res=item->val_str(&tmp)))
+ {
+ if (net_store_null(packet))
+ goto err;
+ }
+ else if (net_store_data(packet,res->ptr(),res->length()))
+ goto err;
+ }
+ 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;
+ err:
+ send_error(&thd->net,ER_OUT_OF_RESOURCES); /* purecov: inspected */
+ return 1; /* purecov: inspected */
+}
+
+
+/*****************************************************************************
+ * Functions to free open table cache
+ ****************************************************************************/
+
+
+void intern_close_table(TABLE *table)
+{ // Free all structures
+ free_io_cache(table);
+ if (table->file)
+ VOID(closefrm(table)); // close file
+}
+
+
+static void free_cache_entry(TABLE *table)
+{
+ DBUG_ENTER("free_cache_entry");
+
+ intern_close_table(table);
+ if (!table->in_use)
+ {
+ table->next->prev=table->prev; /* remove from used chain */
+ table->prev->next=table->next;
+ if (table == unused_tables)
+ {
+ unused_tables=unused_tables->next;
+ if (table == unused_tables)
+ unused_tables=0;
+ }
+ check_unused(); // consisty check
+ }
+ my_free((gptr) table,MYF(0));
+ DBUG_VOID_RETURN;
+}
+
+
+void free_io_cache(TABLE *table)
+{
+ if (table->io_cache)
+ {
+ close_cached_file(table->io_cache);
+ my_free((gptr) table->io_cache,MYF(0));
+ table->io_cache=0;
+ }
+ if (table->record_pointers)
+ {
+ my_free((gptr) table->record_pointers,MYF(0));
+ table->record_pointers=0;
+ }
+}
+
+ /* Close all tables which aren't in use by any thread */
+
+bool close_cached_tables(bool if_wait_for_refresh)
+{
+ bool result=0;
+ DBUG_ENTER("close_cached_tables");
+
+ VOID(pthread_mutex_lock(&LOCK_open));
+ while (unused_tables)
+ {
+#ifdef EXTRA_DEBUG
+ if (hash_delete(&open_cache,(byte*) unused_tables))
+ printf("Warning: Couldn't delete open table from hash\n");
+#else
+ VOID(hash_delete(&open_cache,(byte*) unused_tables));
+#endif
+ }
+ if (!open_cache.records)
+ {
+ end_key_cache(); /* No tables in memory */
+ key_cache_used=0;
+ }
+ refresh_version++; // Force close of open tables
+ 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
+ */
+ kill_delayed_threads();
+ THD *thd=current_thd;
+ pthread_mutex_lock(&thd->mysys_var->mutex);
+ thd->mysys_var->current_mutex= &LOCK_open;
+ thd->mysys_var->current_cond= &COND_refresh;
+ thd->proc_info="Flushing tables";
+ pthread_mutex_unlock(&thd->mysys_var->mutex);
+ VOID(pthread_cond_broadcast(&COND_refresh)); // If one flush is locked
+
+ close_old_data_files(thd,thd->open_tables,1);
+ bool found=1;
+ /* Wait until all threads has closed all the tables we had locked */
+ DBUG_PRINT("info", ("Waiting for others threads to close their open tables"));
+ while (found && ! thd->killed)
+ {
+ found=0;
+ for (uint idx=0 ; idx < open_cache.records ; idx++)
+ {
+ TABLE *table=(TABLE*) hash_element(&open_cache,idx);
+ if ((table->version) < refresh_version && table->db_stat)
+ {
+ found=1;
+ pthread_cond_wait(&COND_refresh,&LOCK_open);
+ break;
+ }
+ }
+ }
+ /*
+ No other thread has the locked tables open; reopen them and get the
+ old locks. This should always succeed (unless some external process
+ has removed the tables)
+ */
+ thd->in_lock_tables=1;
+ result=reopen_tables(thd,1,1);
+ thd->in_lock_tables=0;
+ }
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ if (if_wait_for_refresh)
+ {
+ THD *thd=current_thd;
+ 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);
+ }
+ DBUG_RETURN(result);
+}
+
+
+/* Put all tables used by thread in free list */
+
+void close_thread_tables(THD *thd, bool locked)
+{
+ DBUG_ENTER("close_thread_tables");
+
+ if (thd->locked_tables)
+ DBUG_VOID_RETURN; // LOCK TABLES in use
+
+ TABLE *table,*next;
+ bool found_old_table=0;
+
+ if (thd->lock)
+ {
+ mysql_unlock_tables(thd, thd->lock); thd->lock=0;
+ }
+ /* VOID(pthread_sigmask(SIG_SETMASK,&thd->block_signals,NULL)); */
+ if (!locked)
+ VOID(pthread_mutex_lock(&LOCK_open));
+
+ DBUG_PRINT("info", ("thd->open_tables=%p", thd->open_tables));
+
+ for (table=thd->open_tables ; table ; table=next)
+ {
+ next=table->next;
+ if (table->version != refresh_version ||
+ thd->version != refresh_version || !table->db_stat)
+ {
+ VOID(hash_delete(&open_cache,(byte*) table));
+ found_old_table=1;
+ }
+ else
+ {
+ if (table->flush_version != flush_version)
+ {
+ table->flush_version=flush_version;
+ table->file->extra(HA_EXTRA_FLUSH);
+ }
+ table->in_use=0;
+ if (unused_tables)
+ {
+ table->next=unused_tables; /* Link in last */
+ table->prev=unused_tables->prev;
+ unused_tables->prev=table;
+ table->prev->next=table;
+ }
+ else
+ unused_tables=table->next=table->prev=table;
+ }
+ }
+ thd->open_tables=0;
+ /* Free tables to hold down open files */
+ while (open_cache.records >= table_cache_size && unused_tables)
+ VOID(hash_delete(&open_cache,(byte*) unused_tables)); /* purecov: tested */
+ check_unused();
+ if (found_old_table)
+ {
+ /* Tell threads waiting for refresh that something has happened */
+ VOID(pthread_cond_broadcast(&COND_refresh));
+ }
+ if (!locked)
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ /* VOID(pthread_sigmask(SIG_SETMASK,&thd->signals,NULL)); */
+ DBUG_VOID_RETURN;
+}
+
+ /* Close and delete temporary tables */
+
+void close_temporary(TABLE *table,bool delete_table)
+{
+ DBUG_ENTER("close_temporary");
+ char path[FN_REFLEN];
+ db_type table_type=table->db_type;
+ strmov(path,table->path);
+ free_io_cache(table);
+ closefrm(table);
+ my_free((char*) table,MYF(0));
+ if (delete_table)
+ rm_temporary_table(table_type, path);
+ DBUG_VOID_RETURN;
+}
+
+
+void close_temporary_tables(THD *thd)
+{
+ TABLE *table,*next;
+ for (table=thd->temporary_tables ; table ; table=next)
+ {
+ next=table->next;
+ close_temporary(table);
+ }
+ thd->temporary_tables=0;
+}
+
+
+TABLE **find_temporary_table(THD *thd, const char *db, const char *table_name)
+{
+ char key[MAX_DBKEY_LENGTH];
+ uint key_length= (uint) (strmov(strmov(key,db)+1,table_name)-key)+1;
+ TABLE *table,**prev;
+
+ prev= &thd->temporary_tables;
+ for (table=thd->temporary_tables ; table ; table=table->next)
+ {
+ if (table->key_length == key_length &&
+ !memcmp(table->table_cache_key,key,key_length))
+ return prev;
+ prev= &table->next;
+ }
+ return 0; // Not a temporary table
+}
+
+bool close_temporary_table(THD *thd, const char *db, const char *table_name)
+{
+ TABLE *table,**prev;
+
+ if (!(prev=find_temporary_table(thd,db,table_name)))
+ return 1;
+ table= *prev;
+ *prev= table->next;
+ close_temporary(table);
+ return 0;
+}
+
+bool rename_temporary_table(TABLE *table, const char *db,
+ const char *table_name)
+{
+ char *key;
+ if (!(key=(char*) alloc_root(&table->mem_root,
+ strlen(db)+ strlen(table_name)+2)))
+ return 1; /* purecov: inspected */
+ table->key_length=(uint)
+ (strmov((table->real_name=strmov(table->table_cache_key=key,
+ db)+1),
+ table_name) - table->table_cache_key)+1;
+ return 0;
+}
+
+
+
+
+ /* move table first in unused links */
+
+static void relink_unused(TABLE *table)
+{
+ if (table != unused_tables)
+ {
+ table->prev->next=table->next; /* Remove from unused list */
+ table->next->prev=table->prev;
+ table->next=unused_tables; /* Link in unused tables */
+ table->prev=unused_tables->prev;
+ unused_tables->prev->next=table;
+ unused_tables->prev=table;
+ unused_tables=table;
+ check_unused();
+ }
+}
+
+
+/*
+ Remove all instances of table from the current open list
+ Free all locks on tables that are done with LOCK TABLES
+ */
+
+TABLE *unlink_open_table(THD *thd, TABLE *list, TABLE *find)
+{
+ char key[MAX_DBKEY_LENGTH];
+ uint key_length=find->key_length;
+ TABLE *start=list,**prev,*next;
+ prev= &start;
+ memcpy(key,find->table_cache_key,key_length);
+ for (; list ; list=next)
+ {
+ next=list->next;
+ if (list->key_length == key_length &&
+ !memcmp(list->table_cache_key,key,key_length))
+ {
+ if (thd->locked_tables)
+ mysql_lock_remove(thd, thd->locked_tables,list);
+ VOID(hash_delete(&open_cache,(byte*) list)); // Close table
+ }
+ else
+ {
+ *prev=list; // put in use list
+ prev= &list->next;
+ }
+ }
+ *prev=0;
+ // Notify any 'refresh' threads
+ pthread_cond_broadcast(&COND_refresh);
+ return start;
+}
+
+
+/*
+ When we call the following function we must have a lock on
+ LOCK_OPEN ; This lock will be freed on return
+*/
+
+void wait_for_refresh(THD *thd)
+{
+ /* Wait until the current table is up to date */
+ const char *proc_info;
+ pthread_mutex_lock(&thd->mysys_var->mutex);
+ thd->mysys_var->current_mutex= &LOCK_open;
+ thd->mysys_var->current_cond= &COND_refresh;
+ proc_info=thd->proc_info;
+ thd->proc_info="Waiting for table";
+ pthread_mutex_unlock(&thd->mysys_var->mutex);
+ (void) pthread_cond_wait(&COND_refresh,&LOCK_open);
+
+ pthread_mutex_unlock(&LOCK_open); // Must be unlocked first
+ 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);
+}
+
+
+/******************************************************************************
+** open a table
+** Uses a cache of open tables to find a table not in use.
+** If refresh is a NULL pointer, then the is no version number checking and
+** the table is not put in the thread-open-list
+** If the return value is NULL and refresh is set then one must close
+** all tables and retry the open
+******************************************************************************/
+
+
+TABLE *open_table(THD *thd,const char *db,const char *table_name,
+ const char *alias,bool *refresh)
+{
+ reg1 TABLE *table;
+ char key[MAX_DBKEY_LENGTH];
+ uint key_length;
+ DBUG_ENTER("open_table");
+
+ /* find a unused table in the open table cache */
+ if (refresh)
+ *refresh=0;
+ if (thd->killed)
+ DBUG_RETURN(0);
+ key_length=(uint) (strmov(strmov(key,db)+1,table_name)-key)+1;
+
+ for (table=thd->temporary_tables; table ; table=table->next)
+ {
+ if (table->key_length == key_length &&
+ !memcmp(table->table_cache_key,key,key_length))
+ {
+ if (table->query_id == thd->query_id)
+ {
+ my_printf_error(ER_CANT_REOPEN_TABLE,
+ ER(ER_CANT_REOPEN_TABLE),MYF(0),table->table_name);
+ DBUG_RETURN(0);
+ }
+ table->query_id=thd->query_id;
+ goto reset;
+ }
+ }
+
+ if (thd->locked_tables)
+ { // Using table locks
+ for (table=thd->open_tables; table ; table=table->next)
+ {
+ if (table->key_length == key_length &&
+ !memcmp(table->table_cache_key,key,key_length) &&
+ !my_strcasecmp(table->table_name,alias))
+ goto reset;
+ }
+ my_printf_error(ER_TABLE_NOT_LOCKED,ER(ER_TABLE_NOT_LOCKED),MYF(0),alias);
+ DBUG_RETURN(0);
+ }
+ VOID(pthread_mutex_lock(&LOCK_open));
+
+ if (!thd->open_tables)
+ thd->version=refresh_version;
+ else if (thd->version != refresh_version && refresh)
+ {
+ /* Someone did a refresh while thread was opening tables */
+ *refresh=1;
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ DBUG_RETURN(0);
+ }
+
+ for (table=(TABLE*) hash_search(&open_cache,(byte*) key,key_length) ;
+ table && table->in_use ;
+ table = (TABLE*) hash_next(&open_cache,(byte*) key,key_length))
+ {
+ if (table->version != refresh_version)
+ {
+ /*
+ ** There is a refresh in progress for this table
+ ** Wait until the table is freed or the thread is killed.
+ */
+ close_old_data_files(thd,thd->open_tables,0);
+ if (table->in_use != thd)
+ wait_for_refresh(thd);
+ else
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ if (refresh)
+ *refresh=1;
+ DBUG_RETURN(0);
+ }
+ }
+ if (table)
+ {
+ if (table == unused_tables)
+ { // First unused
+ unused_tables=unused_tables->next; // Remove from link
+ if (table == unused_tables)
+ unused_tables=0;
+ }
+ table->prev->next=table->next; /* Remove from unused list */
+ table->next->prev=table->prev;
+ }
+ else
+ {
+ /* Free cache if too big */
+ while (open_cache.records >= table_cache_size && unused_tables)
+ VOID(hash_delete(&open_cache,(byte*) unused_tables)); /* purecov: tested */
+
+ /* make a new table */
+ if (!(table=(TABLE*) my_malloc(sizeof(*table),MYF(MY_WME))))
+ DBUG_RETURN(NULL);
+ if (open_unireg_entry(table,db,table_name,alias) ||
+ !(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 = strlen(table_name);
+ int db_len = 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));
+ DBUG_RETURN(NULL);
+ }
+ table->key_length=key_length;
+ table->version=refresh_version;
+ table->flush_version=flush_version;
+ if (!key_cache_used)
+ {
+ key_cache_used=1;
+ ha_key_cache();
+ }
+ DBUG_PRINT("info", ("inserting table %p into the cache", table));
+ VOID(hash_insert(&open_cache,(byte*) table));
+ }
+
+ table->in_use=thd;
+ check_unused();
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ if (refresh)
+ {
+ table->next=thd->open_tables; /* Link into simple list */
+ thd->open_tables=table;
+ }
+ table->reginfo.lock_type=TL_READ; /* Assume read */
+
+ reset:
+ /* Fix alias if table name changes */
+ if (strcmp(table->table_name,alias))
+ {
+ uint length=strlen(alias)+1;
+ table->table_name= (char*) my_realloc(table->table_name,length,
+ MYF(MY_WME));
+ memcpy(table->table_name,alias,length);
+ for (uint i=0 ; i < table->fields ; i++)
+ table->field[i]->table_name=table->table_name;
+ }
+ /* These variables are also set in reopen_table() */
+ table->tablenr=thd->current_tablenr++;
+ table->used_fields=0;
+ table->const_table=0;
+ table->outer_join=table->null_row=table->maybe_null=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);
+ DBUG_RETURN(table);
+}
+
+
+TABLE *find_locked_table(THD *thd, const char *db,const char *table_name)
+{
+ char key[MAX_DBKEY_LENGTH];
+ uint key_length=(uint) (strmov(strmov(key,db)+1,table_name)-key)+1;
+
+ for (TABLE *table=thd->open_tables; table ; table=table->next)
+ {
+ if (table->key_length == key_length &&
+ !memcmp(table->table_cache_key,key,key_length))
+ return table;
+ }
+ return(0);
+}
+
+
+/****************************************************************************
+** Reopen an table because the definition has changed. The date file for the
+** table is already closed.
+** Returns 0 if ok.
+** If table can't be reopened, the entry is unchanged.
+****************************************************************************/
+
+bool reopen_table(TABLE *table,bool locked)
+{
+ TABLE tmp;
+ char *db=table->table_cache_key;
+ char *table_name=table->real_name;
+ bool error=1;
+ Field **field;
+ uint key,part;
+ DBUG_ENTER("reopen_table");
+
+#ifdef EXTRA_DEBUG
+ if (table->db_stat)
+ sql_print_error("Table %s had a open data handler in reopen_table",
+ table->table_name);
+#endif
+ if (!locked)
+ VOID(pthread_mutex_lock(&LOCK_open));
+
+ if (open_unireg_entry(&tmp,db,table_name,table->table_name))
+ goto end;
+ free_io_cache(table);
+
+ if (!(tmp.table_cache_key= memdup_root(&tmp.mem_root,db,
+ table->key_length)))
+ {
+ closefrm(&tmp); // End of memory
+ goto end;
+ }
+
+ tmp.key_length=table->key_length;
+ tmp.in_use=table->in_use;
+ tmp.used_keys=tmp.keys_in_use;
+ tmp.reginfo.lock_type=table->reginfo.lock_type;
+ tmp.version=refresh_version;
+ tmp.next=table->next;
+ tmp.prev=table->prev;
+
+ /* This list copies varibles set by open_table */
+ tmp.tablenr= table->tablenr;
+ tmp.tmp_table= table->tmp_table;
+ tmp.used_fields= table->used_fields;
+ tmp.const_table= table->const_table;
+ tmp.outer_join= table->outer_join;
+ tmp.null_row= table->null_row;
+ tmp.status= table->status;
+ tmp.grant= table->grant;
+
+ if (table->file)
+ VOID(closefrm(table)); // close file, free everything
+
+ *table=tmp;
+ table->file->change_table_ptr(table);
+
+ for (field=table->field ; *field ; field++)
+ {
+ (*field)->table=table;
+ (*field)->table_name=table->table_name;
+ }
+ for (key=0 ; key < table->keys ; key++)
+ for (part=0 ; part < table->key_info[key].usable_key_parts ; part++)
+ table->key_info[key].key_part[part].field->table=table;
+ VOID(pthread_cond_broadcast(&COND_refresh));
+ error=0;
+
+ end:
+ if (!locked)
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ DBUG_RETURN(error);
+}
+
+
+/*
+ Used with ALTER TABLE:
+ Close all instanses of table when LOCK TABLES is in used;
+ Close first all instances of table and then reopen them
+ */
+
+bool close_data_tables(THD *thd,const char *db, const char *table_name)
+{
+ TABLE *table;
+ for (table=thd->open_tables; table ; table=table->next)
+ {
+ if (!strcmp(table->real_name,table_name) &&
+ !strcmp(table->table_cache_key,db))
+ {
+ mysql_lock_remove(thd, thd->locked_tables,table);
+ table->file->close();
+ table->db_stat=0;
+ }
+ }
+ return 0; // For the future
+}
+
+
+/*
+ Reopen all tables with closed data files
+ One should have lock on LOCK_open when calling this
+*/
+
+bool reopen_tables(THD *thd,bool get_locks,bool in_refresh)
+{
+ DBUG_ENTER("reopen_tables");
+ if (!thd->open_tables)
+ DBUG_RETURN(0);
+
+ TABLE *table,*next,**prev;
+ TABLE **tables,**tables_ptr; // For locks
+ bool error=0;
+ if (get_locks)
+ {
+ /* The ptr is checked later */
+ uint opens=0;
+ for (table=thd->open_tables; table ; table=table->next) opens++;
+ tables= (TABLE**) my_alloca(sizeof(TABLE*)*opens);
+ }
+ else
+ tables= &thd->open_tables;
+ tables_ptr =tables;
+
+ prev= &thd->open_tables;
+ for (table=thd->open_tables; table ; table=next)
+ {
+ uint db_stat=table->db_stat;
+ next=table->next;
+ if (!tables || (!db_stat && reopen_table(table,1)))
+ {
+ my_error(ER_CANT_REOPEN_TABLE,MYF(0),table->table_name);
+ VOID(hash_delete(&open_cache,(byte*) table));
+ error=1;
+ }
+ else
+ {
+ *prev= table;
+ prev= &table->next;
+ if (get_locks && !db_stat)
+ *tables_ptr++= table; // need new lock on this
+ if (in_refresh)
+ {
+ table->version=0;
+ table->locked_by_flush=0;
+ }
+ }
+ }
+ if (tables != tables_ptr) // Should we get back old locks
+ {
+ MYSQL_LOCK *lock;
+ /* We should always get these locks */
+ thd->some_tables_deleted=0;
+ if ((lock=mysql_lock_tables(thd,tables,(uint) (tables_ptr-tables))))
+ {
+ thd->locked_tables=mysql_lock_merge(thd->locked_tables,lock);
+ }
+ else
+ error=1;
+ }
+ if (get_locks && tables)
+ {
+ my_afree((gptr) tables);
+ }
+ VOID(pthread_cond_broadcast(&COND_refresh)); // Signal to refresh
+ *prev=0;
+ DBUG_RETURN(error);
+}
+
+/*
+ Close handlers for tables in list, but leave the TABLE structure
+ intact so that we can re-open these quickly
+ abort_locks is set if called from flush_tables.
+*/
+
+void close_old_data_files(THD *thd, TABLE *table, bool abort_locks)
+{
+ bool found=0;
+ for (; table ; table=table->next)
+ {
+ if (table->version != refresh_version)
+ {
+ found=1;
+ if (!abort_locks) // If not from flush tables
+ table->version = refresh_version; // Let other threads use table
+ if (table->db_stat)
+ {
+ if (abort_locks)
+ {
+ mysql_lock_abort(thd,table); // Close waiting threads
+ mysql_lock_remove(thd, thd->locked_tables,table);
+ table->locked_by_flush=1; // Will be reopened with locks
+ }
+ table->file->close();
+ table->db_stat=0;
+ }
+ }
+ }
+ if (found)
+ VOID(pthread_cond_broadcast(&COND_refresh)); // Signal to refresh
+}
+
+
+/*
+ Wait until all threads has closed the tables in the list
+ We have also to wait if there is thread that has a lock on this table even
+ if the table is closed
+*/
+
+static bool table_is_used(TABLE *table)
+{
+ do
+ {
+ char *key= table->table_cache_key;
+ uint key_length=table->key_length;
+ for (TABLE *search=(TABLE*) hash_search(&open_cache,(byte*) key,key_length) ;
+ search ;
+ search = (TABLE*) hash_next(&open_cache,(byte*) key,key_length))
+ {
+ if (search->locked_by_flush ||
+ search->db_stat && search->version < refresh_version)
+ return 1; // Table is used
+ }
+ } while ((table=table->next));
+ return 0;
+}
+
+
+/* Wait until all used tables are refreshed */
+
+bool wait_for_tables(THD *thd)
+{
+ bool result;
+ DBUG_ENTER("wait_for_tables");
+
+ thd->proc_info="Waiting for tables";
+ pthread_mutex_lock(&LOCK_open);
+ thd->some_tables_deleted=0;
+ close_old_data_files(thd,thd->open_tables,0);
+ if (dropping_tables)
+ {
+ (void) pthread_cond_broadcast(&COND_refresh); // Signal to refresh/delete
+ (void) pthread_cond_wait(&COND_refresh,&LOCK_open);
+ }
+
+ while (table_is_used(thd->open_tables) && ! thd->killed)
+ {
+ (void) pthread_cond_wait(&COND_refresh,&LOCK_open);
+ }
+
+ if (thd->killed)
+ result= 1; // aborted
+ else
+ {
+ /* Now we can open all tables without any interference */
+ thd->proc_info="Reopen tables";
+ result=reopen_tables(thd,0,0);
+ }
+ pthread_mutex_unlock(&LOCK_open);
+ thd->proc_info=0;
+ DBUG_RETURN(result);
+}
+
+
+/* drop tables from locked list */
+
+bool drop_locked_tables(THD *thd,const char *db, const char *table_name)
+{
+ TABLE *table,*next,**prev;
+ bool found=0;
+ prev= &thd->open_tables;
+ for (table=thd->open_tables; table ; table=next)
+ {
+ next=table->next;
+ if (!strcmp(table->real_name,table_name) &&
+ !strcmp(table->table_cache_key,db))
+ {
+ mysql_lock_remove(thd, thd->locked_tables,table);
+ VOID(hash_delete(&open_cache,(byte*) table));
+ found=1;
+ }
+ else
+ {
+ *prev=table;
+ prev= &table->next;
+ }
+ }
+ *prev=0;
+ if (found)
+ VOID(pthread_cond_broadcast(&COND_refresh)); // Signal to refresh
+ if (thd->locked_tables && thd->locked_tables->table_count == 0)
+ {
+ my_free((gptr) thd->locked_tables,MYF(0));
+ thd->locked_tables=0;
+ }
+ return found;
+}
+
+
+/* lock table to force abort of any threads trying to use table */
+
+void abort_locked_tables(THD *thd,const char *db, const char *table_name)
+{
+ TABLE *table;
+ 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);
+ }
+}
+
+/****************************************************************************
+** 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
+*/
+
+static int open_unireg_entry(TABLE *entry,const char *db,const char *name,
+ const char *alias)
+{
+ char path[FN_REFLEN];
+ DBUG_ENTER("open_unireg_entry");
+
+ (void) sprintf(path,"%s/%s/%s",mysql_data_home,db,name);
+ if (openfrm(path,alias,
+ (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE | HA_GET_INDEX |
+ HA_TRY_READ_ONLY),
+ READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD,
+ entry))
+ {
+ DBUG_RETURN(1);
+ }
+ (void) entry->file->extra(HA_EXTRA_NO_READCHECK); // Not needed in SQL
+ DBUG_RETURN(0);
+}
+
+
+/*****************************************************************************
+** open all tables in list
+*****************************************************************************/
+
+int open_tables(THD *thd,TABLE_LIST *start)
+{
+ TABLE_LIST *tables;
+ bool refresh;
+ int result=0;
+ DBUG_ENTER("open_tables");
+
+ restart:
+ thd->proc_info="Opening tables";
+ for (tables=start ; tables ; tables=tables->next)
+ {
+ if (!tables->table &&
+ !(tables->table=open_table(thd,
+ tables->db ? tables->db : thd->db,
+ tables->real_name,
+ tables->name, &refresh)))
+ {
+ if (refresh) // Refresh in progress
+ {
+ /* close all 'old' tables used by this thread */
+ pthread_mutex_lock(&LOCK_open);
+ thd->version=refresh_version;
+ TABLE **prev_table= &thd->open_tables;
+ bool found=0;
+ for (TABLE_LIST *tmp=start ; tmp ; tmp=tmp->next)
+ {
+ if (tmp->table)
+ {
+ if (tmp->table->version != refresh_version ||
+ ! tmp->table->db_stat)
+ {
+ VOID(hash_delete(&open_cache,(byte*) tmp->table));
+ tmp->table=0;
+ found=1;
+ }
+ else
+ {
+ *prev_table= tmp->table; // Relink open list
+ prev_table= &tmp->table->next;
+ }
+ }
+ }
+ *prev_table=0;
+ if (found)
+ VOID(pthread_cond_broadcast(&COND_refresh)); // Signal to refresh
+ pthread_mutex_unlock(&LOCK_open);
+ goto restart;
+ }
+ result= -1; // Fatal error
+ break;
+ }
+ if (tables->lock_type != TL_UNLOCK)
+ tables->table->reginfo.lock_type=tables->lock_type;
+ tables->table->grant= tables->grant;
+ }
+ thd->proc_info=0;
+ DBUG_RETURN(result);
+}
+
+
+TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type)
+{
+ TABLE *table;
+ bool refresh;
+ DBUG_ENTER("open_ltable");
+
+#ifdef __WIN__
+ /* Win32 can't drop a file that is open */
+ if (lock_type == TL_WRITE_ALLOW_READ)
+ lock_type= TL_WRITE;
+#endif
+ thd->proc_info="Opening table";
+ while (!(table=open_table(thd,table_list->db ? table_list->db : thd->db,
+ table_list->real_name,table_list->name,
+ &refresh)) && refresh) ;
+ if (table)
+ {
+ 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->name);
+ DBUG_RETURN(0);
+ }
+ thd->proc_info=0;
+ DBUG_RETURN(table);
+ }
+ 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
+*/
+
+int open_and_lock_tables(THD *thd,TABLE_LIST *tables)
+{
+ if (open_tables(thd,tables) || lock_tables(thd,tables))
+ return -1; /* purecov: inspected */
+ return 0;
+}
+
+int lock_tables(THD *thd,TABLE_LIST *tables)
+{
+ if (tables && !thd->locked_tables)
+ {
+ uint count=0;
+ TABLE_LIST *table;
+ for (table = tables ; table ; table=table->next)
+ count++;
+ TABLE **start,**ptr;
+ if (!(ptr=start=(TABLE**) sql_alloc(sizeof(TABLE*)*count)))
+ return -1;
+ for (table = tables ; table ; table=table->next)
+ *(ptr++)= table->table;
+ if (!(thd->lock=mysql_lock_tables(thd,start,count)))
+ return -1; /* purecov: inspected */
+ }
+ 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 ...
+*/
+
+TABLE *open_temporary_table(THD *thd, const char *path, const char *db,
+ const char *table_name, bool link_in_list)
+{
+ TABLE *tmp_table;
+ DBUG_ENTER("open_temporary_table");
+ if (!(tmp_table=(TABLE*) my_malloc(sizeof(*tmp_table)+strlen(db)+
+ strlen(table_name)+2,
+ MYF(MY_WME))))
+ DBUG_RETURN(0); /* purecov: inspected */
+
+ if (openfrm(path, table_name,
+ (uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE | HA_GET_INDEX |
+ HA_TRY_READ_ONLY),
+ READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD,
+ tmp_table))
+ {
+ 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 = 1;
+ tmp_table->table_cache_key=(char*) (tmp_table+1);
+ tmp_table->key_length= (uint) (strmov(strmov(tmp_table->table_cache_key,db)
+ +1, table_name)
+ - tmp_table->table_cache_key)+1;
+ if (link_in_list)
+ {
+ tmp_table->next=thd->temporary_tables;
+ thd->temporary_tables=tmp_table;
+ }
+ DBUG_RETURN(tmp_table);
+}
+
+
+bool rm_temporary_table(enum db_type base, char *path)
+{
+ bool error=0;
+ fn_format(path, path,"",reg_ext,4);
+ unpack_filename(path,path);
+ if (my_delete(path,MYF(0)))
+ error=1; /* purecov: inspected */
+ *fn_ext(path)='\0'; // remove extension
+ handler *file=get_new_handler((TABLE*) 0, base);
+ if (file && file->delete_table(path))
+ error=1;
+ delete file;
+ return error;
+}
+
+
+/*****************************************************************************
+** find field in list or tables. if field is unqualifed and unique,
+** return unique field
+******************************************************************************/
+
+#define WRONG_GRANT (Field*) -1
+
+Field *find_field_in_table(THD *thd,TABLE *table,const char *name,uint length,
+ bool check_grants, bool allow_rowid)
+{
+ Field *field;
+ if (table->name_hash.records)
+ {
+ if ((field=(Field*) hash_search(&table->name_hash,(byte*) name,
+ length)))
+ goto found;
+ }
+ else
+ {
+ Field **ptr=table->field;
+ while ((field = *ptr++))
+ {
+ if (!my_strcasecmp(field->field_name, name))
+ goto found;
+ }
+ }
+ if (allow_rowid && !my_strcasecmp(name,"_rowid") &&
+ (field=table->rowid_field))
+ goto found;
+ return (Field*) 0;
+
+ found:
+ if (thd->set_query_id)
+ {
+ if (field->query_id != thd->query_id)
+ {
+ field->query_id=thd->query_id;
+ field->table->used_fields++;
+ }
+ else
+ thd->dupp_field=field;
+ field->table->used_keys&=field->part_of_key;
+ }
+ if (check_grants && !thd->master_access && check_grant_column(thd,table,name,length))
+ return WRONG_GRANT;
+ return field;
+}
+
+
+Field *
+find_field_in_tables(THD *thd,Item_field *item,TABLE_LIST *tables)
+{
+ Field *found=0;
+ const char *db=item->db_name;
+ const char *table_name=item->table_name;
+ const char *name=item->field_name;
+ uint length=strlen(name);
+
+ if (table_name)
+ { /* Qualified field */
+ bool found_table=0;
+ for (; tables ; tables=tables->next)
+ {
+ if (!strcmp(tables->name,table_name) &&
+ (!db ||
+ (tables->db && !strcmp(db,tables->db)) ||
+ (!tables->db && !strcmp(db,thd->db))))
+ {
+ found_table=1;
+ Field *find=find_field_in_table(thd,tables->table,name,length,
+ grant_option && !thd->master_access,1);
+ if (find)
+ {
+ if (find == WRONG_GRANT)
+ return (Field*) 0;
+ if (db || !thd->where)
+ return find;
+ if (found)
+ {
+ my_printf_error(ER_NON_UNIQ_ERROR,ER(ER_NON_UNIQ_ERROR),MYF(0),
+ item->full_name(),thd->where);
+ return (Field*) 0;
+ }
+ found=find;
+ }
+ }
+ }
+ if (found)
+ return found;
+ if (!found_table)
+ {
+ char buff[NAME_LEN*2+1];
+ if (db)
+ {
+ strxmov(buff,db,".",table_name,NullS);
+ table_name=buff;
+ }
+ my_printf_error(ER_UNKNOWN_TABLE,ER(ER_UNKNOWN_TABLE),MYF(0),table_name,
+ thd->where);
+ }
+ else
+ my_printf_error(ER_BAD_FIELD_ERROR,ER(ER_BAD_FIELD_ERROR),MYF(0),
+ item->full_name(),thd->where);
+ return (Field*) 0;
+ }
+ bool allow_rowid= tables && !tables->next; // Only one table
+ for (; tables ; tables=tables->next)
+ {
+ Field *field=find_field_in_table(thd,tables->table,name,length,
+ grant_option && !thd->master_access, allow_rowid);
+ if (field)
+ {
+ if (field == WRONG_GRANT)
+ return (Field*) 0;
+ if (found)
+ {
+ if (!thd->where) // Returns first found
+ break;
+ my_printf_error(ER_NON_UNIQ_ERROR,ER(ER_NON_UNIQ_ERROR),MYF(0),
+ name,thd->where);
+ return (Field*) 0;
+ }
+ found=field;
+ }
+ }
+ if (found)
+ return found;
+ my_printf_error(ER_BAD_FIELD_ERROR,ER(ER_BAD_FIELD_ERROR),
+ MYF(0),item->full_name(),thd->where);
+ return (Field*) 0;
+}
+
+Item **
+find_item_in_list(Item *find,List<Item> &items)
+{
+ List_iterator<Item> li(items);
+ Item **found=0,*item;
+ const char *field_name=0;
+ const char *table_name=0;
+ if (find->type() == Item::FIELD_ITEM || find->type() == Item::REF_ITEM)
+ {
+ field_name= ((Item_ident*) find)->field_name;
+ table_name= ((Item_ident*) find)->table_name;
+ }
+
+ while ((item=li++))
+ {
+ if (field_name && item->type() == Item::FIELD_ITEM)
+ {
+ if (!my_strcasecmp(((Item_field*) item)->name,field_name))
+ {
+ if (!table_name)
+ {
+ if (found)
+ {
+ if ((*found)->eq(item))
+ continue; // Same field twice (Access?)
+ if (current_thd->where)
+ my_printf_error(ER_NON_UNIQ_ERROR,ER(ER_NON_UNIQ_ERROR),MYF(0),
+ find->full_name(), current_thd->where);
+ return (Item**) 0;
+ }
+ found=li.ref();
+ }
+ else if (!strcmp(((Item_field*) item)->table_name,table_name))
+ {
+ found=li.ref();
+ break;
+ }
+ }
+ }
+ else if (!table_name && (item->eq(find) ||
+ find->name &&
+ !my_strcasecmp(item->name,find->name)))
+ {
+ found=li.ref();
+ break;
+ }
+ }
+ if (!found && current_thd->where)
+ my_printf_error(ER_BAD_FIELD_ERROR,ER(ER_BAD_FIELD_ERROR),MYF(0),
+ find->full_name(),current_thd->where);
+ return found;
+}
+
+
+/****************************************************************************
+** Check that all given fields exists and fill struct with current data
+** Check also that the 'used keys' and 'ignored keys' exists and set up the
+** table structure accordingly
+****************************************************************************/
+
+int setup_fields(THD *thd, TABLE_LIST *tables, List<Item> &fields,
+ bool set_query_id, List<Item> *sum_func_list)
+{
+ 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->where="field list";
+
+ /* Remap table numbers if INSERT ... SELECT */
+ 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)
+ {
+ key_map map= get_key_map_from_key_list(thd,table->table,
+ table->use_index);
+ if (map == ~(key_map) 0)
+ DBUG_RETURN(-1);
+ table->table->keys_in_use_for_query=map;
+ }
+ if (table->ignore_index)
+ {
+ key_map map= get_key_map_from_key_list(thd,table->table,
+ table->ignore_index);
+ if (map == ~(key_map) 0)
+ DBUG_RETURN(-1);
+ table->table->keys_in_use_for_query &= ~map;
+ }
+ }
+ if (tablenr > MAX_TABLES)
+ {
+ my_error(ER_TOO_MANY_TABLES,MYF(0),MAX_TABLES);
+ DBUG_RETURN(-1);
+ }
+ while ((item=it++))
+ {
+ if (item->type() == Item::FIELD_ITEM &&
+ ((Item_field*) item)->field_name[0] == '*')
+ {
+ if (insert_fields(thd,tables,((Item_field*) item)->table_name,&it))
+ DBUG_RETURN(-1); /* purecov: inspected */
+ }
+ else
+ {
+ if (item->fix_fields(thd,tables))
+ DBUG_RETURN(-1); /* purecov: inspected */
+ if (item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM)
+ item->split_sum_func(*sum_func_list);
+ }
+ }
+ DBUG_RETURN(test(thd->fatal_error));
+}
+
+
+static key_map get_key_map_from_key_list(THD *thd, TABLE *table,
+ List<String> *index_list)
+{
+ key_map map=0;
+ List_iterator<String> it(*index_list);
+ String *name;
+ uint pos;
+ while ((name=it++))
+ {
+ if ((pos=find_type(name->c_ptr(), &table->keynames, 1)) <= 0)
+ {
+ my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), name->c_ptr(),
+ table->real_name);
+ return (~ (key_map) 0);
+ }
+ map|= ((key_map) 1) << (pos-1);
+ }
+ return map;
+}
+
+/****************************************************************************
+** This just drops in all fields instead of current '*' field
+** Returns pointer to last inserted field if ok
+****************************************************************************/
+
+static bool
+insert_fields(THD *thd,TABLE_LIST *tables, const char *table_name,
+ List_iterator<Item> *it)
+{
+ TABLE_LIST *table;
+ uint found;
+ DBUG_ENTER("insert_fields");
+
+ found=0;
+ for (table=tables ; table ; table=table->next)
+ {
+ if (grant_option && !thd->master_access &&
+ check_grant_all_columns(thd,SELECT_ACL,table->table) )
+ DBUG_RETURN(-1);
+ if (!table_name || !strcmp(table_name,table->name))
+ {
+ Field **ptr=table->table->field,*field;
+ while ((field = *ptr++))
+ {
+ Item_field *item= new Item_field(field);
+ if (!found++)
+ (void) it->replace(item);
+ else
+ it->after(item);
+ if (field->query_id == thd->query_id)
+ thd->dupp_field=field;
+ field->query_id=thd->query_id;
+ field->table->used_keys&=field->part_of_key;
+ }
+ /* All fields are used */
+ table->table->used_fields=table->table->fields;
+ }
+ }
+ if (!found)
+ {
+ if (!table_name)
+ my_error(ER_NO_TABLES_USED,MYF(0));
+ else
+ my_error(ER_BAD_TABLE_ERROR,MYF(0),table_name);
+ }
+ DBUG_RETURN(!found);
+}
+
+
+/*
+** Fix all conditions and outer join expressions
+*/
+
+int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds)
+{
+ DBUG_ENTER("setup_conds");
+ thd->set_query_id=1;
+ thd->cond_count=0;
+ thd->allow_sum_func=0;
+ if (*conds)
+ {
+ thd->where="where clause";
+ if ((*conds)->fix_fields(thd,tables))
+ DBUG_RETURN(1);
+ }
+
+ /* Check if we are using outer joins */
+ for (TABLE_LIST *table=tables ; table ; table=table->next)
+ {
+ if (table->natural_join)
+ {
+ /* Make a join of all fields with have the same name */
+ TABLE *t1=table->table;
+ TABLE *t2=table->natural_join->table;
+ Item_cond_and *cond_and=new Item_cond_and();
+ if (!cond_and) // If not out of memory
+ DBUG_RETURN(1);
+
+ uint i,j;
+ for (i=0 ; i < t1->fields ; i++)
+ {
+ // TODO: This could be optimized to use hashed names if t2 had a hash
+ for (j=0 ; j < t2->fields ; j++)
+ {
+ if (!my_strcasecmp(t1->field[i]->field_name,
+ t2->field[j]->field_name))
+ {
+ Item_func_eq *tmp=new Item_func_eq(new Item_field(t1->field[i]),
+ new Item_field(t2->field[j]));
+ if (!tmp)
+ DBUG_RETURN(1);
+ tmp->fix_length_and_dec(); // Update cmp_type
+ tmp->const_item_cache=0;
+ cond_and->list.push_back(tmp);
+ t1->used_keys&= t1->field[i]->part_of_key;
+ t2->used_keys&= t2->field[j]->part_of_key;
+ break;
+ }
+ }
+ }
+ cond_and->used_tables_cache= t1->map | t2->map;
+ thd->cond_count+=cond_and->list.elements;
+ if (!table->outer_join) // Not left join
+ {
+ if (!(*conds=and_conds(*conds, cond_and)))
+ DBUG_RETURN(1);
+ }
+ else
+ table->on_expr=cond_and;
+ }
+ else if (table->on_expr)
+ {
+ /* Make a join an a expression */
+ thd->where="on clause";
+ if (table->on_expr->fix_fields(thd,tables))
+ DBUG_RETURN(1);
+ thd->cond_count++;
+
+ /* If it's a normal join, add the ON/USING expression to the WHERE */
+ if (!table->outer_join)
+ {
+ if (!(*conds=and_conds(*conds, table->on_expr)))
+ DBUG_RETURN(1);
+ table->on_expr=0;
+ }
+ }
+ }
+ DBUG_RETURN(test(thd->fatal_error));
+}
+
+
+/******************************************************************************
+** Fill a record with data (for INSERT or UPDATE)
+** Returns : 1 if some field has wrong type
+******************************************************************************/
+
+int
+fill_record(List<Item> &fields,List<Item> &values)
+{
+ List_iterator<Item> f(fields),v(values);
+ Item *value;
+ Item_field *field;
+ DBUG_ENTER("fill_record");
+
+ while ((field=(Item_field*) f++))
+ {
+ value=v++;
+ if (value->save_in_field(field->field))
+ DBUG_RETURN(1);
+ }
+ DBUG_RETURN(0);
+}
+
+
+int
+fill_record(Field **ptr,List<Item> &values)
+{
+ List_iterator<Item> v(values);
+ Item *value;
+ DBUG_ENTER("fill_record");
+
+ Field *field;
+ while ((field = *ptr++))
+ {
+ value=v++;
+ if (value->save_in_field(field))
+ DBUG_RETURN(1);
+ }
+ DBUG_RETURN(0);
+}
+
+
+static void mysql_rm_tmp_tables(void)
+{
+ uint idx;
+ char filePath[FN_REFLEN];
+ MY_DIR *dirp;
+ FILEINFO *file;
+ DBUG_ENTER("mysql_rm_tmp_tables");
+
+ /* See if the directory exists */
+ if (!(dirp = my_dir(mysql_tmpdir,MYF(MY_WME | MY_DONT_SORT))))
+ DBUG_VOID_RETURN; /* purecov: inspected */
+
+ /*
+ ** Remove all SQLxxx tables from directory
+ */
+
+ for (idx=2 ; idx < (uint) dirp->number_off_files ; idx++)
+ {
+ file=dirp->dir_entry+idx;
+ if (!bcmp(file->name,tmp_file_prefix,tmp_file_prefix_length))
+ {
+ sprintf(filePath,"%s%s",mysql_tmpdir,file->name); /* purecov: inspected */
+ VOID(my_delete(filePath,MYF(MY_WME))); /* purecov: inspected */
+ }
+ }
+ my_dirend(dirp);
+ DBUG_VOID_RETURN;
+}
+
+
+/*
+** CREATE INDEX and DROP INDEX are implemented by calling ALTER TABLE with
+** the proper arguments. This isn't very fast but it should work for most
+** cases.
+** One should normally create all indexes with CREATE TABLE or ALTER TABLE.
+*/
+
+int mysql_create_index(THD *thd, TABLE_LIST *table_list, List<Key> &keys)
+{
+ List<create_field> fields;
+ List<Alter_drop> drop;
+ List<Alter_column> alter;
+ HA_CREATE_INFO create_info;
+ DBUG_ENTER("mysql_create_index");
+ bzero((char*) &create_info,sizeof(create_info));
+ 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, FALSE, DUP_ERROR));
+}
+
+
+int mysql_drop_index(THD *thd, TABLE_LIST *table_list, List<Alter_drop> &drop)
+{
+ List<create_field> fields;
+ List<Key> keys;
+ List<Alter_column> alter;
+ HA_CREATE_INFO create_info;
+ DBUG_ENTER("mysql_drop_index");
+ bzero((char*) &create_info,sizeof(create_info));
+ 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, FALSE, DUP_ERROR));
+}
+
+/*****************************************************************************
+ unireg support functions
+*****************************************************************************/
+
+/*
+** Invalidate any cache entries that are for some DB
+** We can't use hash_delete when looping hash_elements. We mark them first
+** and afterwards delete those marked unused.
+*/
+
+void remove_db_from_cache(const my_string db)
+{
+ for (uint idx=0 ; idx < open_cache.records ; idx++)
+ {
+ TABLE *table=(TABLE*) hash_element(&open_cache,idx);
+ if (!strcmp(table->table_cache_key,db))
+ {
+ table->version=0L; /* Free when thread is ready */
+ if (!table->in_use)
+ relink_unused(table);
+ }
+ }
+ while (unused_tables && !unused_tables->version)
+ VOID(hash_delete(&open_cache,(byte*) unused_tables));
+}
+
+
+/*
+** free all unused tables
+*/
+
+void flush_tables()
+{
+ (void) pthread_mutex_lock(&LOCK_open);
+ while (unused_tables)
+ hash_delete(&open_cache,(byte*) unused_tables);
+ (void) pthread_mutex_unlock(&LOCK_open);
+}
+
+
+/*
+** Mark all entries with the table as deleted to force an reopen of the table
+** Returns true if the table is in use by another thread
+*/
+
+bool remove_table_from_cache(THD *thd, const char *db,const char *table_name)
+{
+ char key[MAX_DBKEY_LENGTH];
+ uint key_length;
+ TABLE *table;
+ bool result=0;
+ DBUG_ENTER("remove_table_from_cache");
+
+ key_length=(uint) (strmov(strmov(key,db)+1,table_name)-key)+1;
+ for (table=(TABLE*) hash_search(&open_cache,(byte*) key,key_length) ;
+ table;
+ table = (TABLE*) hash_next(&open_cache,(byte*) key,key_length))
+ {
+ table->version=0L; /* Free when thread is ready */
+ if (!table->in_use)
+ relink_unused(table);
+ else if (table->in_use != thd)
+ {
+ THD *in_use=table->in_use;
+
+ in_use->some_tables_deleted=1;
+ if (table->db_stat)
+ result=1;
+ /* Kill delayed insert threads */
+ if (in_use->system_thread && ! in_use->killed)
+ {
+ in_use->killed=1;
+ pthread_mutex_lock(&in_use->mysys_var->mutex);
+ if (in_use->mysys_var->current_mutex)
+ {
+ pthread_mutex_lock(in_use->mysys_var->current_mutex);
+ pthread_cond_broadcast(in_use->mysys_var->current_cond);
+ pthread_mutex_unlock(in_use->mysys_var->current_mutex);
+ }
+ pthread_mutex_unlock(&in_use->mysys_var->mutex);
+ }
+ }
+ }
+ while (unused_tables && !unused_tables->version)
+ VOID(hash_delete(&open_cache,(byte*) unused_tables));
+ DBUG_RETURN(result);
+}
+
+/*
+ Will be used for ft-query optimization someday.
+ SerG.
+ */
+int setup_ftfuncs(THD *thd,TABLE_LIST *tables, List<Item_func_match> &ftfuncs)
+{
+ List_iterator<Item_func_match> li(ftfuncs);
+ Item_func_match *ftf;
+
+ while ((ftf=li++))
+ if (ftf->fix_index())
+ return 1;
+
+ return 0;
+}
+
diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc
new file mode 100644
index 00000000000..78053d0d3e3
--- /dev/null
+++ b/sql/sql_cache.cc
@@ -0,0 +1,98 @@
+/* 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>
+#include <my_dir.h>
+#include <hash.h>
+
+#define SQL_CACHE_LENGTH 300
+
+HASH sql_cache;
+LEX lex_array_static[SQL_CACHE_LENGTH];
+LEX * lex_array = lex_array_static;
+int last_lex_array_item = SQL_CACHE_LENGTH - 1;
+
+/* Function to return a text string from a LEX struct */
+static byte *cache_key(const byte *record, uint *length, my_bool not_used)
+{
+#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;
+#endif
+ return 0;
+}
+
+/* At the moment we do not really want to do anything upon delete */
+static void free_cache_entry(void *entry)
+{
+}
+
+/* Initialization of the SQL cache hash -- should be called during
+ the bootstrap stage */
+bool sql_cache_init(void)
+{
+ if (query_buff_size)
+ {
+ VOID(hash_init(&sql_cache, 4096, 0, 0,
+ cache_key,
+ (void (*)(void*)) free_cache_entry,
+ 0));
+ }
+ return 0;
+}
+
+/* Clearing the SQL cache hash -- during shutdown */
+void sql_cache_free(void)
+{
+ hash_free(&sql_cache);
+}
+
+/* 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)
+{
+#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--;
+
+ lex_start(thd, (uchar *)sql, length);
+
+ if (hash_insert(&sql_cache, (const byte *)ptr)) {
+ fprintf(stderr, "Out of memory during hash_insert?\n");
+ }
+ 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");
+ }
+ return 0;
+ }
+#endif
+ return 1;
+}
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
new file mode 100644
index 00000000000..19b9ecdaae0
--- /dev/null
+++ b/sql/sql_class.cc
@@ -0,0 +1,593 @@
+/* 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 */
+
+
+/*****************************************************************************
+**
+** This file implements classes defined in sql_class.h
+** Especially the classes to handle a result from a select
+**
+*****************************************************************************/
+
+#ifdef __GNUC__
+#pragma implementation // gcc: Class implementation
+#endif
+
+#include "mysql_priv.h"
+#include "sql_acl.h"
+#include <m_ctype.h>
+#include <sys/stat.h>
+#ifdef __WIN__
+#include <io.h>
+#endif
+
+/*****************************************************************************
+** Instansiate templates
+*****************************************************************************/
+
+#ifdef __GNUC__
+/* Used templates */
+template class List<Key>;
+template class List_iterator<Key>;
+template class List<key_part_spec>;
+template class List_iterator<key_part_spec>;
+template class List<Alter_drop>;
+template class List_iterator<Alter_drop>;
+template class List<Alter_column>;
+template class List_iterator<Alter_column>;
+#endif
+
+/****************************************************************************
+** User variables
+****************************************************************************/
+
+static 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)
+{
+ char *pos= (char*) entry+ALIGN_SIZE(sizeof(*entry));
+ if (entry->value && entry->value != pos)
+ my_free(entry->value, MYF(0));
+ my_free((char*) entry,MYF(0));
+}
+
+
+/****************************************************************************
+** Thread specific functions
+****************************************************************************/
+
+THD::THD()
+{
+ host=user=db=query=ip=0;
+ proc_info="login";
+ locked=killed=count_cuted_fields=some_tables_deleted=no_errors=password=
+ fatal_error=query_start_used=last_insert_id_used=insert_id_used=
+ user_time=bootstrap=in_lock_tables=global_read_lock=0;
+ query_length=col_access=0;
+ query_error=0;
+ server_status=SERVER_STATUS_AUTOCOMMIT;
+ next_insert_id=last_insert_id=0;
+ open_tables=temporary_tables=0;
+ tmp_table=0;
+ lock=locked_tables=0;
+ cuted_fields=0L;
+ options=thd_startup_options;
+ update_lock_default= low_priority_updates ? TL_WRITE_LOW_PRIORITY : TL_WRITE;
+ start_time=(time_t) 0;
+ last_nx_table = last_nx_db = 0;
+ inactive_timeout=net_wait_timeout;
+ cond_count=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);
+ convert_set=0;
+ mysys_var=0;
+ db_access=NO_ACCESS;
+ hash_init(&user_vars, USER_VARS_HASH_SIZE, 0, 0,
+ (hash_get_key) get_var_key,
+ (void (*)(void*)) free_var,0);
+ net.vio=0;
+ ull=0;
+ system_thread=0;
+ bzero((char*) &alloc,sizeof(alloc));
+#ifdef __WIN__
+ real_id = 0 ;
+#endif
+ transaction.bdb_lock_count=0;
+ transaction.bdb_tid=0;
+}
+
+THD::~THD()
+{
+ DBUG_ENTER("~THD()");
+ /* Close connection */
+ if (net.vio)
+ {
+ vio_delete(net.vio);
+ net_end(&net);
+ }
+ ha_rollback(this);
+ if (locked_tables)
+ {
+ lock=locked_tables; locked_tables=0;
+ close_thread_tables(this);
+ }
+ close_temporary_tables(this);
+ if (global_read_lock)
+ {
+ pthread_mutex_lock(&LOCK_open);
+ ::global_read_lock--;
+ pthread_cond_broadcast(&COND_refresh);
+ pthread_mutex_unlock(&LOCK_open);
+ }
+ if (ull)
+ {
+ pthread_mutex_lock(&LOCK_user_locks);
+ item_user_lock_release(ull);
+ pthread_mutex_unlock(&LOCK_user_locks);
+ }
+ hash_free(&user_vars);
+
+ DBUG_PRINT("info", ("freeing host"));
+
+ safeFree(host);
+ safeFree(user);
+ safeFree(db);
+ safeFree(ip);
+ free_root(&alloc);
+ mysys_var=0; // Safety (shouldn't be needed)
+ 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()
+{
+ return (my_pthread_setspecific_ptr(THR_THD, this) ||
+ my_pthread_setspecific_ptr(THR_MALLOC, &alloc) ||
+ my_pthread_setspecific_ptr(THR_NET, &net));
+}
+
+
+/*****************************************************************************
+** Functions to provide a interface to select results
+*****************************************************************************/
+
+select_result::select_result()
+{
+ thd=current_thd;
+}
+
+static String default_line_term("\n"),default_escaped("\\"),
+ default_field_term("\t");
+
+sql_exchange::sql_exchange(char *name,bool flag)
+ :file_name(name), opt_enclosed(0), dumpfile(flag), skip_lines(0)
+{
+ field_term= &default_field_term;
+ enclosed= line_start= &empty_string;
+ line_term= &default_line_term;
+ escaped= &default_escaped;
+}
+
+bool select_send::send_fields(List<Item> &list,uint flag)
+{
+ return ::send_fields(thd,list,flag);
+}
+
+
+/* Send data to client. Returns 0 if ok */
+
+bool select_send::send_data(List<Item> &items)
+{
+ List_iterator<Item> li(items);
+ String *packet= &thd->packet;
+ DBUG_ENTER("send_data");
+
+ if (thd->offset_limit)
+ { // using limit offset,count
+ thd->offset_limit--;
+ DBUG_RETURN(0);
+ }
+ packet->length(0); // Reset packet
+ Item *item;
+ while ((item=li++))
+ {
+ if (item->send(packet))
+ {
+ packet->free(); // Free used
+ my_error(ER_OUT_OF_RESOURCES,MYF(0));
+ DBUG_RETURN(1);
+ }
+ }
+ bool error=my_net_write(&thd->net,(char*) packet->ptr(),packet->length());
+ DBUG_RETURN(error);
+}
+
+
+void select_send::send_error(uint errcode,const char *err)
+{
+ ::send_error(&thd->net,errcode,err);
+}
+
+bool select_send::send_eof()
+{
+ /* Unlock tables before sending packet to gain some speed */
+ if (thd->lock)
+ {
+ mysql_unlock_tables(thd, thd->lock); thd->lock=0;
+ }
+ ::send_eof(&thd->net);
+ return 0;
+}
+
+
+/***************************************************************************
+** Export of select to textfile
+***************************************************************************/
+
+
+select_export::~select_export()
+{
+ if (file >= 0)
+ { // This only happens in case of error
+ (void) end_io_cache(&cache);
+ (void) my_close(file,MYF(0));
+ file= -1;
+ }
+}
+
+int
+select_export::prepare(List<Item> &list)
+{
+ char path[FN_REFLEN];
+ uint option=4;
+ bool blob_flag=0;
+#ifdef DONT_ALLOW_FULL_LOAD_DATA_PATHS
+ option|=1; // Force use of db directory
+#endif
+ if (strlen(exchange->file_name) + NAME_LEN >= FN_REFLEN)
+ strmake(path,exchange->file_name,FN_REFLEN-1);
+ (void) fn_format(path,exchange->file_name, thd->db ? thd->db : "", "",
+ option);
+ if (!access(path,F_OK))
+ {
+ my_error(ER_FILE_EXISTS_ERROR,MYF(0),exchange->file_name);
+ return 1;
+ }
+ /* Create the file world readable */
+ if ((file=my_create(path, 0666, O_WRONLY, MYF(MY_WME))) < 0)
+ return 1;
+#ifdef HAVE_FCHMOD
+ (void) fchmod(file,0666); // Because of umask()
+#else
+ (void) chmod(path,0666);
+#endif
+ if (init_io_cache(&cache,file,0L,WRITE_CACHE,0L,1,MYF(MY_WME)))
+ {
+ my_close(file,MYF(0));
+ file= -1;
+ return 1;
+ }
+ /* Check if there is any blobs in data */
+ {
+ List_iterator<Item> li(list);
+ Item *item;
+ while ((item=li++))
+ {
+ if (item->max_length >= MAX_BLOB_WIDTH)
+ {
+ blob_flag=1;
+ break;
+ }
+ }
+ }
+ field_term_length=exchange->field_term->length();
+ if (!exchange->line_term->length())
+ exchange->line_term=exchange->field_term; // Use this if it exists
+ field_sep_char= (exchange->enclosed->length() ? (*exchange->enclosed)[0] :
+ field_term_length ? (*exchange->field_term)[0] : INT_MAX);
+ escape_char= (exchange->escaped->length() ? (*exchange->escaped)[0] : -1);
+ line_sep_char= (exchange->line_term->length() ?
+ (*exchange->line_term)[0] : INT_MAX);
+ if (!field_term_length)
+ exchange->opt_enclosed=0;
+ if (!exchange->enclosed->length())
+ exchange->opt_enclosed=1; // A little quicker loop
+ fixed_row_size= (!field_term_length && !exchange->enclosed->length() &&
+ !blob_flag);
+ return 0;
+}
+
+
+bool select_export::send_data(List<Item> &items)
+{
+
+ DBUG_ENTER("send_data");
+ char buff[MAX_FIELD_WIDTH],null_buff[2],space[MAX_FIELD_WIDTH];
+ bool space_inited=0;
+ String tmp(buff,sizeof(buff)),*res;
+ tmp.length(0);
+
+ if (thd->offset_limit)
+ { // using limit offset,count
+ thd->offset_limit--;
+ DBUG_RETURN(0);
+ }
+ row_count++;
+ Item *item;
+ char *buff_ptr=buff;
+ uint used_length=0,items_left=items.elements;
+ List_iterator<Item> li(items);
+
+ if (my_b_write(&cache,(byte*) exchange->line_start->ptr(),
+ exchange->line_start->length()))
+ goto err;
+ while ((item=li++))
+ {
+ Item_result result_type=item->result_type();
+ res=item->str_result(&tmp);
+ if (res && (!exchange->opt_enclosed || result_type == STRING_RESULT))
+ {
+ if (my_b_write(&cache,(byte*) exchange->enclosed->ptr(),
+ exchange->enclosed->length()))
+ goto err;
+ }
+ if (!res)
+ { // NULL
+ if (!fixed_row_size)
+ {
+ if (escape_char != -1) // Use \N syntax
+ {
+ null_buff[0]=escape_char;
+ null_buff[1]='N';
+ if (my_b_write(&cache,(byte*) null_buff,2))
+ goto err;
+ }
+ else if (my_b_write(&cache,(byte*) "NULL",4))
+ goto err;
+ }
+ else
+ {
+ used_length=0; // Fill with space
+ }
+ }
+ else
+ {
+ if (fixed_row_size)
+ used_length=min(res->length(),item->max_length);
+ else
+ used_length=res->length();
+ if (result_type == STRING_RESULT && escape_char != -1)
+ {
+ char *pos,*start,*end;
+
+ for (start=pos=(char*) res->ptr(),end=pos+used_length ;
+ pos != end ;
+ pos++)
+ {
+#ifdef USE_MB
+ if (use_mb(default_charset_info))
+ {
+ int l;
+ if ((l=my_ismbchar(default_charset_info, pos, end)))
+ {
+ pos += l-1;
+ continue;
+ }
+ }
+#endif
+ if ((int) *pos == escape_char || (int) *pos == field_sep_char ||
+ (int) *pos == line_sep_char || !*pos)
+ {
+ char tmp_buff[2];
+ tmp_buff[0]= escape_char;
+ tmp_buff[1]= *pos ? *pos : '0';
+ if (my_b_write(&cache,(byte*) start,(uint) (pos-start)) ||
+ my_b_write(&cache,(byte*) tmp_buff,2))
+ goto err;
+ start=pos+1;
+ }
+ }
+ if (my_b_write(&cache,(byte*) start,(uint) (pos-start)))
+ goto err;
+ }
+ else if (my_b_write(&cache,(byte*) res->ptr(),used_length))
+ goto err;
+ }
+ if (fixed_row_size)
+ { // Fill with space
+ if (item->max_length > used_length)
+ {
+ /* QQ: Fix by adding a my_b_fill() function */
+ if (!space_inited)
+ {
+ space_inited=1;
+ bfill(space,sizeof(space),' ');
+ }
+ uint length=item->max_length-used_length;
+ for ( ; length > sizeof(space) ; length-=sizeof(space))
+ {
+ if (my_b_write(&cache,(byte*) space,sizeof(space)))
+ goto err;
+ }
+ if (my_b_write(&cache,(byte*) space,length))
+ goto err;
+ }
+ }
+ buff_ptr=buff; // Place separators here
+ if (res && (!exchange->opt_enclosed || result_type == STRING_RESULT))
+ {
+ memcpy(buff_ptr,exchange->enclosed->ptr(),exchange->enclosed->length());
+ buff_ptr+=exchange->enclosed->length();
+ }
+ if (--items_left)
+ {
+ memcpy(buff_ptr,exchange->field_term->ptr(),field_term_length);
+ buff_ptr+=field_term_length;
+ }
+ if (my_b_write(&cache,(byte*) buff,(uint) (buff_ptr-buff)))
+ goto err;
+ }
+ if (my_b_write(&cache,(byte*) exchange->line_term->ptr(),
+ exchange->line_term->length()))
+ goto err;
+ DBUG_RETURN(0);
+err:
+ DBUG_RETURN(1);
+}
+
+
+void select_export::send_error(uint errcode,const char *err)
+{
+ ::send_error(&thd->net,errcode,err);
+ (void) end_io_cache(&cache);
+ (void) my_close(file,MYF(0));
+ file= -1;
+}
+
+
+bool select_export::send_eof()
+{
+ int error=test(end_io_cache(&cache));
+ if (my_close(file,MYF(MY_WME)))
+ error=1;
+ if (error)
+ ::send_error(&thd->net);
+ else
+ ::send_ok(&thd->net,row_count);
+ file= -1;
+ return error;
+}
+
+
+/***************************************************************************
+** Dump of select to a binary file
+***************************************************************************/
+
+
+select_dump::~select_dump()
+{
+ if (file >= 0)
+ { // This only happens in case of error
+ (void) end_io_cache(&cache);
+ (void) my_close(file,MYF(0));
+ file= -1;
+ }
+}
+
+int
+select_dump::prepare(List<Item> &list __attribute__((unused)))
+{
+ uint option=4;
+#ifdef DONT_ALLOW_FULL_LOAD_DATA_PATHS
+ option|=1; // Force use of db directory
+#endif
+ (void) fn_format(path,exchange->file_name, thd->db ? thd->db : "", "",
+ option);
+ if (!access(path,F_OK))
+ {
+ my_error(ER_FILE_EXISTS_ERROR,MYF(0),exchange->file_name);
+ return 1;
+ }
+ /* Create the file world readable */
+ if ((file=my_create(path, 0666, O_WRONLY, MYF(MY_WME))) < 0)
+ return 1;
+#ifdef HAVE_FCHMOD
+ (void) fchmod(file,0666); // Because of umask()
+#else
+ (void) chmod(path,0666);
+#endif
+ if (init_io_cache(&cache,file,0L,WRITE_CACHE,0L,1,MYF(MY_WME)))
+ {
+ my_close(file,MYF(0));
+ my_delete(path,MYF(0));
+ file= -1;
+ return 1;
+ }
+ return 0;
+}
+
+
+bool select_dump::send_data(List<Item> &items)
+{
+ List_iterator<Item> li(items);
+ char buff[MAX_FIELD_WIDTH];
+ String tmp(buff,sizeof(buff)),*res;
+ tmp.length(0);
+ Item *item;
+ DBUG_ENTER("send_data");
+
+ if (thd->offset_limit)
+ { // using limit offset,count
+ thd->offset_limit--;
+ DBUG_RETURN(0);
+ }
+ if (row_count++ > 1)
+ {
+ my_error(ER_TOO_MANY_ROWS,MYF(0));
+ goto err;
+ }
+ while ((item=li++))
+ {
+ Item_result result_type=item->result_type();
+ res=item->str_result(&tmp);
+ if (!res)
+ {
+ if (my_b_write(&cache,(byte*) "",1)) goto err; // NULL
+ }
+ else if (my_b_write(&cache,(byte*) res->ptr(),res->length()))
+ {
+ my_error(ER_ERROR_ON_WRITE,MYF(0), path, my_errno);
+ goto err;
+ }
+ }
+ DBUG_RETURN(0);
+err:
+ DBUG_RETURN(1);
+}
+
+
+void select_dump::send_error(uint errcode,const char *err)
+{
+ ::send_error(&thd->net,errcode,err);
+ (void) end_io_cache(&cache);
+ (void) my_close(file,MYF(0));
+ (void) my_delete(path,MYF(0)); // Delete file on error
+ file= -1;
+}
+
+
+bool select_dump::send_eof()
+{
+ int error=test(end_io_cache(&cache));
+ if (my_close(file,MYF(MY_WME)))
+ error=1;
+ if (error)
+ ::send_error(&thd->net);
+ else
+ ::send_ok(&thd->net,row_count);
+ file= -1;
+ return error;
+}
diff --git a/sql/sql_class.h b/sql/sql_class.h
new file mode 100644
index 00000000000..2ea18751dea
--- /dev/null
+++ b/sql/sql_class.h
@@ -0,0 +1,472 @@
+/* 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 in mysql */
+
+#ifdef __GNUC__
+#pragma interface /* gcc class implementation */
+#endif
+
+
+class Query_log_event;
+class Load_log_event;
+
+
+enum enum_duplicates { DUP_ERROR, DUP_REPLACE, DUP_IGNORE };
+enum enum_log_type { LOG_CLOSED, LOG_NORMAL, LOG_NEW, LOG_BIN };
+
+// log info errors
+#define LOG_INFO_EOF -1
+#define LOG_INFO_IO -2
+#define LOG_INFO_INVALID -3
+#define LOG_INFO_SEEK -4
+
+typedef struct st_log_info
+{
+ char log_file_name[FN_REFLEN];
+ my_off_t index_file_offset;
+ my_off_t pos;
+} LOG_INFO;
+
+typedef struct st_master_info
+{
+ char log_file_name[FN_REFLEN];
+ ulong pos;
+ FILE* file; // we keep the file open, so we need to remember the file pointer
+
+ // 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];
+ uint port;
+ uint connect_retry;
+ pthread_mutex_t lock;
+ bool inited;
+
+ st_master_info():inited(0) { host[0] = 0; user[0] = 0; password[0] = 0;}
+ inline void inc_pos(ulong val)
+ {
+ pthread_mutex_lock(&lock);
+ pos += val;
+ 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(ulong& var)
+ {
+ pthread_mutex_lock(&lock);
+ var = pos;
+ pthread_mutex_unlock(&lock);
+ }
+} MASTER_INFO;
+
+class MYSQL_LOG {
+ public:
+ private:
+ pthread_mutex_t LOCK_log, LOCK_index;
+ FILE *file, *index_file;
+ time_t last_time,query_start;
+ char *name;
+ enum_log_type log_type;
+ char time_buff[20],db[NAME_LEN+1];
+ char log_file_name[FN_REFLEN],index_file_name[FN_REFLEN];
+ bool write_error,inited;
+
+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 open(const char *log_name,enum_log_type log_type,
+ const char *new_name=0);
+ void new_file(void);
+ void write(enum enum_server_command command,const char *format,...);
+ void write(const char *query, uint query_length, ulong time_to_do_query=0);
+ void write(Query_log_event* event_info); // binary log write
+ void write(Load_log_event* event_info);
+
+ 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);
+ void flush(void);
+ void close(bool exiting = 0); // if we are exiting, we also want to close the
+ // index file
+
+ // 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 get_current_log(LOG_INFO* linfo);
+
+ bool is_open() { return log_type != LOG_CLOSED; }
+ char* get_index_fname() { return index_file_name;}
+ char* get_log_fname() { return log_file_name; }
+};
+
+/* character conversion tables */
+
+class CONVERT
+{
+ const uchar *from_map,*to_map;
+ 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) {}
+ 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);
+};
+
+typedef struct st_copy_info {
+ ha_rows records;
+ ha_rows deleted;
+ ha_rows copied;
+ ha_rows error;
+ enum enum_duplicates handle_duplicates;
+ int escape_char;
+} COPY_INFO;
+
+
+class key_part_spec :public Sql_alloc {
+public:
+ const char *field_name;
+ uint length;
+ key_part_spec(const char *name,uint len=0) :field_name(name), length(len) {}
+};
+
+
+class Alter_drop :public Sql_alloc {
+public:
+ enum drop_type {KEY, COLUMN };
+ const char *name;
+ enum drop_type type;
+ Alter_drop(enum drop_type par_type,const char *par_name)
+ :name(par_name), type(par_type) {}
+};
+
+
+class Alter_column :public Sql_alloc {
+public:
+ const char *name;
+ Item *def;
+ Alter_column(const char *par_name,Item *literal)
+ :name(par_name), def(literal) {}
+};
+
+
+class Key :public Sql_alloc {
+public:
+ enum Keytype { PRIMARY, UNIQUE, MULTIPLE, FULLTEXT };
+ enum Keytype type;
+ 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) {}
+ ~Key() {}
+ const char *name() { return Name; }
+};
+
+
+typedef struct st_mysql_lock
+{
+ TABLE **table;
+ uint table_count,lock_count;
+ THR_LOCK_DATA **locks;
+} MYSQL_LOCK;
+
+
+class LEX_COLUMN : public Sql_alloc
+{
+public:
+ String column;
+ uint rights;
+ LEX_COLUMN (const String& x,const uint& y ): column (x),rights (y) {}
+};
+
+#include "sql_lex.h" /* Must be here */
+
+// needed to be able to have an I_List of char* strings.in mysqld.cc where we cannot use String
+// because it is Sql_alloc'ed
+class i_string: public ilink
+{
+public:
+ char* ptr;
+ i_string():ptr(0) { }
+ i_string(char* s) : ptr(s) {}
+};
+
+/****************************************************************************
+** every connection is handle by a thread with a THD
+****************************************************************************/
+
+class delayed_insert;
+
+class THD :public ilink {
+public:
+ NET net;
+ LEX lex;
+ MEM_ROOT alloc;
+ HASH user_vars;
+ String packet; /* Room for 1 row */
+ struct sockaddr_in remote;
+ struct rand_struct rand;
+ char *query,*thread_stack;
+ char *host,*user,*priv_user,*db,*ip;
+ const char *proc_info;
+ uint client_capabilities,max_packet_length;
+ uint master_access,db_access;
+ TABLE *open_tables,*temporary_tables;
+ MYSQL_LOCK *lock,*locked_tables;
+ ULL *ull;
+ struct st_my_thread_var *mysys_var;
+ enum enum_server_command command;
+ 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_t connect_time,thr_create_time; // track down slow pthread_create
+ thr_lock_type update_lock_default;
+ delayed_insert *di;
+ struct st_transactions {
+ void *bdb_tid;
+ uint bdb_lock_count;
+ } transaction;
+ Item *free_list;
+ CONVERT *convert_set;
+ Field *dupp_field;
+#ifndef __WIN__
+ sigset_t signals,block_signals;
+#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;
+ ulong query_id,version, inactive_timeout,options,thread_id;
+ long dbug_thread_id;
+ pthread_t real_id;
+ uint current_tablenr,tmp_table,cond_count,col_access,query_length;
+ uint server_status;
+ char scramble[9];
+ 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,user_time;
+ bool volatile killed,bootstrap;
+ bool system_thread,in_lock_tables,global_read_lock;
+ bool query_error;
+ THD();
+ ~THD();
+ bool store_globals();
+ inline time_t query_start() { query_start_used=1; return start_time; }
+ inline void set_time() { if (!user_time) time(&start_time); }
+ inline void set_time(time_t t) { start_time=t; user_time=1; }
+ inline void insert_id(ulonglong id)
+ { last_insert_id=id; insert_id_used=1; }
+ inline ulonglong insert_id(void)
+ {
+ if (!last_insert_id_used)
+ {
+ last_insert_id_used=1;
+ current_insert_id=last_insert_id;
+ }
+ return last_insert_id;
+ }
+};
+
+
+class sql_exchange :public Sql_alloc
+{
+public:
+ char *file_name;
+ String *field_term,*enclosed,*line_term,*line_start,*escaped;
+ bool opt_enclosed;
+ bool dumpfile;
+ uint skip_lines;
+ sql_exchange(char *name,bool dumpfile_flag);
+ ~sql_exchange() {}
+};
+
+#include "log_event.h"
+
+/*
+** This is used to get result from a select
+*/
+
+class select_result :public Sql_alloc {
+protected:
+ THD *thd;
+public:
+ select_result();
+ virtual ~select_result() {};
+ 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 send_eof()=0;
+ virtual void abort() {}
+};
+
+
+class select_send :public select_result {
+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();
+};
+
+
+class select_export :public select_result {
+ sql_exchange *exchange;
+ File file;
+ IO_CACHE cache;
+ ha_rows row_count;
+ uint field_term_length;
+ int field_sep_char,escape_char,line_sep_char;
+ bool fixed_row_size;
+public:
+ select_export(sql_exchange *ex) :exchange(ex),file(-1),row_count(0L) {}
+ ~select_export();
+ int prepare(List<Item> &list);
+ 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_dump :public select_result {
+ sql_exchange *exchange;
+ File file;
+ IO_CACHE cache;
+ ha_rows row_count;
+ char path[FN_REFLEN];
+public:
+ select_dump(sql_exchange *ex) :exchange(ex),file(-1),row_count(0L)
+ { path[0]=0; }
+ ~select_dump();
+ int prepare(List<Item> &list);
+ 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_insert :public select_result {
+ protected:
+ 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;
+ }
+ ~select_insert();
+ int prepare(List<Item> &list);
+ 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;
+ const char *name;
+ List<create_field> *extra_fields;
+ List<Key> *keys;
+ HA_CREATE_INFO *create_info;
+ MYSQL_LOCK *lock;
+ Field **field;
+public:
+ select_create (const char *db_name, const char *table_name,
+ HA_CREATE_INFO *create_info_par,
+ List<create_field> &fields_par,
+ List<Key> &keys_par,
+ List<Item> &select_fields,enum_duplicates duplic)
+ :select_insert (NULL, &select_fields, duplic), db(db_name),
+ name(table_name), extra_fields(&fields_par),keys(&keys_par),
+ create_info(create_info_par),
+ lock(0)
+ {}
+ int prepare(List<Item> &list);
+ bool send_data(List<Item> &values);
+ bool send_eof();
+ void abort();
+};
+
+/* Structs used when sorting */
+
+typedef struct st_sort_field {
+ Field *field; /* Field to sort */
+ Item *item; /* Item if not sorting fields */
+ uint length; /* Length of sort field */
+ my_bool reverse; /* if descending sort */
+ Item_result result_type; /* Type of item */
+} SORT_FIELD;
+
+
+typedef struct st_sort_buffer {
+ uint index; /* 0 or 1 */
+ uint sort_orders;
+ uint change_pos; /* If sort-fields changed */
+ char **buff;
+ SORT_FIELD *sortorder;
+} SORT_BUFFER;
+
+
+/* Structure for db & table in sql_yacc */
+
+class Table_ident :public Sql_alloc {
+ public:
+ LEX_STRING db;
+ LEX_STRING table;
+ inline Table_ident(LEX_STRING db_arg,LEX_STRING table_arg,bool force)
+ :table(table_arg)
+ {
+ if (!force && (current_thd->client_capabilities & CLIENT_NO_SCHEMA))
+ db.str=0;
+ else
+ db= db_arg;
+ }
+ inline Table_ident(LEX_STRING table_arg) :table(table_arg) {db.str=0;}
+ inline void change_db(char *db_name)
+ { db.str= db_name; db.length=strlen(db_name); }
+};
+
+// this is needed for user_vars hash
+class user_var_entry
+{
+ public:
+ LEX_STRING name;
+ char *value;
+ ulong length;
+ Item_result type;
+};
+
diff --git a/sql/sql_crypt.cc b/sql/sql_crypt.cc
new file mode 100644
index 00000000000..371d63f8c73
--- /dev/null
+++ b/sql/sql_crypt.cc
@@ -0,0 +1,82 @@
+/* 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 */
+
+
+
+/*
+ Functions to handle the encode() and decode() functions
+ The strongness of this crypt is large based on how good the random
+ generator is. It should be ok for short strings, but for communication one
+ needs something like 'ssh'.
+*/
+
+#ifdef __GNUC__
+#pragma implementation // gcc: Class implementation
+#endif
+
+#include "mysql_priv.h"
+
+SQL_CRYPT::SQL_CRYPT(const char *password)
+{
+ ulong rand_nr[2];
+ hash_password(rand_nr,password);
+ crypt_init(rand_nr);
+}
+
+void SQL_CRYPT::crypt_init(ulong *rand_nr)
+{
+ uint i;
+ randominit(&rand,rand_nr[0],rand_nr[1]);
+
+ for (i=0 ; i<=255; i++)
+ decode_buff[i]= (char) i;
+
+ for (i=0 ; i<= 255 ; i++)
+ {
+ int idx= (uint) (rnd(&rand)*255.0);
+ char a= decode_buff[idx];
+ decode_buff[idx]= decode_buff[i];
+ decode_buff[+i]=a;
+ }
+ for (i=0 ; i <= 255 ; i++)
+ encode_buff[(unsigned char) decode_buff[i]]=i;
+ org_rand=rand;
+ shift=0;
+}
+
+
+void SQL_CRYPT::encode(char *str,uint length)
+{
+ for (uint i=0; i < length; i++)
+ {
+ shift^=(uint) (rnd(&rand)*255.0);
+ uint idx= (uint) (uchar) str[0];
+ *str++ = (char) ((uchar) encode_buff[idx] ^ shift);
+ shift^= idx;
+ }
+}
+
+
+void SQL_CRYPT::decode(char *str,uint length)
+{
+ for (uint i=0; i < length; i++)
+ {
+ shift^=(uint) (rnd(&rand)*255.0);
+ uint idx= (uint) ((unsigned char) str[0] ^ shift);
+ *str = decode_buff[idx];
+ shift^= (uint) (uchar) *str++;
+ }
+}
diff --git a/sql/sql_crypt.h b/sql/sql_crypt.h
new file mode 100644
index 00000000000..b3a9d54133f
--- /dev/null
+++ b/sql/sql_crypt.h
@@ -0,0 +1,38 @@
+/* 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 __GNUC__
+#pragma interface /* gcc class implementation */
+#endif
+
+class SQL_CRYPT :public Sql_alloc
+{
+ struct rand_struct rand,org_rand;
+ char decode_buff[256],encode_buff[256];
+ uint shift;
+ void crypt_init(ulong *seed);
+ public:
+ SQL_CRYPT(const char *seed);
+ SQL_CRYPT(ulong *seed)
+ {
+ crypt_init(seed);
+ }
+ ~SQL_CRYPT() {}
+ void init() { shift=0; rand=org_rand; }
+ void encode(char *str, uint length);
+ void decode(char *str, uint length);
+};
diff --git a/sql/sql_db.cc b/sql/sql_db.cc
new file mode 100644
index 00000000000..33c8ee54037
--- /dev/null
+++ b/sql/sql_db.cc
@@ -0,0 +1,300 @@
+/* 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 */
+
+
+/* create and drop of databases */
+
+#include "mysql_priv.h"
+#include "sql_acl.h"
+#include <my_dir.h>
+#include <m_ctype.h>
+#ifdef __WIN__
+#include <direct.h>
+#endif
+
+static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *path,
+ uint level);
+
+void mysql_create_db(THD *thd, char *db, uint create_options)
+{
+ char path[FN_REFLEN+16];
+ MY_DIR *dirp;
+ long result=1;
+ DBUG_ENTER("mysql_create_db");
+
+ if (!stripp_sp(db) || strlen(db) > NAME_LEN || check_db_name(db))
+ {
+ net_printf(&thd->net,ER_WRONG_DB_NAME, db);
+ DBUG_VOID_RETURN;
+ }
+ VOID(pthread_mutex_lock(&LOCK_mysql_create_db));
+
+ /* Check directory */
+ (void)sprintf(path,"%s/%s", mysql_data_home, db);
+ unpack_dirname(path,path); // Convert if not unix
+ if ((dirp = my_dir(path,MYF(MY_DONT_SORT))))
+ {
+ my_dirend(dirp);
+ if (!(create_options & HA_LEX_CREATE_IF_NOT_EXISTS))
+ {
+ net_printf(&thd->net,ER_DB_CREATE_EXISTS,db);
+ goto exit;
+ }
+ result = 0;
+ }
+ else
+ {
+ 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);
+ goto exit;
+ }
+ }
+ if (!thd->query)
+ {
+ thd->query = path;
+ thd->query_length = (uint) (strxmov(path,"create database ", db, NullS)-
+ path);
+ }
+ {
+ mysql_update_log.write(thd->query, thd->query_length);
+ Query_log_event qinfo(thd, thd->query);
+ mysql_bin_log.write(&qinfo);
+ }
+ if (thd->query == path)
+ {
+ thd->query = 0; // just in case
+ thd->query_length = 0;
+ }
+ send_ok(&thd->net, result);
+
+exit:
+ VOID(pthread_mutex_unlock(&LOCK_mysql_create_db));
+ DBUG_VOID_RETURN;
+}
+
+const char *del_exts[]=
+{".frm",".ISM",".ISD",".ISM",".HSH",".DAT",".MRG",".PSM",".MYI",".MYD", ".db",
+ NullS};
+static TYPELIB deletable_extentions=
+{array_elements(del_exts)-1,"del_exts", del_exts};
+
+
+void mysql_rm_db(THD *thd,char *db,bool if_exists)
+{
+ long deleted=0;
+ char path[FN_REFLEN+16];
+ MY_DIR *dirp;
+ DBUG_ENTER("mysql_rm_db");
+
+ if (!stripp_sp(db) || strlen(db) > NAME_LEN || check_db_name(db))
+ {
+ net_printf(&thd->net,ER_WRONG_DB_NAME, db);
+ DBUG_VOID_RETURN;
+ }
+
+ VOID(pthread_mutex_lock(&LOCK_mysql_create_db));
+ VOID(pthread_mutex_lock(&LOCK_open));
+
+ (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 (!if_exists)
+ net_printf(&thd->net,ER_DB_DROP_EXISTS,db);
+ else
+ send_ok(&thd->net,0);
+ goto exit;
+ }
+ remove_db_from_cache(db);
+
+ if ((deleted=mysql_rm_known_files(thd, dirp, path,0)) >= 0)
+ {
+ if (!thd->query)
+ {
+ thd->query = path;
+ thd->query_length = (uint) (strxmov(path,"drop database ", db, NullS)-
+ path);
+ }
+ mysql_update_log.write(thd->query, thd->query_length);
+ Query_log_event qinfo(thd, thd->query);
+ mysql_bin_log.write(&qinfo);
+ if (thd->query == path)
+ {
+ thd->query = 0; // just in case
+ thd->query_length = 0;
+ }
+ send_ok(&thd->net,(ulong) deleted);
+ }
+
+exit:
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ VOID(pthread_mutex_unlock(&LOCK_mysql_create_db));
+ DBUG_VOID_RETURN;
+}
+
+/*
+ Removes files with known extensions plus all found subdirectories that
+ are 2 digits (raid directories).
+*/
+
+static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *path,
+ uint level)
+{
+ long deleted=0;
+ ulong found_other_files=0;
+ char filePath[FN_REFLEN];
+ DBUG_ENTER("mysql_rm_known_files");
+ DBUG_PRINT("enter",("path: %s", path));
+ /* remove all files with known extensions */
+
+ for (uint idx=2 ;
+ idx < (uint) dirp->number_off_files && !thd->killed ;
+ idx++)
+ {
+ FILEINFO *file=dirp->dir_entry+idx;
+ DBUG_PRINT("info",("Examining: %s", file->name));
+
+ /* Check if file is a raid directory */
+ if (isdigit(file->name[0]) && isdigit(file->name[1]) &&
+ !file->name[2] && !level)
+ {
+ char newpath[FN_REFLEN];
+ MY_DIR *new_dirp;
+ strxmov(newpath,path,"/",file->name,NullS);
+ 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)
+ {
+ my_dirend(dirp);
+ DBUG_RETURN(-1);
+ }
+ }
+ continue;
+ }
+ if (find_type(fn_ext(file->name),&deletable_extentions,1) <= 0)
+ {
+ found_other_files++;
+ continue;
+ }
+ strxmov(filePath,path,"/",file->name,NullS);
+ if (my_delete(filePath,MYF(MY_WME)))
+ {
+ net_printf(&thd->net,ER_DB_DROP_DELETE,filePath,my_error);
+ my_dirend(dirp);
+ DBUG_RETURN(-1);
+ }
+ deleted++;
+ }
+
+ my_dirend(dirp);
+
+ if (thd->killed)
+ {
+ send_error(&thd->net,ER_SERVER_SHUTDOWN);
+ DBUG_RETURN(-1);
+ }
+
+ /*
+ If the directory is a symbolic link, remove the link first, then
+ remove the directory the symbolic link pointed at
+ */
+ if (!found_other_files)
+ {
+#ifdef HAVE_READLINK
+ int linkcount = readlink(path,filePath,sizeof(filePath)-1);
+ if (linkcount > 0) // If the path was a symbolic link
+ {
+ *(filePath + linkcount) = '\0';
+ if (my_delete(path,MYF(!level ? MY_WME : 0)))
+ {
+ /* Don't give errors if we can't delete 'RAID' directory */
+ if (level)
+ DBUG_RETURN(deleted);
+ send_error(&thd->net);
+ DBUG_RETURN(-1);
+ }
+ path=filePath;
+ }
+#endif
+ /* 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);
+ DBUG_RETURN(-1);
+ }
+ }
+ DBUG_RETURN(deleted);
+}
+
+
+bool mysql_change_db(THD *thd,const char *name)
+{
+ int length;
+ char *dbname=my_strdup((char*) name,MYF(MY_WME));
+ char path[FN_REFLEN];
+ uint db_access;
+ DBUG_ENTER("mysql_change_db");
+
+ if (!dbname || !(length=stripp_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)
+ {
+ net_printf(&thd->net,ER_WRONG_DB_NAME, dbname);
+ DBUG_RETURN(1);
+ }
+ DBUG_PRINT("general",("Use database: %s", dbname));
+ if (test_all_bits(thd->master_access,DB_ACLS))
+ db_access=DB_ACLS;
+ else
+ db_access= (acl_get(thd->host,thd->ip,(char*) &thd->remote.sin_addr,
+ thd->priv_user,dbname) |
+ thd->master_access);
+ if (!(db_access & DB_ACLS) && (!grant_option || check_grant_db(thd,dbname)))
+ {
+ net_printf(&thd->net,ER_DBACCESS_DENIED_ERROR,
+ thd->priv_user,
+ thd->host ? thd->host : thd->ip ? thd->ip : "unknown",
+ dbname);
+ mysql_log.write(COM_INIT_DB,ER(ER_DBACCESS_DENIED_ERROR),
+ thd->priv_user,
+ thd->host ? thd->host : thd->ip ? thd->ip : "unknown",
+ dbname);
+ my_free(dbname,MYF(0));
+ DBUG_RETURN(1);
+ }
+
+ (void) sprintf(path,"%s/%s",mysql_data_home,dbname);
+ unpack_dirname(path,path); // Convert if not unix
+ if (access(path,F_OK))
+ {
+ net_printf(&thd->net,ER_BAD_DB_ERROR,dbname);
+ my_free(dbname,MYF(0));
+ DBUG_RETURN(1);
+ }
+ send_ok(&thd->net);
+ x_free(thd->db);
+ thd->db=dbname;
+ thd->db_access=db_access;
+ DBUG_RETURN(0);
+}
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
new file mode 100644
index 00000000000..5d21a089283
--- /dev/null
+++ b/sql/sql_delete.cc
@@ -0,0 +1,213 @@
+/* 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 */
+
+
+/* Delete of records */
+
+#include "mysql_priv.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
+*/
+
+static 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";
+ /* 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)
+ {
+ send_ok(&thd->net); // This should return record count
+ mysql_update_log.write(thd->query,thd->query_length);
+ Query_log_event qinfo(thd, thd->query);
+ mysql_bin_log.write(&qinfo);
+ }
+ DBUG_RETURN(error ? -1 : 0);
+}
+
+
+int mysql_delete(THD *thd,TABLE_LIST *table_list,COND *conds,ha_rows limit,
+ thr_lock_type lock_type)
+{
+ int error;
+ TABLE *table;
+ SQL_SELECT *select;
+ READ_RECORD info;
+ bool using_limit=limit != HA_POS_ERROR;
+ bool use_generate_table;
+ DBUG_ENTER("mysql_delete");
+
+ if (!table_list->db)
+ table_list->db=thd->db;
+ if ((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_AUTO_COMMIT));
+ 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)))
+ DBUG_RETURN(-1);
+ 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))
+ DBUG_RETURN(-1);
+
+ 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 & SQL_SAFE_UPDATES),
+ limit))
+ {
+ delete select;
+ send_ok(&thd->net,0L);
+ DBUG_RETURN(0);
+ }
+
+ /* If running in safe sql mode, don't allow updates without keys */
+ if ((thd->options & OPTION_SAFE_UPDATES) && !table->quick_keys &&
+ limit == HA_POS_ERROR)
+ {
+ delete select;
+ send_error(&thd->net,ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE);
+ DBUG_RETURN(1);
+ }
+
+ (void) table->file->extra(HA_EXTRA_NO_READCHECK);
+ init_read_record(&info,thd,table,select,1,1);
+ ulong deleted=0L;
+ thd->proc_info="updating";
+ while (!(error=info.read_record(&info)) && !thd->killed)
+ {
+ if (!(select && select->skipp_record()))
+ {
+ if (!(error=table->file->delete_row(table->record[0])))
+ {
+ deleted++;
+ if (!--limit && using_limit)
+ {
+ error= -1;
+ break;
+ }
+ }
+ else
+ {
+ table->file->print_error(error,MYF(0));
+ error=0;
+ break;
+ }
+ }
+ }
+ thd->proc_info="end";
+ end_read_record(&info);
+ VOID(table->file->extra(HA_EXTRA_READCHECK));
+ if (deleted)
+ {
+ mysql_update_log.write(thd->query, thd->query_length);
+ Query_log_event qinfo(thd, thd->query);
+ mysql_bin_log.write(&qinfo);
+ }
+ if (ha_autocommit_or_rollback(thd,error >= 0))
+ error=1;
+ if (thd->lock)
+ {
+ mysql_unlock_tables(thd, thd->lock);
+ thd->lock=0;
+ }
+ delete select;
+ if (error >= 0) // Fatal error
+ send_error(&thd->net,thd->killed ? ER_SERVER_SHUTDOWN: 0);
+ else
+ {
+ send_ok(&thd->net,deleted);
+ DBUG_PRINT("info",("%d records deleted",deleted));
+ }
+ DBUG_RETURN(0);
+}
+
+
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
new file mode 100644
index 00000000000..a7a6a6c24c7
--- /dev/null
+++ b/sql/sql_insert.cc
@@ -0,0 +1,1346 @@
+/* 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 */
+
+
+/* Insert of records */
+
+#include "mysql_priv.h"
+#include "sql_acl.h"
+
+static int check_null_fields(THD *thd,TABLE *entry);
+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);
+static void unlink_blobs(register TABLE *table);
+
+/* Define to force use of my_malloc() if the allocated memory block is big */
+
+#ifndef HAVE_ALLOCA
+#define my_safe_alloca(size, min_length) my_alloca(size)
+#define my_safe_afree(ptr, size, min_length) my_afree(ptr)
+#else
+#define my_safe_alloca(size, min_length) ((size <= min_length) ? my_alloca(size) : my_malloc(size,MYF(0)))
+#define my_safe_afree(ptr, size, min_length) if (size > min_length) my_free(ptr,MYF(0))
+#endif
+
+
+/*
+ Check if insert fields are correct
+ Resets form->time_stamp if a timestamp value is set
+*/
+
+static int
+check_insert_fields(THD *thd,TABLE *table,List<Item> &fields,
+ List<Item> &values, ulong counter)
+{
+ if (fields.elements == 0 && values.elements != 0)
+ {
+ if (values.elements != table->fields)
+ {
+ my_printf_error(ER_WRONG_VALUE_COUNT_ON_ROW,
+ ER(ER_WRONG_VALUE_COUNT_ON_ROW),
+ MYF(0),counter);
+ return -1;
+ }
+ if (grant_option &&
+ check_grant_all_columns(thd,INSERT_ACL,table))
+ return -1;
+ table->time_stamp=0; // This should be saved
+ }
+ else
+ { // Part field list
+ if (fields.elements != values.elements)
+ {
+ my_printf_error(ER_WRONG_VALUE_COUNT_ON_ROW,
+ ER(ER_WRONG_VALUE_COUNT_ON_ROW),
+ MYF(0),counter);
+ return -1;
+ }
+ TABLE_LIST table_list;
+ bzero((char*) &table_list,sizeof(table_list));
+ table_list.name=table->table_name;
+ table_list.table=table;
+ table_list.grant=table->grant;
+
+ thd->dupp_field=0;
+ if (setup_fields(thd,&table_list,fields,1,0))
+ return -1;
+ if (thd->dupp_field)
+ {
+ my_error(ER_FIELD_SPECIFIED_TWICE,MYF(0), thd->dupp_field->field_name);
+ return -1;
+ }
+ 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
+ }
+ // 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)
+{
+ int error;
+ bool log_on= ((thd->options & OPTION_UPDATE_LOG) ||
+ !(thd->master_access & PROCESS_ACL));
+ 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_item *values;
+ char *query=thd->query;
+ DBUG_ENTER("mysql_insert");
+
+ if (lock_type == TL_WRITE_DELAYED &&
+ (specialflag & (SPECIAL_NO_NEW_FUNC | SPECIAL_SAFE_MODE)) ||
+ lock_type == TL_WRITE_CONCURRENT_INSERT && duplic == DUP_REPLACE)
+ lock_type=TL_WRITE;
+
+ if (lock_type == TL_WRITE_DELAYED)
+ {
+ if (thd->locked_tables)
+ {
+ if (find_locked_table(thd,
+ table_list->db ? table_list->db : thd->db,
+ table_list->real_name))
+ {
+ my_printf_error(ER_DELAYED_INSERT_TABLE_LOCKED,
+ ER(ER_DELAYED_INSERT_TABLE_LOCKED),
+ MYF(0), table_list->real_name);
+ DBUG_RETURN(-1);
+ }
+ }
+ if (!(table = delayed_get_table(thd,table_list)) && !thd->fatal_error)
+ table = open_ltable(thd,table_list,lock_type=thd->update_lock_default);
+ }
+ else
+ table = open_ltable(thd,table_list,lock_type);
+ if (!table)
+ DBUG_RETURN(-1);
+ thd->proc_info="init";
+ save_time_stamp=table->time_stamp;
+ values= its++;
+ if (check_insert_fields(thd,table,fields,*values,1) ||
+ setup_fields(thd,table_list,*values,0,0))
+ {
+ table->time_stamp=save_time_stamp;
+ goto abort;
+ }
+ value_count= values->elements;
+ while ((values = its++))
+ {
+ counter++;
+ if (values->elements != value_count)
+ {
+ 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;
+ goto abort;
+ }
+ }
+ its.rewind ();
+ /*
+ ** Fill in the given fields and dump it to the table file
+ */
+
+ info.records=info.deleted=info.copied=0;
+ info.handle_duplicates=duplic;
+ // Don't count warnings for simple inserts
+ if (values_list.elements > 1 || (thd->options & OPTION_WARNINGS))
+ thd->count_cuted_fields = 1;
+ thd->cuted_fields = 0L;
+ table->next_number_field=table->found_next_number_field;
+
+ error=0;
+ id=0;
+ thd->proc_info="update";
+ while ((values = its++))
+ {
+ if (fields.elements || !value_count)
+ {
+ restore_record(table,2); // Get empty record
+ if (fill_record(fields,*values) || check_null_fields(thd,table))
+ {
+ if (values_list.elements != 1)
+ {
+ info.records++;
+ continue;
+ }
+ error=1;
+ break;
+ }
+ }
+ else
+ {
+ table->record[0][0]=table->record[2][0]; // Fix delete marker
+ if (fill_record(table->field,*values))
+ {
+ if (values_list.elements != 1)
+ {
+ info.records++;
+ continue;
+ }
+ error=1;
+ break;
+ }
+ }
+ if (lock_type == TL_WRITE_DELAYED)
+ {
+ error=write_delayed(thd,table,duplic,query, thd->query_length, log_on);
+ query=0;
+ }
+ else
+ error=write_record(table,&info);
+ if (error)
+ break;
+ /*
+ If auto_increment values are used, save the first one
+ for LAST_INSERT_ID() and for the 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)
+ { // Get auto increment value
+ id= thd->last_insert_id;
+ }
+ }
+ if (lock_type == TL_WRITE_DELAYED)
+ {
+ id=0; // No auto_increment id
+ info.copied=values_list.elements;
+ end_delayed_insert(thd);
+ }
+ else
+ {
+ 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
+ if (info.copied || info.deleted)
+ {
+ mysql_update_log.write(thd->query, thd->query_length);
+ Query_log_event qinfo(thd, thd->query);
+ mysql_bin_log.write(&qinfo);
+ }
+ error=ha_autocommit_or_rollback(thd,error);
+ if (thd->lock)
+ {
+ mysql_unlock_tables(thd, thd->lock);
+ thd->lock=0;
+ }
+ }
+ 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 (error)
+ goto abort;
+
+ if (values_list.elements == 1 && (!(thd->options & OPTION_WARNINGS) ||
+ !thd->cuted_fields))
+ send_ok(&thd->net,info.copied+info.deleted,id);
+ else {
+ char buff[160];
+ if (duplic == DUP_IGNORE)
+ sprintf(buff,ER(ER_INSERT_INFO),info.records,info.records-info.copied,
+ thd->cuted_fields);
+ else
+ sprintf(buff,ER(ER_INSERT_INFO),info.records,info.deleted,
+ thd->cuted_fields);
+ ::send_ok(&thd->net,info.copied+info.deleted,0L,buff);
+ }
+ DBUG_RETURN(0);
+
+abort:
+ if (lock_type == TL_WRITE_DELAYED)
+ end_delayed_insert(thd);
+ DBUG_RETURN(-1);
+}
+
+
+ /* Check if there is more uniq keys after field */
+
+static int last_uniq_key(TABLE *table,uint keynr)
+{
+ while (++keynr < table->keys)
+ if (table->key_info[keynr].flags & HA_NOSAME)
+ return 0;
+ return 1;
+}
+
+
+/*
+** Write a record to table with optional deleting of conflicting records
+*/
+
+
+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)
+ 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 */
+ goto err;
+ }
+ if (table->file->option_flag() & 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 */
+ {
+ error=my_errno;
+ goto err;
+ }
+
+ if (!key)
+ {
+ if (!(key=(char*) my_safe_alloca(table->max_unique_length,
+ MAX_KEY_LENGTH)))
+ {
+ error=ENOMEM;
+ goto err;
+ }
+ }
+ key_copy((byte*) key,table,key_nr,0);
+ if ((error=(table->file->index_read_idx(table->record[1],key_nr,
+ (byte*) key,0,
+ HA_READ_KEY_EXACT))))
+ goto err;
+ }
+ if (last_uniq_key(table,key_nr))
+ {
+ if ((error=table->file->update_row(table->record[1],table->record[0])))
+ goto err;
+ info->deleted++;
+ break; /* Update logfile and count */
+ }
+ else if ((error=table->file->delete_row(table->record[1])))
+ goto err;
+ info->deleted++;
+ }
+ info->copied++;
+ }
+ else if ((error=table->file->write_row(table->record[0])))
+ {
+ if (info->handle_duplicates != DUP_IGNORE ||
+ (error != HA_ERR_FOUND_DUPP_KEY && error != HA_ERR_FOUND_DUPP_UNIQUE))
+ goto err;
+ }
+ else
+ info->copied++;
+ if (key)
+ my_safe_afree(key,table->max_unique_length,MAX_KEY_LENGTH);
+ return 0;
+
+err:
+ if (key)
+ my_afree(key);
+ 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.
+******************************************************************************/
+
+static int check_null_fields(THD *thd __attribute__((unused)),
+ TABLE *entry __attribute__((unused)))
+{
+#ifdef DONT_USE_DEFAULT_FIELDS
+ for (Field **field=entry->field ; *field ; field++)
+ {
+ if ((*field)->query_id != thd->query_id && !(*field)->maybe_null() &&
+ *field != entry->timestamp_field &&
+ *field != entry->next_number_field)
+ {
+ my_printf_error(ER_BAD_NULL_ERROR, ER(ER_BAD_NULL_ERROR),MYF(0),
+ (*field)->field_name);
+ return 1;
+ }
+ }
+#endif
+ return 0;
+}
+
+/*****************************************************************************
+** Handling of delayed inserts
+**
+** A thread is created for each table that one uses with the DELAYED
+** attribute.
+*****************************************************************************/
+
+class delayed_row :public ilink {
+public:
+ char *record,*query;
+ enum_duplicates dup;
+ time_t start_time;
+ bool query_start_used,last_insert_id_used,insert_id_used,log_query;
+ ulonglong last_insert_id;
+ ulong time_stamp;
+ uint query_length;
+
+ delayed_row(enum_duplicates dup_arg, bool log_query_arg)
+ :record(0),query(0),dup(dup_arg),log_query(log_query_arg) {}
+ ~delayed_row()
+ {
+ x_free(record);
+ }
+};
+
+
+class delayed_insert :public ilink {
+ uint locks_in_memory;
+public:
+ THD thd;
+ TABLE *table;
+ pthread_mutex_t mutex;
+ pthread_cond_t cond,cond_client;
+ volatile uint tables_in_use,stacked_inserts;
+ volatile bool status,dead;
+ COPY_INFO info;
+ I_List<delayed_row> rows;
+ uint group_count;
+ TABLE_LIST *table_list; // Argument
+
+ delayed_insert()
+ :locks_in_memory(0),
+ table(0),tables_in_use(0),stacked_inserts(0), status(0), dead(0),
+ group_count(0)
+ {
+ thd.user=thd.host=(char*) "";
+ thd.current_tablenr=0;
+ thd.version=refresh_version;
+ thd.command=COM_DELAYED_INSERT;
+
+ bzero((char*) &thd.net,sizeof(thd.net)); // Safety
+ thd.system_thread=1;
+ bzero((char*) &info,sizeof(info));
+ pthread_mutex_init(&mutex,NULL);
+ pthread_cond_init(&cond,NULL);
+ pthread_cond_init(&cond_client,NULL);
+ VOID(pthread_mutex_lock(&LOCK_thread_count));
+ delayed_insert_threads++;
+ VOID(pthread_mutex_unlock(&LOCK_thread_count));
+ }
+ ~delayed_insert()
+ {
+ /* The following is not really neaded, but just for safety */
+ delayed_row *row;
+ while ((row=rows.get()))
+ delete row;
+ pthread_mutex_destroy(&mutex);
+ if (table)
+ close_thread_tables(&thd);
+ VOID(pthread_mutex_lock(&LOCK_thread_count));
+ thd.unlink(); // Must be unlinked under lock
+ x_free(thd.query);
+ 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));
+ }
+
+ /* The following is for checking when we can delete ourselves */
+ inline void lock()
+ {
+ locks_in_memory++; // Assume LOCK_delay_insert
+ }
+ void unlock()
+ {
+ pthread_mutex_lock(&LOCK_delayed_insert);
+ if (!--locks_in_memory)
+ {
+ pthread_mutex_lock(&mutex);
+ if (thd.killed && ! stacked_inserts && ! tables_in_use)
+ {
+ pthread_cond_signal(&cond);
+ status=1;
+ }
+ pthread_mutex_unlock(&mutex);
+ }
+ pthread_mutex_unlock(&LOCK_delayed_insert);
+ }
+ inline uint lock_count() { return locks_in_memory; }
+
+ TABLE* get_local_table(THD* client_thd);
+ bool handle_inserts(void);
+};
+
+
+I_List<delayed_insert> delayed_threads;
+
+
+delayed_insert *find_handler(THD *thd, TABLE_LIST *table_list)
+{
+ thd->proc_info="waiting for delay_list";
+ pthread_mutex_lock(&LOCK_delayed_insert); // Protect master list
+ I_List_iterator<delayed_insert> it(delayed_threads);
+ delayed_insert *tmp;
+ while ((tmp=it++))
+ {
+ if (!strcmp(tmp->thd.db,table_list->db) &&
+ !strcmp(table_list->real_name,tmp->table->real_name))
+ {
+ tmp->lock();
+ break;
+ }
+ }
+ pthread_mutex_unlock(&LOCK_delayed_insert); // For unlink from list
+ return tmp;
+}
+
+
+static TABLE *delayed_get_table(THD *thd,TABLE_LIST *table_list)
+{
+ int error;
+ delayed_insert *tmp;
+ DBUG_ENTER("delayed_get_table");
+
+ if (!table_list->db)
+ table_list->db=thd->db;
+
+ /* no match; create a new thread to handle the table */
+ if (!(tmp=find_handler(thd,table_list)))
+ {
+ thd->proc_info="Creating delayed handler";
+ pthread_mutex_lock(&LOCK_delayed_create);
+ if (!(tmp=find_handler(thd,table_list))) // Was just created
+ {
+ if (!(tmp=new delayed_insert()))
+ {
+ thd->fatal_error=1;
+ my_error(ER_OUTOFMEMORY,MYF(0),sizeof(delayed_insert));
+ pthread_mutex_unlock(&LOCK_delayed_create);
+ DBUG_RETURN(0);
+ }
+ if (!(tmp->thd.db=my_strdup(table_list->db,MYF(MY_WME))) ||
+ !(tmp->thd.query=my_strdup(table_list->real_name,MYF(MY_FAE))))
+ {
+ delete tmp;
+ thd->fatal_error=1;
+ my_error(ER_OUT_OF_RESOURCES,MYF(0));
+ pthread_mutex_unlock(&LOCK_delayed_create);
+ DBUG_RETURN(0);
+ }
+ tmp->table_list=table_list; // Needed to open table
+ tmp->lock();
+ pthread_mutex_lock(&LOCK_thread_count);
+ thread_count++;
+ pthread_mutex_unlock(&LOCK_thread_count);
+ pthread_mutex_lock(&tmp->mutex);
+ if ((error=pthread_create(&tmp->thd.real_id,&connection_attrib,
+ handle_delayed_insert,(void*) tmp)))
+ {
+ DBUG_PRINT("error",
+ ("Can't create thread to handle delayed insert (error %d)",
+ error));
+ pthread_mutex_unlock(&tmp->mutex);
+ tmp->unlock();
+ delete tmp;
+ thd->fatal_error=1;
+ pthread_mutex_unlock(&LOCK_delayed_create);
+ net_printf(&thd->net,ER_CANT_CREATE_THREAD,error);
+ DBUG_RETURN(0);
+ }
+
+ /* Wait until table is open */
+ thd->proc_info="waiting for handler open";
+ while (!tmp->thd.killed && !tmp->table && !thd->killed)
+ {
+ pthread_cond_wait(&tmp->cond_client,&tmp->mutex);
+ }
+ pthread_mutex_unlock(&tmp->mutex);
+ thd->proc_info="got old table";
+ if (tmp->thd.killed)
+ {
+ if (tmp->thd.fatal_error)
+ {
+ /* 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;
+ }
+ tmp->unlock();
+ pthread_mutex_unlock(&LOCK_delayed_create);
+ DBUG_RETURN(0); // Continue with normal insert
+ }
+ if (thd->killed)
+ {
+ tmp->unlock();
+ pthread_mutex_unlock(&LOCK_delayed_create);
+ DBUG_RETURN(0);
+ }
+ }
+ pthread_mutex_unlock(&LOCK_delayed_create);
+ }
+
+ pthread_mutex_lock(&tmp->mutex);
+ TABLE *table=tmp->get_local_table(thd);
+ pthread_mutex_unlock(&tmp->mutex);
+ tmp->unlock();
+ if (table)
+ thd->di=tmp;
+ else if (tmp->thd.fatal_error)
+ thd->fatal_error=1;
+ DBUG_RETURN((table_list->table=table));
+}
+
+
+/*
+ As we can't let many threads modify the same TABLE structure, we create
+ an own structure for each tread. This includes a row buffer to save the
+ column values and new fields that points to the new row buffer.
+ The memory is allocated in the client thread and is freed automaticly.
+*/
+
+TABLE *delayed_insert::get_local_table(THD* client_thd)
+{
+ my_ptrdiff_t adjust_ptrs;
+ Field **field,**org_field;
+ TABLE *copy;
+
+ /* First request insert thread to get a lock */
+ status=1;
+ tables_in_use++;
+ if (!thd.lock) // Table is not locked
+ {
+ client_thd->proc_info="waiting for handler lock";
+ pthread_cond_signal(&cond); // Tell handler to lock table
+ while (!dead && !thd.lock && ! client_thd->killed)
+ {
+ pthread_cond_wait(&cond_client,&mutex);
+ }
+ client_thd->proc_info="got handler lock";
+ if (client_thd->killed)
+ goto error;
+ if (dead)
+ {
+ strmov(client_thd->net.last_error,thd.net.last_error);
+ client_thd->net.last_errno=thd.net.last_errno;
+ goto error;
+ }
+ }
+
+ client_thd->proc_info="allocating local table";
+ copy= (TABLE*) sql_alloc(sizeof(*copy)+
+ (table->fields+1)*sizeof(Field**)+
+ table->reclength);
+ if (!copy)
+ goto error;
+ *copy= *table;
+ bzero((char*) &copy->name_hash,sizeof(copy->name_hash)); // No name hashing
+ /* We don't need to change the file handler here */
+
+ field=copy->field=(Field**) (copy+1);
+ copy->record[0]=(byte*) (field+table->fields+1);
+ memcpy((char*) copy->record[0],(char*) table->record[0],table->reclength);
+
+ /* Make a copy of all fields */
+
+ adjust_ptrs=PTR_BYTE_DIFF(copy->record[0],table->record[0]);
+
+ for (org_field=table->field ; *org_field ; org_field++,field++)
+ {
+ if (!(*field= (*org_field)->new_field(copy)))
+ return 0;
+ (*field)->move_field(adjust_ptrs); // Point at copy->record[0]
+ }
+ *field=0;
+
+ /* Adjust timestamp */
+ if (table->timestamp_field)
+ {
+ /* Restore offset as this may have been reset in handle_inserts */
+ copy->time_stamp=table->timestamp_field->offset()+1;
+ copy->timestamp_field=
+ (Field_timestamp*) copy->field[table->timestamp_field_offset];
+ }
+
+ /* _rowid is not used with delayed insert */
+ copy->rowid_field=0;
+ return copy;
+
+ /* Got fatal error */
+ error:
+ tables_in_use--;
+ status=1;
+ pthread_cond_signal(&cond); // Inform thread about abort
+ return 0;
+}
+
+
+/* Put a question in queue */
+
+static int write_delayed(THD *thd,TABLE *table,enum_duplicates duplic,
+ char *query, uint query_length, bool log_on)
+{
+ delayed_row *row=0;
+ delayed_insert *di=thd->di;
+ DBUG_ENTER("write_delayed");
+
+ thd->proc_info="waiting for handler insert";
+ pthread_mutex_lock(&di->mutex);
+ while (di->stacked_inserts >= delayed_queue_size && !thd->killed)
+ pthread_cond_wait(&di->cond_client,&di->mutex);
+ thd->proc_info="storing row into queue";
+
+ if (thd->killed || !(row= new delayed_row(duplic, log_on)))
+ goto err;
+
+ if (!query)
+ query_length=0;
+ if (!(row->record= (char*) my_malloc(table->reclength+query_length+1,
+ MYF(MY_WME))))
+ goto err;
+ memcpy(row->record,table->record[0],table->reclength);
+ if (query_length)
+ {
+ row->query=row->record+table->reclength;
+ memcpy(row->query,query,query_length+1);
+ }
+ row->query_length= query_length;
+ row->start_time= thd->start_time;
+ row->query_start_used= thd->query_start_used;
+ row->last_insert_id_used= thd->last_insert_id_used;
+ row->insert_id_used= thd->insert_id_used;
+ row->last_insert_id= thd->last_insert_id;
+ row->time_stamp= table->time_stamp;
+
+ di->rows.push_back(row);
+ di->stacked_inserts++;
+ di->status=1;
+ if (table->blob_fields)
+ unlink_blobs(table);
+ pthread_cond_signal(&di->cond);
+
+ thread_safe_increment(delayed_rows_in_use,&LOCK_delayed_status);
+ pthread_mutex_unlock(&di->mutex);
+ DBUG_RETURN(0);
+
+ err:
+ delete row;
+ pthread_mutex_unlock(&di->mutex);
+ DBUG_RETURN(1);
+}
+
+
+static void end_delayed_insert(THD *thd)
+{
+ delayed_insert *di=thd->di;
+ pthread_mutex_lock(&di->mutex);
+ if (!--di->tables_in_use || di->thd.killed)
+ { // Unlock table
+ di->status=1;
+ pthread_cond_signal(&di->cond);
+ }
+ pthread_mutex_unlock(&di->mutex);
+}
+
+
+/* We kill all delayed threads when doing flush-tables */
+
+void kill_delayed_threads(void)
+{
+ VOID(pthread_mutex_lock(&LOCK_delayed_insert)); // For unlink from list
+
+ I_List_iterator<delayed_insert> it(delayed_threads);
+ delayed_insert *tmp;
+ while ((tmp=it++))
+ {
+ pthread_mutex_lock(&tmp->mutex);
+ tmp->thd.killed=1;
+ if (tmp->thd.mysys_var)
+ {
+ pthread_mutex_lock(&tmp->thd.mysys_var->mutex);
+ if (tmp->thd.mysys_var->current_mutex)
+ {
+ if (&tmp->mutex != tmp->thd.mysys_var->current_mutex)
+ pthread_mutex_lock(tmp->thd.mysys_var->current_mutex);
+ pthread_cond_broadcast(tmp->thd.mysys_var->current_cond);
+ if (&tmp->mutex != tmp->thd.mysys_var->current_mutex)
+ pthread_mutex_unlock(tmp->thd.mysys_var->current_mutex);
+ }
+ pthread_mutex_unlock(&tmp->thd.mysys_var->mutex);
+ }
+ pthread_mutex_unlock(&tmp->mutex);
+ }
+ VOID(pthread_mutex_unlock(&LOCK_delayed_insert)); // For unlink from list
+}
+
+
+/*
+ * Create a new delayed insert thread
+*/
+
+static pthread_handler_decl(handle_delayed_insert,arg)
+{
+ delayed_insert *di=(delayed_insert*) arg;
+ THD *thd= &di->thd;
+
+ pthread_detach_this_thread();
+ /* Add thread to THD list so that's it's visible in 'show processlist' */
+ pthread_mutex_lock(&LOCK_thread_count);
+ thd->thread_id=thread_id++;
+ threads.append(thd);
+ pthread_mutex_unlock(&LOCK_thread_count);
+
+ pthread_mutex_lock(&di->mutex);
+#ifndef __WIN__ /* Win32 calls this in pthread_create */
+ if (my_thread_init())
+ {
+ strmov(thd->net.last_error,ER(thd->net.last_errno=ER_OUT_OF_RESOURCES));
+ goto end;
+ }
+#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))
+ {
+ 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();
+#ifndef __WIN__
+ sigset_t set;
+ VOID(sigemptyset(&set)); // Get mask in use
+ VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals));
+#endif
+
+ /* open table */
+
+ if (!(di->table=open_ltable(thd,di->table_list,TL_WRITE_DELAYED)))
+ {
+ thd->fatal_error=1; // Abort waiting inserts
+ goto end;
+ }
+ di->table->copy_blobs=1;
+
+ /* One can now use this */
+ pthread_mutex_lock(&LOCK_delayed_insert);
+ delayed_threads.append(di);
+ pthread_mutex_unlock(&LOCK_delayed_insert);
+
+ /* Tell client that the thread is initialized */
+ pthread_cond_signal(&di->cond_client);
+
+ /* Now wait until we get an insert or lock to handle */
+ /* We will not abort as long as a client thread uses this thread */
+
+ for (;;)
+ {
+ if (thd->killed)
+ {
+ uint lock_count;
+ /*
+ Remove this from delay insert list so that no one can request a
+ table from this
+ */
+ pthread_mutex_unlock(&di->mutex);
+ pthread_mutex_lock(&LOCK_delayed_insert);
+ di->unlink();
+ lock_count=di->lock_count();
+ pthread_mutex_unlock(&LOCK_delayed_insert);
+ pthread_mutex_lock(&di->mutex);
+ if (!lock_count && !di->tables_in_use && !di->stacked_inserts)
+ break; // Time to die
+ }
+
+ 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
+
+ /* Information for pthread_kill */
+ pthread_mutex_lock(&di->thd.mysys_var->mutex);
+ di->thd.mysys_var->current_mutex= &di->mutex;
+ di->thd.mysys_var->current_cond= &di->cond;
+ pthread_mutex_unlock(&di->thd.mysys_var->mutex);
+ di->thd.proc_info=0;
+
+ for ( ; ;)
+ {
+ int error;
+#if (defined(HAVE_BROKEN_COND_TIMEDWAIT) || defined(HAVE_LINUXTHREADS))
+ error=pthread_cond_wait(&di->cond,&di->mutex);
+#else
+ error=pthread_cond_timedwait(&di->cond,&di->mutex,&abstime);
+#ifdef EXTRA_DEBUG
+ if (error && error != EINTR)
+ {
+ fprintf(stderr, "Got error %d from pthread_cond_timedwait\n",error);
+ DBUG_PRINT("error",("Got error %d from pthread_cond_timedwait",
+ error));
+ }
+#endif
+#endif
+ if (thd->killed || di->status)
+ break;
+ if (error == ETIME || error == ETIMEDOUT)
+ {
+ thd->killed=1;
+ break;
+ }
+ }
+ pthread_mutex_lock(&di->thd.mysys_var->mutex);
+ di->thd.mysys_var->current_mutex= 0;
+ di->thd.mysys_var->current_cond= 0;
+ pthread_mutex_unlock(&di->thd.mysys_var->mutex);
+ }
+
+ if (di->tables_in_use && ! thd->lock)
+ {
+ /* request for new delayed insert */
+ if (!(thd->lock=mysql_lock_tables(thd,&di->table,1)))
+ {
+ di->dead=thd->killed=1; // Fatal error
+ }
+ pthread_cond_broadcast(&di->cond_client);
+ }
+ if (di->stacked_inserts)
+ {
+ if (di->handle_inserts())
+ {
+ di->dead=thd->killed=1; // Some fatal error
+ }
+ }
+ di->status=0;
+ if (!di->stacked_inserts && !di->tables_in_use && thd->lock)
+ {
+ /* No one is doing a insert delayed;
+ Unlock it so that other threads can use it */
+ MYSQL_LOCK *lock=thd->lock;
+ thd->lock=0;
+ pthread_mutex_unlock(&di->mutex);
+ mysql_unlock_tables(thd, lock);
+ di->group_count=0;
+ pthread_mutex_lock(&di->mutex);
+ }
+ if (di->tables_in_use)
+ pthread_cond_broadcast(&di->cond_client); // If waiting clients
+ }
+
+end:
+ /*
+ di should be unlinked from the thread handler list and have no active
+ clients
+ */
+
+ close_thread_tables(thd); // Free the table
+ di->table=0;
+ di->dead=thd->killed=1; // If error
+ pthread_cond_broadcast(&di->cond_client); // Safety
+ pthread_mutex_unlock(&di->mutex);
+
+ pthread_mutex_lock(&LOCK_delayed_create); // Because of delayed_get_table
+ pthread_mutex_lock(&LOCK_delayed_insert);
+ delete di;
+ pthread_mutex_unlock(&LOCK_delayed_insert);
+ pthread_mutex_unlock(&LOCK_delayed_create);
+
+ my_thread_end();
+ pthread_exit(0);
+ DBUG_RETURN(0);
+}
+
+
+/* Remove pointers from temporary fields to allocated values */
+
+static void unlink_blobs(register TABLE *table)
+{
+ for (Field **ptr=table->field ; *ptr ; ptr++)
+ {
+ if ((*ptr)->flags & BLOB_FLAG)
+ ((Field_blob *) (*ptr))->clear_temporary();
+ }
+}
+
+/* Free blobs stored in current row */
+
+static void free_delayed_insert_blobs(register TABLE *table)
+{
+ for (Field **ptr=table->field ; *ptr ; ptr++)
+ {
+ if ((*ptr)->flags & BLOB_FLAG)
+ {
+ char *str;
+ ((Field_blob *) (*ptr))->get_ptr(&str);
+ my_free(str,MYF(MY_ALLOW_ZERO_PTR));
+ ((Field_blob *) (*ptr))->reset();
+ }
+ }
+}
+
+
+bool delayed_insert::handle_inserts(void)
+{
+ int error;
+ uint max_rows;
+ DBUG_ENTER("handle_inserts");
+
+ /* Allow client to insert new rows */
+ pthread_mutex_unlock(&mutex);
+
+ table->next_number_field=table->found_next_number_field;
+
+ thd.proc_info="upgrading lock";
+ if (thr_upgrade_write_delay_lock(*thd.lock->locks))
+ {
+ /* This can only happen if thread is killed by shutdown */
+ sql_print_error(ER(ER_DELAYED_CANT_CHANGE_LOCK),table->real_name);
+ goto err;
+ }
+
+ thd.proc_info="insert";
+ max_rows=delayed_insert_limit;
+ if (thd.killed || table->version != refresh_version)
+ {
+ thd.killed=1;
+ max_rows= ~0; // Do as much as possible
+ }
+
+ table->file->extra(HA_EXTRA_WRITE_CACHE);
+ pthread_mutex_lock(&mutex);
+ delayed_row *row;
+ while ((row=rows.get()))
+ {
+ stacked_inserts--;
+ pthread_mutex_unlock(&mutex);
+ memcpy(table->record[0],row->record,table->reclength);
+
+ thd.start_time=row->start_time;
+ thd.query_start_used=row->query_start_used;
+ thd.last_insert_id=row->last_insert_id;
+ thd.last_insert_id_used=row->last_insert_id_used;
+ thd.insert_id_used=row->insert_id_used;
+ table->time_stamp=row->time_stamp;
+
+ info.handle_duplicates= row->dup;
+ if (write_record(table,&info))
+ {
+ info.error++; // Ignore errors
+ pthread_mutex_lock(&LOCK_delayed_status);
+ delayed_insert_errors++;
+ pthread_mutex_unlock(&LOCK_delayed_status);
+ }
+ if (row->query && row->log_query)
+ {
+ mysql_update_log.write(row->query, row->query_length);
+ Query_log_event qinfo(&thd, row->query);
+ mysql_bin_log.write(&qinfo);
+ }
+ if (table->blob_fields)
+ free_delayed_insert_blobs(table);
+ thread_safe_sub(delayed_rows_in_use,1,&LOCK_delayed_status);
+ thread_safe_increment(delayed_insert_writes,&LOCK_delayed_status);
+ pthread_mutex_lock(&mutex);
+
+ delete row;
+ /* Let READ clients do something once in a while */
+ if (group_count++ == max_rows)
+ {
+ group_count=0;
+ if (stacked_inserts || tables_in_use) // Let these wait a while
+ {
+ if (tables_in_use)
+ pthread_cond_broadcast(&cond_client); // If waiting clients
+ thd.proc_info="reschedule";
+ pthread_mutex_unlock(&mutex);
+ if ((error=table->file->extra(HA_EXTRA_NO_CACHE)))
+ {
+ /* This should never happen */
+ table->file->print_error(error,MYF(0));
+ sql_print_error("%s",thd.net.last_error);
+ goto err;
+ }
+ if (thr_reschedule_write_lock(*thd.lock->locks))
+ {
+ /* This should never happen */
+ sql_print_error(ER(ER_DELAYED_CANT_CHANGE_LOCK),table->real_name);
+ }
+ table->file->extra(HA_EXTRA_WRITE_CACHE);
+ pthread_mutex_lock(&mutex);
+ thd.proc_info="insert";
+ }
+ if (tables_in_use)
+ pthread_cond_broadcast(&cond_client); // If waiting clients
+ }
+ }
+
+ thd.proc_info=0;
+ table->next_number_field=0;
+ pthread_mutex_unlock(&mutex);
+ if ((error=table->file->extra(HA_EXTRA_NO_CACHE)))
+ { // This shouldn't happen
+ table->file->print_error(error,MYF(0));
+ sql_print_error("%s",thd.net.last_error);
+ goto err;
+ }
+ pthread_mutex_lock(&mutex);
+ DBUG_RETURN(0);
+
+ err:
+ thread_safe_increment(delayed_insert_errors, &LOCK_delayed_status);
+ pthread_mutex_lock(&mutex);
+ DBUG_RETURN(1);
+}
+
+
+
+/***************************************************************************
+** store records in INSERT ... SELECT *
+***************************************************************************/
+
+int
+select_insert::prepare(List<Item> &values)
+{
+ TABLE *form=table;
+ DBUG_ENTER("select_insert::prepare");
+
+ save_time_stamp=table->time_stamp;
+ if (check_insert_fields(thd,table,*fields,values,1))
+ DBUG_RETURN(1);
+
+ if (fields->elements)
+ {
+ restore_record(form,2); // Get empty record
+ }
+ else
+ form->record[0][0]=form->record[2][0]; // Fix delete marker
+ form->next_number_field=form->found_next_number_field;
+ thd->count_cuted_fields=1; /* calc cuted fields */
+ thd->cuted_fields=0;
+ if (info.handle_duplicates != DUP_REPLACE)
+ form->file->extra(HA_EXTRA_WRITE_CACHE);
+ DBUG_RETURN(0);
+}
+
+select_insert::~select_insert()
+{
+ if (table)
+ {
+ if (save_time_stamp)
+ table->time_stamp=save_time_stamp;
+ table->next_number_field=0;
+ }
+ thd->count_cuted_fields=0;
+}
+
+
+bool select_insert::send_data(List<Item> &values)
+{
+ if (thd->offset_limit)
+ { // using limit offset,count
+ thd->offset_limit--;
+ return 0;
+ }
+ if (fields->elements)
+ fill_record(*fields,values);
+ else
+ fill_record(table->field,values);
+ if (write_record(table,&info))
+ return 1;
+ if (table->next_number_field) // Clear for next record
+ {
+ table->next_number_field->reset();
+ if (! last_insert_id && thd->insert_id_used)
+ last_insert_id=thd->insert_id();
+ }
+ return 0;
+}
+
+
+void select_insert::send_error(uint errcode,const char *err)
+{
+ ::send_error(&thd->net,errcode,err);
+ VOID(table->file->extra(HA_EXTRA_NO_CACHE));
+}
+
+
+bool select_insert::send_eof()
+{
+ int error;
+ if ((error=table->file->extra(HA_EXTRA_NO_CACHE)))
+ {
+ table->file->print_error(error,MYF(0));
+ ::send_error(&thd->net);
+ return 1;
+ }
+ else
+ {
+ char buff[160];
+ if (info.handle_duplicates == DUP_IGNORE)
+ sprintf(buff,ER(ER_INSERT_INFO),info.records,info.records-info.copied,
+ thd->cuted_fields);
+ else
+ sprintf(buff,ER(ER_INSERT_INFO),info.records,info.deleted,
+ thd->cuted_fields);
+ if (last_insert_id)
+ thd->insert_id(last_insert_id); // For update log
+ ::send_ok(&thd->net,info.copied,last_insert_id,buff);
+ mysql_update_log.write(thd->query,thd->query_length);
+ Query_log_event qinfo(thd, thd->query);
+ mysql_bin_log.write(&qinfo);
+ return 0;
+ }
+}
+
+
+/***************************************************************************
+** CREATE TABLE (SELECT) ...
+***************************************************************************/
+
+int
+select_create::prepare(List<Item> &values)
+{
+ DBUG_ENTER("select_create::prepare");
+
+ table=create_table_from_items(thd, create_info, db, name,
+ extra_fields, keys, &values, &lock);
+ if (!table)
+ DBUG_RETURN(-1); // abort() deletes table
+
+ /* 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();
+ table->time_stamp=0; // This should be saved
+ }
+ table->next_number_field=table->found_next_number_field;
+
+ restore_record(table,2); // Get empty record
+ thd->count_cuted_fields=1; // count warnings
+ thd->cuted_fields=0;
+ DBUG_RETURN(0);
+}
+
+
+bool select_create::send_data(List<Item> &values)
+{
+ if (thd->offset_limit)
+ { // using limit offset,count
+ thd->offset_limit--;
+ return 0;
+ }
+ fill_record(field,values);
+ if (write_record(table,&info))
+ return 1;
+ if (table->next_number_field) // Clear for next record
+ {
+ table->next_number_field->reset();
+ if (! last_insert_id && thd->insert_id_used)
+ last_insert_id=thd->insert_id();
+ }
+ return 0;
+}
+
+extern HASH open_cache;
+
+bool select_create::send_eof()
+{
+ bool tmp=select_insert::send_eof();
+ if (tmp)
+ abort();
+ else
+ {
+ VOID(pthread_mutex_lock(&LOCK_open));
+ mysql_unlock_tables(thd, lock);
+ hash_delete(&open_cache,(byte*) table);
+ lock=0; table=0;
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ }
+ return tmp;
+}
+
+void select_create::abort()
+{
+ VOID(pthread_mutex_lock(&LOCK_open));
+ if (lock)
+ {
+ mysql_unlock_tables(thd, lock);
+ lock=0;
+ }
+ if (table)
+ {
+ enum db_type table_type=table->db_type;
+ hash_delete(&open_cache,(byte*) table);
+ quick_rm_table(table_type,db,name);
+ table=0;
+ }
+ VOID(pthread_mutex_unlock(&LOCK_open));
+}
+
+
+/*****************************************************************************
+** Instansiate templates
+*****************************************************************************/
+
+#ifdef __GNUC__
+template class List_iterator<List_item>;
+template class I_List<delayed_insert>;
+template class I_List_iterator<delayed_insert>;
+template class I_List<delayed_row>;
+#endif
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
new file mode 100644
index 00000000000..50c9ab852c1
--- /dev/null
+++ b/sql/sql_lex.cc
@@ -0,0 +1,810 @@
+/* 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 */
+
+
+/* A lexical scanner on a temporary buffer with a yacc interface */
+
+#include "mysql_priv.h"
+#include "item_create.h"
+#include <m_ctype.h>
+#include <hash.h>
+
+LEX_STRING tmp_table_alias= {(char*) "tmp-table",8};
+
+/* Macros to look like lex */
+
+#define yyGet() *(lex->ptr++)
+#define yyGetLast() lex->ptr[-1]
+#define yyPeek() lex->ptr[0]
+#define yyPeek2() lex->ptr[1]
+#define yyUnget() lex->ptr--
+#define yySkip() lex->ptr++
+#define yyLength() ((uint) (lex->ptr - lex->tok_start)-1)
+
+#if MYSQL_VERSION_ID < 32300
+#define FLOAT_NUM REAL_NUM
+#endif
+
+pthread_key(LEX*,THR_LEX);
+
+#define TOCK_NAME_LENGTH 24
+
+/*
+ The following is based on the latin1 character set, and is only
+ used when comparing keywords
+*/
+
+uchar to_upper_lex[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,123,124,125,126,127,
+ 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
+ 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,
+ 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,
+ 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,
+ 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,
+ 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,
+ 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,
+ 208,209,210,211,212,213,214,247,216,217,218,219,220,221,222,255
+};
+
+inline int lex_casecmp(const char *s, const char *t, uint len)
+{
+ while (len-- != 0 &&
+ to_upper_lex[(uchar) *s++] == to_upper_lex[(uchar) *t++]) ;
+ return (int) len+1;
+}
+
+#include "lex_hash.h"
+
+static uchar state_map[256];
+
+
+void lex_init(void)
+{
+ uint i;
+ DBUG_ENTER("lex_init");
+ for (i=0 ; i < array_elements(symbols) ; i++)
+ symbols[i].length=(uchar) strlen(symbols[i].name);
+ for (i=0 ; i < array_elements(sql_functions) ; i++)
+ sql_functions[i].length=(uchar) strlen(sql_functions[i].name);
+
+ VOID(pthread_key_create(&THR_LEX,NULL));
+
+ /* Fill state_map with states to get a faster parser */
+ for (i=0; i < 256 ; i++)
+ {
+ if (isalpha(i))
+ state_map[i]=(uchar) STATE_IDENT;
+ else if (isdigit(i))
+ state_map[i]=(uchar) STATE_NUMBER_IDENT;
+#if defined(USE_MB) && defined(USE_MB_IDENT)
+ else if (use_mb(default_charset_info) && my_ismbhead(default_charset_info, i))
+ state_map[i]=(uchar) STATE_IDENT;
+#endif
+ else if (!isgraph(i))
+ state_map[i]=(uchar) STATE_SKIP;
+ else
+ state_map[i]=(uchar) STATE_CHAR;
+ }
+ state_map[(uchar)'_']=state_map[(uchar)'$']=(uchar) STATE_IDENT;
+ state_map[(uchar)'\'']=state_map[(uchar)'"']=(uchar) STATE_STRING;
+ state_map[(uchar)'-']=state_map[(uchar)'+']=(uchar) STATE_SIGNED_NUMBER;
+ state_map[(uchar)'.']=(uchar) STATE_REAL_OR_POINT;
+ state_map[(uchar)'>']=state_map[(uchar)'=']=state_map[(uchar)'!']= (uchar) STATE_CMP_OP;
+ state_map[(uchar)'<']= (uchar) STATE_LONG_CMP_OP;
+ state_map[(uchar)'&']=state_map[(uchar)'|']=(uchar) STATE_BOOL;
+ state_map[(uchar)'#']=(uchar) STATE_COMMENT;
+ state_map[(uchar)';']=(uchar) STATE_COLON;
+ state_map[(uchar)':']=(uchar) STATE_SET_VAR;
+ state_map[0]=(uchar) STATE_EOL;
+ state_map[(uchar)'\\']= (uchar) STATE_ESCAPE;
+ state_map[(uchar)'/']= (uchar) STATE_LONG_COMMENT;
+ state_map[(uchar)'*']= (uchar) STATE_END_LONG_COMMENT;
+ state_map[(uchar)'@']= (uchar) STATE_USER_END;
+ state_map[(uchar) '`']= (uchar) STATE_USER_VARIABLE_DELIMITER;
+ if (thd_startup_options & OPTION_ANSI_MODE)
+ {
+ state_map[(uchar) '"'] = STATE_USER_VARIABLE_DELIMITER;
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+void lex_free(void)
+{ // Call this when daemon ends
+ DBUG_ENTER("lex_free");
+ DBUG_VOID_RETURN;
+}
+
+
+LEX *lex_start(THD *thd, uchar *buf,uint length)
+{
+ LEX *lex= &thd->lex;
+ lex->next_state=STATE_START;
+ lex->end_of_query=(lex->ptr=buf)+length;
+ lex->yylineno = 1;
+ lex->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=(lex->thd=thd)->convert_set;
+ lex->yacc_yyss=lex->yacc_yyvs=0;
+ lex->ignore_space=test(thd->client_capabilities & CLIENT_IGNORE_SPACE);
+ return lex;
+}
+
+void lex_end(LEX *lex)
+{
+ lex->expr_list.delete_elements(); // If error when parsing sql-varargs
+ x_free(lex->yacc_yyss);
+ x_free(lex->yacc_yyvs);
+}
+
+
+static int find_keyword(LEX *lex, uint len, bool function)
+{
+ uchar *tok=lex->tok_start;
+
+ SYMBOL *symbol = get_hash_symbol((const char *)tok,len,function);
+ if (symbol)
+ {
+ lex->yylval->symbol.symbol=symbol;
+ lex->yylval->symbol.str= (char*) tok;
+ lex->yylval->symbol.length=len;
+ return symbol->tok;
+ }
+#ifdef HAVE_DLOPEN
+ udf_func *udf;
+ if (function && using_udf_functions && (udf=find_udf((char*) tok, len)))
+ {
+ 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;
+ }
+ }
+#endif
+ return 0;
+}
+
+
+/* make a copy of token before ptr and set yytoklen */
+
+static inline 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);
+ return tmp;
+}
+
+/* Return an unescaped text literal without quotes */
+/* Fix sometimes to do only one scan of the string */
+
+static char *get_text(LEX *lex)
+{
+ reg1 uchar c,sep;
+ uint found_escape=0;
+
+ sep= yyGetLast(); // String should end with this
+ //lex->tok_start=lex->ptr-1; // Remember '
+ while (lex->ptr != lex->end_of_query)
+ {
+ c = yyGet();
+#ifdef USE_MB
+ int l;
+ if (use_mb(default_charset_info) &&
+ (l = my_ismbchar(default_charset_info,
+ (const char *)lex->ptr-1,
+ (const char *)lex->end_of_query))) {
+ lex->ptr += l-1;
+ continue;
+ }
+#endif
+ if (c == '\\')
+ { // Escaped character
+ found_escape=1;
+ if (lex->ptr == lex->end_of_query)
+ return 0;
+ yySkip();
+ }
+ else if (c == sep)
+ {
+ if (c == yyGet()) // Check if two separators in a row
+ {
+ found_escape=1; // dupplicate. Remember for delete
+ continue;
+ }
+ else
+ yyUnget();
+
+ /* Found end. Unescape and return string */
+ uchar *str,*end,*start;
+
+ str=lex->tok_start+1;
+ end=lex->ptr-1;
+ start=(uchar*) sql_alloc((uint) (end-str)+1);
+ if (!found_escape)
+ {
+ lex->yytoklen=(uint) (end-str);
+ memcpy(start,str,lex->yytoklen);
+ start[lex->yytoklen]=0;
+ }
+ else
+ {
+ uchar *to;
+ for (to=start ; str != end ; str++)
+ {
+#ifdef USE_MB
+ int l;
+ if (use_mb(default_charset_info) &&
+ (l = my_ismbchar(default_charset_info,
+ (const char *)str, (const char *)end))) {
+ while (l--)
+ *to++ = *str++;
+ str--;
+ continue;
+ }
+#endif
+ if (*str == '\\' && str+1 != end)
+ {
+ switch(*++str) {
+ case 'n':
+ *to++='\n';
+ break;
+ case 't':
+ *to++= '\t';
+ break;
+ case 'r':
+ *to++ = '\r';
+ break;
+ case 'b':
+ *to++ = '\b';
+ break;
+ case '0':
+ *to++= 0; // Ascii null
+ break;
+ case 'Z': // ^Z must be escaped on Win32
+ *to++='\032';
+ break;
+ case '_':
+ case '%':
+ *to++= '\\'; // remember prefix for wildcard
+ /* Fall through */
+ default:
+ *to++ = *str;
+ break;
+ }
+ }
+ else if (*str == sep)
+ *to++= *str++; // Two ' or "
+ else
+ *to++ = *str;
+
+ }
+ *to=0;
+ lex->yytoklen=(uint) (to-start);
+ }
+ if (lex->convert_set)
+ lex->convert_set->convert((char*) start,lex->yytoklen);
+ return (char*) start;
+ }
+ }
+ return 0; // unexpected end of query
+}
+
+
+/*
+** Calc type of integer; long integer, longlong integer or real.
+** Returns smallest type that match the string.
+** When using unsigned long long values the result is converted to a real
+** because else they will be unexpected sign changes because all calculation
+** is done with longlong or double.
+*/
+
+static const char *long_str="2147483647";
+static const uint long_len=10;
+static const char *signed_long_str="-2147483648";
+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;
+
+
+inline static uint int_token(const char *str,uint length)
+{
+ if (length < long_len) // quick normal case
+ return NUM;
+ bool neg=0;
+
+ if (*str == '+') // Remove sign and pre-zeros
+ {
+ str++; length--;
+ }
+ else if (*str == '-')
+ {
+ str++; length--;
+ neg=1;
+ }
+ while (*str == '0' && length)
+ {
+ str++; length --;
+ }
+ if (length < long_len)
+ return NUM;
+
+ uint smaller,bigger;
+ const char *cmp;
+ if (neg)
+ {
+ if (length == long_len)
+ {
+ cmp= signed_long_str+1;
+ smaller=NUM; // If <= signed_long_str
+ bigger=LONG_NUM; // If >= signed_long_str
+ }
+ else if (length < signed_longlong_len)
+ return LONG_NUM;
+ else if (length > signed_longlong_len)
+ return REAL_NUM;
+ else
+ {
+ cmp=signed_longlong_str+1;
+ smaller=LONG_NUM; // If <= signed_longlong_str
+ bigger=REAL_NUM;
+ }
+ }
+ else
+ {
+ if (length == long_len)
+ {
+ cmp= long_str;
+ smaller=NUM;
+ bigger=LONG_NUM;
+ }
+ else if (length < longlong_len)
+ return LONG_NUM;
+ else if (length > longlong_len)
+ return REAL_NUM;
+ else
+ {
+ cmp=longlong_str;
+ smaller=LONG_NUM;
+ bigger=REAL_NUM;
+ }
+ }
+ while (*cmp && *cmp++ == *str++) ;
+ return ((uchar) str[-1] <= (uchar) cmp[-1]) ? smaller : bigger;
+}
+
+
+// yylex remember the following states from the following yylex()
+// STATE_EOQ ; found end of query
+// STATE_OPERATOR_OR_IDENT ; last state was an ident, text or number
+// (which can't be followed by a signed number)
+
+int yylex(void *arg)
+{
+ reg1 uchar c;
+ int tokval;
+ uint length;
+ enum lex_states state,prev_state;
+ LEX *lex=current_lex;
+ YYSTYPE *yylval=(YYSTYPE*) arg;
+
+ lex->yylval=yylval; // The global state
+ lex->tok_start=lex->tok_end=lex->ptr;
+ prev_state=state=lex->next_state;
+ lex->next_state=STATE_OPERATOR_OR_IDENT;
+ LINT_INIT(c);
+ for (;;)
+ {
+ switch(state) {
+ case STATE_OPERATOR_OR_IDENT: // Next is operator or keyword
+ case STATE_START: // Start of token
+ // Skipp startspace
+ for (c=yyGet() ; (state_map[c] == STATE_SKIP) ; c= yyGet())
+ {
+ if (c == '\n')
+ lex->yylineno++;
+ }
+ lex->tok_start=lex->ptr-1; // Start of real token
+ state= (enum lex_states) state_map[c];
+ break;
+ case STATE_ESCAPE:
+ if (yyGet() == 'N')
+ { // Allow \N as shortcut for NULL
+ yylval->lex_str.str=(char*) "\\N";
+ yylval->lex_str.length=2;
+ return NULL_SYM;
+ }
+ case STATE_CHAR: // Unknown or single char token
+ case STATE_SKIP: // This should not happen
+ yylval->lex_str.str=(char*) (lex->ptr=lex->tok_start);// Set to first char
+ yylval->lex_str.length=1;
+ c=yyGet();
+ if (c != ')')
+ lex->next_state= STATE_START; // Allow signed numbers
+ if (c == ',')
+ lex->tok_start=lex->ptr; // Let tok_start point at next item
+ return((int) c);
+
+ case STATE_IDENT: // Incomplete keyword or ident
+#if defined(USE_MB) && defined(USE_MB_IDENT)
+ if (use_mb(default_charset_info))
+ {
+ if (my_ismbhead(default_charset_info, yyGetLast()))
+ {
+ int l = my_ismbchar(default_charset_info,
+ (const char *)lex->ptr-1,
+ (const char *)lex->end_of_query);
+ if (l == 0) {
+ state = STATE_CHAR;
+ continue;
+ }
+ lex->ptr += l - 1;
+ }
+ while (state_map[c=yyGet()] == STATE_IDENT ||
+ state_map[c] == STATE_NUMBER_IDENT)
+ {
+ if (my_ismbhead(default_charset_info, c))
+ {
+ int l;
+ if ((l = my_ismbchar(default_charset_info,
+ (const char *)lex->ptr-1,
+ (const char *)lex->end_of_query)) == 0)
+ break;
+ lex->ptr += l-1;
+ }
+ }
+ }
+ else
+#endif
+ while (state_map[c=yyGet()] == STATE_IDENT ||
+ state_map[c] == STATE_NUMBER_IDENT) ;
+ length= (uint) (lex->ptr - lex->tok_start)-1;
+ if (lex->ignore_space)
+ {
+ for ( ; state_map[c] == STATE_SKIP ; c= yyGet());
+ }
+ if (c == '.' && (state_map[yyPeek()] == STATE_IDENT ||
+ state_map[yyPeek()] == STATE_NUMBER_IDENT))
+ lex->next_state=STATE_IDENT_SEP;
+ else
+ { // '(' must follow directly if function
+ yyUnget();
+ if ((tokval = find_keyword(lex,length,c == '(')))
+ {
+ lex->next_state= STATE_START; // Allow signed numbers
+ return(tokval); // Was keyword
+ }
+ yySkip(); // next state does a unget
+ }
+ yylval->lex_str=get_token(lex,length);
+ return(IDENT);
+
+ case STATE_IDENT_SEP: // Found ident and now '.'
+ lex->next_state=STATE_IDENT_START;// Next is an ident (not a keyword)
+ yylval->lex_str.str=(char*) lex->ptr;
+ yylval->lex_str.length=1;
+ c=yyGet(); // should be '.'
+ return((int) c);
+
+ case STATE_NUMBER_IDENT: // number or ident which starts with num
+ while (isdigit((c = yyGet()))) ;
+ if (state_map[c] != STATE_IDENT)
+ { // Can't be identifier
+ state=STATE_INT_OR_REAL;
+ break;
+ }
+ if (c == 'e' || c == 'E')
+ {
+ if ((c=(yyGet())) == '+' || c == '-')
+ { // Allow 1E+10
+ if (isdigit(yyPeek())) // Number must have digit after sign
+ {
+ yySkip();
+ while (isdigit(yyGet())) ;
+ yylval->lex_str=get_token(lex,yyLength());
+ return(FLOAT_NUM);
+ }
+ }
+ yyUnget(); /* purecov: inspected */
+ }
+ else if (c == 'x' && (lex->ptr - lex->tok_start) == 2 &&
+ lex->tok_start[0] == '0' )
+ { // Varbinary
+ while (isxdigit((c = yyGet()))) ;
+ if ((lex->ptr - lex->tok_start) >= 4)
+ {
+ yylval->lex_str=get_token(lex,yyLength());
+ yylval->lex_str.str+=2; // Skipp 0x
+ yylval->lex_str.length-=2;
+ lex->yytoklen-=2;
+ return (HEX_NUM);
+ }
+ yyUnget();
+ }
+ // fall through
+ case STATE_IDENT_START: // Incomplete ident
+#if defined(USE_MB) && defined(USE_MB_IDENT)
+ if (use_mb(default_charset_info))
+ {
+ if (my_ismbhead(default_charset_info, yyGetLast()))
+ {
+ int l = my_ismbchar(default_charset_info,
+ (const char *)lex->ptr-1,
+ (const char *)lex->end_of_query);
+ if (l == 0)
+ {
+ state = STATE_CHAR;
+ continue;
+ }
+ lex->ptr += l - 1;
+ }
+ while (state_map[c=yyGet()] == STATE_IDENT ||
+ state_map[c] == STATE_NUMBER_IDENT)
+ {
+ if (my_ismbhead(default_charset_info, c))
+ {
+ int l;
+ if ((l = my_ismbchar(default_charset_info,
+ (const char *)lex->ptr-1,
+ (const char *)lex->end_of_query)) == 0)
+ break;
+ lex->ptr += l-1;
+ }
+ }
+ }
+ else
+#endif
+ while (state_map[c = yyGet()] == STATE_IDENT ||
+ state_map[c] == STATE_NUMBER_IDENT) ;
+
+ if (c == '.' && (state_map[yyPeek()] == STATE_IDENT ||
+ state_map[yyPeek()] == STATE_NUMBER_IDENT))
+ lex->next_state=STATE_IDENT_SEP;// Next is '.'
+ // fall through
+
+ case STATE_FOUND_IDENT: // Complete ident
+ yylval->lex_str=get_token(lex,yyLength());
+ return(IDENT);
+
+ case STATE_USER_VARIABLE_DELIMITER:
+ lex->tok_start=lex->ptr; // Skipp first `
+ while ((c=yyGet()) && state_map[c] != STATE_USER_VARIABLE_DELIMITER &&
+ c != (uchar) NAMES_SEP_CHAR) ;
+ yylval->lex_str=get_token(lex,yyLength());
+ if (state_map[c] == STATE_USER_VARIABLE_DELIMITER)
+ yySkip(); // Skipp end `
+ return(IDENT);
+
+ case STATE_SIGNED_NUMBER: // Incomplete signed number
+ if (prev_state == STATE_OPERATOR_OR_IDENT)
+ {
+ if (c == '-' && yyPeek() == '-' && isspace(yyPeek2()))
+ state=STATE_COMMENT;
+ else
+ state= STATE_CHAR; // Must be operator
+ break;
+ }
+ if (!isdigit(c=yyGet()) || yyPeek() == 'x')
+ {
+ if (c != '.')
+ {
+ if (c == '-' && isspace(yyPeek()))
+ state=STATE_COMMENT;
+ else
+ state = STATE_CHAR; // Return sign as single char
+ break;
+ }
+ yyUnget(); // Fix for next loop
+ }
+ while (isdigit(c=yyGet())) ; // Incomplete real or int number
+ if ((c == 'e' || c == 'E') && (yyPeek() == '+' || yyPeek() == '-'))
+ { // Real number
+ yyUnget();
+ c= '.'; // Fool next test
+ }
+ // fall through
+ case STATE_INT_OR_REAL: // Compleat int or incompleat real
+ if (c != '.')
+ { // Found complete integer number.
+ yylval->lex_str=get_token(lex,yyLength());
+ return int_token(yylval->lex_str.str,yylval->lex_str.length);
+ }
+ // fall through
+ case STATE_REAL: // Incomplete real number
+ while (isdigit(c = yyGet())) ;
+
+ if (c == 'e' || c == 'E')
+ {
+ c = yyGet();
+ if (c != '-' && c != '+')
+ { // No exp sig found
+ state= STATE_CHAR;
+ break;
+ }
+ if (!isdigit(yyGet()))
+ { // No digit after sign
+ state= STATE_CHAR;
+ break;
+ }
+ while (isdigit(yyGet())) ;
+ yylval->lex_str=get_token(lex,yyLength());
+ return(FLOAT_NUM);
+ }
+ yylval->lex_str=get_token(lex,yyLength());
+ return(REAL_NUM);
+
+ case STATE_CMP_OP: // Incomplete comparison operator
+ if (state_map[yyPeek()] == STATE_CMP_OP ||
+ state_map[yyPeek()] == STATE_LONG_CMP_OP)
+ yySkip();
+ if ((tokval = find_keyword(lex,(uint) (lex->ptr - lex->tok_start),0)))
+ {
+ lex->next_state= STATE_START; // Allow signed numbers
+ return(tokval);
+ }
+ state = STATE_CHAR; // Something fishy found
+ break;
+
+ case STATE_LONG_CMP_OP: // Incomplete comparison operator
+ if (state_map[yyPeek()] == STATE_CMP_OP ||
+ state_map[yyPeek()] == STATE_LONG_CMP_OP)
+ {
+ yySkip();
+ if (state_map[yyPeek()] == STATE_CMP_OP)
+ yySkip();
+ }
+ if ((tokval = find_keyword(lex,(uint) (lex->ptr - lex->tok_start),0)))
+ {
+ lex->next_state= STATE_START; // Found long op
+ return(tokval);
+ }
+ state = STATE_CHAR; // Something fishy found
+ break;
+
+ case STATE_BOOL:
+ if (c != yyPeek())
+ {
+ state=STATE_CHAR;
+ break;
+ }
+ yySkip();
+ tokval = find_keyword(lex,2,0); // Is a bool operator
+ lex->next_state= STATE_START; // Allow signed numbers
+ return(tokval);
+
+ case STATE_STRING: // Incomplete text string
+ if (!(yylval->lex_str.str = get_text(lex)))
+ {
+ state= STATE_CHAR; // Read char by char
+ break;
+ }
+ yylval->lex_str.length=lex->yytoklen;
+ return(TEXT_STRING);
+
+ case STATE_COMMENT: // Comment
+ while ((c = yyGet()) != '\n' && c) ;
+ yyUnget(); // Safety against eof
+ state = STATE_START; // Try again
+ break;
+ case STATE_LONG_COMMENT: /* Long C comment? */
+ if (yyPeek() != '*')
+ {
+ state=STATE_CHAR; // Probable division
+ break;
+ }
+ yySkip(); // Skip '*'
+ if (yyPeek() == '!') // MySQL command in comment
+ {
+ ulong version=MYSQL_VERSION_ID;
+ yySkip();
+ state=STATE_START;
+ if (isdigit(yyPeek()))
+ { // Version number
+ version=strtol((char*) lex->ptr,(char**) &lex->ptr,10);
+ }
+ if (version <= MYSQL_VERSION_ID)
+ {
+ lex->in_comment=1;
+ break;
+ }
+ }
+ while (lex->ptr != lex->end_of_query &&
+ ((c=yyGet()) != '*' || yyPeek() != '/'))
+ {
+ if (c == '\n')
+ lex->yylineno++;
+ }
+ if (lex->ptr != lex->end_of_query)
+ yySkip(); // remove last '/'
+ state = STATE_START; // Try again
+ break;
+ case STATE_END_LONG_COMMENT:
+ if (lex->in_comment && yyPeek() == '/')
+ {
+ yySkip();
+ lex->in_comment=0;
+ state=STATE_START;
+ }
+ else
+ state=STATE_CHAR; // Return '*'
+ break;
+ case STATE_SET_VAR: // Check if ':='
+ if (yyPeek() != '=')
+ {
+ state=STATE_CHAR; // Return ':'
+ break;
+ }
+ yySkip();
+ return (SET_VAR);
+ case STATE_COLON: // optional line terminator
+ if (yyPeek())
+ {
+ state=STATE_CHAR; // Return ';'
+ break;
+ }
+ /* fall true */
+ case STATE_EOL:
+ lex->next_state=STATE_END; // Mark for next loop
+ return(END_OF_INPUT);
+ case STATE_END:
+ lex->next_state=STATE_END;
+ return(0); // We found end of input last time
+
+ // Actually real shouldn't start
+ // with . but allow them anyhow
+ case STATE_REAL_OR_POINT:
+ if (isdigit(yyPeek()))
+ state = STATE_REAL; // Real
+ else
+ {
+ state = STATE_CHAR; // return '.'
+ lex->next_state=STATE_IDENT_START;// Next is an ident (not a keyword)
+ }
+ break;
+ case STATE_USER_END: // end '@' of user@hostname
+ if (state_map[yyPeek()] != STATE_STRING &&
+ state_map[yyPeek()] != STATE_USER_VARIABLE_DELIMITER)
+ lex->next_state=STATE_HOSTNAME; // Mark for next loop
+ yylval->lex_str.str=(char*) lex->ptr;
+ yylval->lex_str.length=1;
+ return((int) '@');
+ case STATE_HOSTNAME: // end '@' of user@hostname
+ for (c=yyGet() ;
+ isalnum(c) || c == '.' || c == '_' || c == '$';
+ c= yyGet()) ;
+ yylval->lex_str=get_token(lex,yyLength());
+ return(LEX_HOSTNAME);
+ }
+ }
+}
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
new file mode 100644
index 00000000000..6fd9f203d52
--- /dev/null
+++ b/sql/sql_lex.h
@@ -0,0 +1,149 @@
+/* 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 */
+
+
+/* YACC and LEX Definitions */
+
+/* These may not be declared yet */
+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
+
+#ifdef MYSQL_YACC
+#define LEX_YYSTYPE void *
+#else
+#include "lex_symbol.h"
+#include "sql_yacc.h"
+#define LEX_YYSTYPE YYSTYPE *
+#endif
+
+enum enum_sql_command {
+ SQLCOM_SELECT,SQLCOM_CREATE_TABLE,SQLCOM_CREATE_INDEX,SQLCOM_ALTER_TABLE,
+ SQLCOM_UPDATE,SQLCOM_INSERT,SQLCOM_INSERT_SELECT,SQLCOM_DELETE,
+ SQLCOM_DROP_TABLE,SQLCOM_DROP_INDEX,SQLCOM_SHOW_DATABASES,
+ SQLCOM_SHOW_TABLES,SQLCOM_SHOW_FIELDS,SQLCOM_SHOW_KEYS,
+ SQLCOM_LOAD,SQLCOM_SET_OPTION,SQLCOM_LOCK_TABLES,SQLCOM_UNLOCK_TABLES,
+ SQLCOM_GRANT, SQLCOM_CHANGE_DB, SQLCOM_CREATE_DB, SQLCOM_DROP_DB,
+ SQLCOM_REPAIR, SQLCOM_REPLACE, SQLCOM_REPLACE_SELECT, SQLCOM_SHOW_VARIABLES,
+ SQLCOM_SHOW_STATUS, SQLCOM_CREATE_FUNCTION, SQLCOM_DROP_FUNCTION,
+ SQLCOM_SHOW_PROCESSLIST,SQLCOM_REVOKE,SQLCOM_OPTIMIZE, SQLCOM_CHECK,
+ SQLCOM_FLUSH, SQLCOM_KILL, SQLCOM_SHOW_GRANTS, SQLCOM_ANALYZE,
+ SQLCOM_ROLLBACK, SQLCOM_COMMIT, SQLCOM_SLAVE_START, SQLCOM_SLAVE_STOP,
+ SQLCOM_BEGIN, SQLCOM_LOAD_MASTER_TABLE, SQLCOM_SHOW_CREATE,
+ SQLCOM_SHOW_MASTER_STAT, SQLCOM_SHOW_SLAVE_STAT, SQLCOM_CHANGE_MASTER
+};
+
+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
+};
+
+typedef List<Item> List_item;
+
+typedef struct st_lex_master_info
+{
+ char* host, *user, *password,*log_file_name;
+ uint port, connect_retry;
+ ulonglong pos;
+} LEX_MASTER_INFO;
+
+/* The state of the lex parsing. This is saved in the THD struct */
+
+typedef struct st_lex {
+ uint yylineno,yytoklen; /* Simulate lex */
+ LEX_YYSTYPE yylval;
+ uchar *ptr,*tok_start,*tok_end,*end_of_query;
+ ha_rows select_limit,offset_limit;
+ bool create_refs,drop_primary,drop_if_exists,local_file,
+ in_comment,ignore_space,verbose;
+ enum_sql_command sql_command;
+ enum lex_states next_state;
+ ulong options;
+ uint in_sum_expr,grant,grant_tot_col,which_columns, sort_default;
+ char *length,*dec,*change,*name;
+ String *wild;
+ sql_exchange *exchange;
+ thr_lock_type lock_option;
+
+ 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<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;
+ TYPELIB *interval;
+ create_field *last_field;
+
+ Item *where,*having,*default_value;
+ enum enum_duplicates duplicates;
+ ulong thread_id,type;
+ HA_CREATE_INFO create_info;
+ CONVERT *convert_set;
+ LEX_USER *grant_user;
+ char *db,*db1,*table1,*db2,*table2; /* For outer join using .. */
+ gptr yacc_yyss,yacc_yyvs;
+ THD *thd;
+ udf_func udf;
+ HA_CHECK_OPT check_opt; // check/repair options
+ LEX_MASTER_INFO mi; // used by CHANGE MASTER
+} LEX;
+
+
+void lex_init(void);
+void lex_free(void);
+LEX *lex_start(THD *thd, uchar *buf,uint length);
+void lex_end(LEX *lex);
+
+extern pthread_key(LEX*,THR_LEX);
+
+extern LEX_STRING tmp_table_alias;
+
+#define current_lex (&current_thd->lex)
diff --git a/sql/sql_list.cc b/sql/sql_list.cc
new file mode 100644
index 00000000000..7d5fc442121
--- /dev/null
+++ b/sql/sql_list.cc
@@ -0,0 +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 */
+
+
+#ifdef __GNUC__
+#pragma implementation // gcc: Class implementation
+#endif
+
+#include "mysql_priv.h"
diff --git a/sql/sql_list.h b/sql/sql_list.h
new file mode 100644
index 00000000000..725d506e62a
--- /dev/null
+++ b/sql/sql_list.h
@@ -0,0 +1,310 @@
+/* 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 */
+
+
+/* mysql standard open memoryallocator */
+
+#ifdef __GNUC__
+#pragma interface /* gcc class implementation */
+#endif
+
+
+class Sql_alloc
+{
+public:
+ static void *operator new(size_t size) {return (void*) sql_alloc(size); }
+ static void operator delete(void *ptr, size_t size) {} /*lint -e715 */
+ inline Sql_alloc() {};
+ inline ~Sql_alloc() {};
+};
+
+/*
+** basic single linked list
+** Used for item and item_buffs.
+*/
+
+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 base_list() { empty(); }
+ inline base_list(const base_list &tmp) :Sql_alloc()
+ {
+ elements=tmp.elements;
+ first=tmp.first;
+ last=tmp.last;
+ }
+ inline bool push_back(void *info)
+ {
+ if (((*last)=new list_node(info,0)))
+ {
+ last= &(*last)->next;
+ elements++;
+ return 0;
+ }
+ return 1;
+ }
+ inline bool push_front(void *info)
+ {
+ list_node *node=new list_node(info,first);
+ if (node)
+ {
+ if (!first)
+ last= &node->next;
+ first=node;
+ elements++;
+ return 0;
+ }
+ return 1;
+ }
+ void remove(list_node **prev)
+ {
+ list_node *node=(*prev)->next;
+ delete *prev;
+ *prev=node;
+ if (!--elements)
+ {
+ last= &first;
+ first=0;
+ }
+ }
+ inline void *pop(void)
+ {
+ if (!first) 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; }
+ friend class base_list_iterator;
+
+protected:
+ void after(void *info,list_node *node)
+ {
+ list_node *new_node=new list_node(info,node->next);
+ node->next=new_node;
+ elements++;
+ if (last == &(node->next))
+ last= &new_node->next;
+ }
+};
+
+
+class base_list_iterator
+{
+ base_list *list;
+ base_list::list_node **el,**prev,*current;
+public:
+ base_list_iterator(base_list &list_par) :list(&list_par),el(&list_par.first),
+ prev(0),current(0)
+ {}
+ inline void *next(void)
+ {
+ prev=el;
+ if (!(current= *el))
+ return 0;
+ el= &current->next;
+ return current->info;
+ }
+ inline void rewind(void)
+ {
+ el= &list->first;
+ }
+ void *replace(void *element)
+ { // Return old element
+ void *tmp=current->info;
+ current->info=element;
+ return tmp;
+ }
+ void *replace(base_list &new_list)
+ {
+ void *ret_value=current->info;
+ if (new_list.first)
+ {
+ *new_list.last=current->next;
+ current->info=new_list.first->info;
+ current->next=new_list.first->next;
+ list->elements+=new_list.elements-1;
+ }
+ return ret_value; // return old element
+ }
+ inline void remove(void) // Remove current
+ {
+ list->remove(prev);
+ el=prev;
+ current=0; // Safeguard
+ }
+ void after(void *element) // Insert element after current
+ {
+ list->after(element,current);
+ current=current->next;
+ el= &current->next;
+ }
+ inline void **ref(void) // Get reference pointer
+ {
+ return &current->info;
+ }
+ inline bool is_last(void)
+ {
+ return *el == 0;
+ }
+};
+
+
+template <class T> class List :public base_list
+{
+public:
+ inline List() :base_list() {}
+ inline List(const List<T> &tmp) :base_list(tmp) {}
+ inline bool push_back(T *a) { return base_list::push_back(a); }
+ inline bool push_front(T *a) { return base_list::push_front(a); }
+ inline T* head() {return (T*) base_list::head(); }
+ inline T** head_ref() {return (T**) base_list::head_ref(); }
+ inline T* pop() {return (T*) base_list::pop(); }
+ void delete_elements(void)
+ {
+ list_node *element,*next;
+ for (element=first; element ; element=next)
+ {
+ next=element->next;
+ delete (T*) element->info;
+ }
+ empty();
+ }
+};
+
+
+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(); }
+};
+
+
+/*
+** An simple intrusive list with automaticly removes element from list
+** on delete (for THD element)
+*/
+
+struct ilink {
+ struct ilink **prev,*next;
+ inline ilink()
+ {
+ prev=0; next=0;
+ }
+ inline void unlink()
+ {
+ /* Extra tests because element doesn't have to be linked */
+ if (prev) *prev= next;
+ if (next) next->prev=prev;
+ prev=0 ; next=0;
+ }
+ virtual ~ilink() { unlink(); } /*lint -e1740 */
+};
+
+template <class T> class I_List_iterator;
+
+class base_ilist {
+ public:
+ struct ilink *first,last;
+ base_ilist() { first= &last; last.prev= &first; }
+ inline bool is_empty() { return first == &last; }
+ inline void append(ilink *a)
+ {
+ first->prev= &a->next;
+ a->next=first; a->prev= &first; first=a;
+ }
+ inline void push_back(ilink *a)
+ {
+ *last.prev= a;
+ a->next= &last;
+ a->prev= last.prev;
+ last.prev= &a->next;
+ }
+ inline struct ilink *get()
+ {
+ struct ilink *first_link=first;
+ if (first_link == &last)
+ return 0;
+ first_link->unlink(); // Unlink from list
+ return first_link;
+ }
+ friend class base_list_iterator;
+};
+
+
+class base_ilist_iterator
+{
+ base_ilist *list;
+ struct ilink **el,*current;
+public:
+ base_ilist_iterator(base_ilist &list_par) :list(&list_par),
+ el(&list_par.first),current(0) {}
+ void *next(void)
+ {
+ /* This is coded to allow push_back() while iterating */
+ current= *el;
+ if (current == &list->last) return 0;
+ el= &current->next;
+ return current;
+ }
+};
+
+
+template <class T>
+class I_List :private base_ilist {
+public:
+ I_List() :base_ilist() {}
+ inline bool is_empty() { return base_ilist::is_empty(); }
+ inline void append(T* a) { base_ilist::append(a); }
+ inline void push_back(T* a) { base_ilist::push_back(a); }
+ inline T* get() { return (T*) base_ilist::get(); }
+#ifndef _lint
+ friend class I_List_iterator<T>;
+#endif
+};
+
+
+template <class T> class I_List_iterator :public base_ilist_iterator
+{
+public:
+ I_List_iterator(I_List<T> &a) : base_ilist_iterator(a) {}
+ inline T* operator++(int) { return (T*) base_ilist_iterator::next(); }
+};
diff --git a/sql/sql_load.cc b/sql/sql_load.cc
new file mode 100644
index 00000000000..6f7db9c5a48
--- /dev/null
+++ b/sql/sql_load.cc
@@ -0,0 +1,790 @@
+/* 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 */
+
+
+/* Copy data from a textfile to table */
+
+#include "mysql_priv.h"
+#include <my_dir.h>
+#include <m_ctype.h>
+
+class READ_INFO {
+ File file;
+ byte *buffer, /* Buffer for read text */
+ *end_of_buff; /* Data in bufferts ends here */
+ uint buff_length, /* Length of buffert */
+ max_length; /* Max length of row */
+ char *field_term_ptr,*line_term_ptr,*line_start_ptr,*line_start_end;
+ uint field_term_length,line_term_length,enclosed_length;
+ int field_term_char,line_term_char,enclosed_char,escape_char;
+ int *stack,*stack_pos;
+ bool found_end_of_line,start_of_line,eof;
+ IO_CACHE cache;
+ NET *io_net;
+
+public:
+ bool error,line_cuted,found_null,enclosed;
+ byte *row_start, /* Found row starts here */
+ *row_end; /* Found row ends here */
+
+ READ_INFO(File file,uint tot_length,
+ String &field_term,String &line_start,String &line_term,
+ String &enclosed,int escape,bool get_it_from_net);
+ ~READ_INFO();
+ int read_field();
+ int read_fixed_length(void);
+ int next_line(void);
+ char unescape(char chr);
+ int terminator(char *ptr,uint length);
+ bool find_start_of_fields();
+};
+
+static int read_fixed_length(THD *thd,COPY_INFO &info,TABLE *table,
+ List<Item> &fields, READ_INFO &read_info);
+static int read_sep_field(THD *thd,COPY_INFO &info,TABLE *table,
+ List<Item> &fields, READ_INFO &read_info,
+ String &enclosed);
+
+
+int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
+ List<Item> &fields, enum enum_duplicates handle_duplicates,
+ bool read_file_from_client,thr_lock_type lock_type)
+{
+ char name[FN_REFLEN];
+ 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;
+ DBUG_ENTER("mysql_load");
+
+ 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);
+ if (!fields.elements)
+ {
+ Field **field;
+ for (field=table->field; *field ; field++)
+ fields.push_back(new Item_field(*field));
+ }
+ else
+ { // Part field list
+ thd->dupp_field=0;
+ if (setup_fields(thd,table_list,fields,1,0) < 0)
+ DBUG_RETURN(-1);
+ if (thd->dupp_field)
+ {
+ my_error(ER_FIELD_SPECIFIED_TWICE, MYF(0), thd->dupp_field->field_name);
+ DBUG_RETURN(-1);
+ }
+ }
+
+ uint tot_length=0;
+ bool use_blobs=0,use_timestamp=0;
+ List_iterator<Item> it(fields);
+
+ Item_field *field;
+ while ((field=(Item_field*) it++))
+ {
+ if (field->field->flags & BLOB_FLAG)
+ {
+ use_blobs=1;
+ tot_length+=256; // Will be extended if needed
+ }
+ else
+ tot_length+=field->field->field_length;
+ if (!field_term->length() && !(field->field->flags & NOT_NULL_FLAG))
+ field->field->set_notnull();
+ if (field->field == table->timestamp_field)
+ use_timestamp=1;
+ }
+ if (use_blobs && !ex->line_term->length() && !field_term->length())
+ {
+ my_message(ER_BLOBS_AND_NO_TERMINATED,ER(ER_BLOBS_AND_NO_TERMINATED),
+ MYF(0));
+ DBUG_RETURN(-1);
+ }
+
+ /* We can't give an error in the middle when using LOCAL files */
+ if (read_file_from_client && handle_duplicates == DUP_ERROR)
+ handle_duplicates=DUP_IGNORE;
+
+ if (read_file_from_client && (thd->client_capabilities & CLIENT_LOCAL_FILES))
+ {
+ 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);
+ file = -1;
+ }
+ else
+ {
+ read_file_from_client=0;
+#ifdef DONT_ALLOW_FULL_LOAD_DATA_PATHS
+ 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 <
+ FN_REFLEN)
+ {
+ (void) sprintf(name,"%s/%s/%s",mysql_data_home,thd->db,ex->file_name);
+ unpack_filename(name,name); /* Convert to system format */
+ }
+ else
+ {
+ unpack_filename(name,ex->file_name);
+#ifndef __WIN__
+ MY_STAT stat_info;
+ if (!my_stat(name,&stat_info,MYF(MY_FAE)))
+ DBUG_RETURN(-1);
+
+ // the file must be:
+ if (!(
+ (stat_info.st_mode & S_IROTH) == S_IROTH
+ && // readable by others
+ (stat_info.st_mode & S_IFLNK) != S_IFLNK
+ && // and not a symlink
+ ((stat_info.st_mode & S_IFREG) == S_IFREG
+ ||
+ (stat_info.st_mode & S_IFIFO) == S_IFIFO
+ )
+ // and either regular or a pipe
+ )
+ )
+ {
+ my_error(ER_TEXTFILE_NOT_READABLE,MYF(0),name);
+ DBUG_RETURN(-1);
+ }
+#endif
+ }
+ if ((file=my_open(name,O_RDONLY,MYF(MY_WME))) < 0)
+ DBUG_RETURN(-1);
+ }
+
+ COPY_INFO info;
+ bzero((char*) &info,sizeof(info));
+ info.handle_duplicates=handle_duplicates;
+ info.escape_char=escaped->length() ? (*escaped)[0] : INT_MAX;
+
+ READ_INFO read_info(file,tot_length,*field_term,
+ *ex->line_start, *ex->line_term, *enclosed,
+ info.escape_char,read_file_from_client);
+ if (read_info.error)
+ {
+ if (file >= 0)
+ my_close(file,MYF(0)); // no files in net reading
+ DBUG_RETURN(-1); // Can't allocate buffers
+ }
+
+ 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--)
+ {
+ if (read_info.next_line())
+ break;
+ }
+ }
+ if (!(error=test(read_info.error)))
+ {
+ uint save_time_stamp=table->time_stamp;
+ if (use_timestamp)
+ table->time_stamp=0;
+ table->next_number_field=table->found_next_number_field;
+ VOID(table->file->extra(HA_EXTRA_WRITE_CACHE));
+ table->copy_blobs=1;
+ if (!field_term->length() && !enclosed->length())
+ 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))
+ error=1; /* purecov: inspected */
+ 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 */
+ if (error)
+ DBUG_RETURN(-1); // Error on read
+ 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);
+ mysql_update_log.write(thd->query,thd->query_length);
+
+ if(!read_file_from_client)
+ {
+ ex->skip_lines = save_skip_lines;
+ Load_log_event qinfo(thd, ex, table->table_name, fields, handle_duplicates);
+ mysql_bin_log.write(&qinfo);
+ }
+ DBUG_RETURN(0);
+}
+
+
+/****************************************************************************
+** Read of rows of fixed size + optional garage + optonal newline
+****************************************************************************/
+
+static int
+read_fixed_length(THD *thd,COPY_INFO &info,TABLE *table,List<Item> &fields,
+ READ_INFO &read_info)
+{
+ List_iterator<Item> it(fields);
+ Item_field *sql_field;
+ DBUG_ENTER("read_fixed_length");
+
+ /* 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();
+
+ while (!read_info.read_fixed_length())
+ {
+ if (thd->killed)
+ {
+ my_error(ER_SERVER_SHUTDOWN,MYF(0));
+ DBUG_RETURN(1);
+ }
+ it.rewind();
+ byte *pos=read_info.row_start;
+#ifdef HAVE_purify
+ read_info.row_end[0]=0;
+#endif
+ while ((sql_field= (Item_field*) it++))
+ {
+ Field *field=sql_field->field;
+ if (pos == read_info.row_end)
+ {
+ thd->cuted_fields++; /* Not enough fields */
+ field->reset();
+ }
+ else
+ {
+ uint length;
+ byte save_chr;
+ if ((length=(uint) (read_info.row_end-pos)) >
+ field->field_length)
+ length=field->field_length;
+ save_chr=pos[length]; pos[length]='\0'; // Safeguard aganst malloc
+ field->store((char*) pos,length);
+ pos[length]=save_chr;
+ if ((pos+=length) > read_info.row_end)
+ pos= read_info.row_end; /* Fills rest with space */
+ }
+ }
+ if (pos != read_info.row_end)
+ thd->cuted_fields++; /* To long row */
+ if (write_record(table,&info))
+ DBUG_RETURN(1);
+ if (table->next_number_field)
+ table->next_number_field->reset(); // Clear for next record
+ if (read_info.next_line()) // Skipp to next line
+ break;
+ if (read_info.line_cuted)
+ thd->cuted_fields++; /* To long row */
+ }
+ DBUG_RETURN(test(read_info.error));
+}
+
+
+
+static int
+read_sep_field(THD *thd,COPY_INFO &info,TABLE *table,
+ List<Item> &fields, READ_INFO &read_info,
+ String &enclosed)
+{
+ List_iterator<Item> it(fields);
+ Item_field *sql_field;
+ uint enclosed_length;
+ DBUG_ENTER("read_sep_field");
+
+ enclosed_length=enclosed.length();
+
+ for (;;it.rewind())
+ {
+ if (thd->killed)
+ {
+ my_error(ER_SERVER_SHUTDOWN,MYF(0));
+ DBUG_RETURN(1);
+ }
+ while ((sql_field=(Item_field*) it++))
+ {
+ uint length;
+ byte *pos;
+
+ if (read_info.read_field())
+ break;
+ pos=read_info.row_start;
+ length=(uint) (read_info.row_end-pos);
+ Field *field=sql_field->field;
+
+ if (!read_info.enclosed &&
+ (enclosed_length && length == 4 && !memcmp(pos,"NULL",4)) ||
+ (length == 1 && read_info.found_null))
+ {
+ field->reset();
+ field->set_null();
+ if (!field->maybe_null())
+ {
+ if (field->type() == FIELD_TYPE_TIMESTAMP)
+ ((Field_timestamp*) field)->set_time();
+ else
+ thd->cuted_fields++;
+ }
+ continue;
+ }
+ field->set_notnull();
+ read_info.row_end[0]=0; // Safe to change end marker
+ field->store((char*) read_info.row_start,length);
+ }
+ if (read_info.error)
+ break;
+ if (sql_field)
+ { // Last record
+ if (sql_field == (Item_field*) fields.head())
+ break;
+ for ( ; sql_field ; sql_field=(Item_field*) it++)
+ {
+ sql_field->field->set_null();
+ sql_field->field->reset();
+ thd->cuted_fields++;
+ }
+ }
+ if (write_record(table,&info))
+ DBUG_RETURN(1);
+ if (table->next_number_field)
+ table->next_number_field->reset(); // Clear for next record
+ if (read_info.next_line()) // Skipp to next line
+ break;
+ if (read_info.line_cuted)
+ thd->cuted_fields++; /* To long row */
+ }
+ DBUG_RETURN(test(read_info.error));
+}
+
+
+/* Unescape all escape characters, mark \N as null */
+
+char
+READ_INFO::unescape(char chr)
+{
+ switch(chr) {
+ case 'n': return '\n';
+ case 't': return '\t';
+ case 'r': return '\r';
+ case 'b': return '\b';
+ case '0': return 0; // Ascii null
+ case 'Z': return '\032'; // Win32 end of file
+ case 'N': found_null=1;
+
+ /* fall through */
+ default: return chr;
+ }
+}
+
+
+ /* 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,
+ String &line_start, String &line_term,
+ String &enclosed_par, int escape, bool get_it_from_net)
+ :file(file_par),escape_char(escape)
+{
+ field_term_ptr=(char*) field_term.ptr();
+ field_term_length= field_term.length();
+ line_term_ptr=(char*) line_term.ptr();
+ line_term_length= line_term.length();
+ if (line_start.length() == 0)
+ {
+ line_start_ptr=0;
+ start_of_line= 0;
+ }
+ else
+ {
+ line_start_ptr=(char*) line_start.ptr();
+ line_start_end=line_start_ptr+line_start.length();
+ start_of_line= 1;
+ }
+ /* If field_terminator == line_terminator, don't use line_terminator */
+ if (field_term_length == line_term_length &&
+ !memcmp(field_term_ptr,line_term_ptr,field_term_length))
+ {
+ line_term_length=0;
+ line_term_ptr=(char*) "";
+ }
+ enclosed_char= (enclosed_length=enclosed_par.length()) ?
+ (uchar) enclosed_par[0] : INT_MAX;
+ field_term_char= field_term_length ? (uchar) field_term_ptr[0] : INT_MAX;
+ line_term_char= line_term_length ? (uchar) line_term_ptr[0] : INT_MAX;
+ error=eof=found_end_of_line=found_null=line_cuted=0;
+ buff_length=tot_length;
+
+
+ /* Set of a stack for unget if long terminators */
+ uint length=max(field_term_length,line_term_length)+1;
+ set_if_bigger(length,line_start.length());
+ stack=stack_pos=(int*) sql_alloc(sizeof(int)*length);
+
+ if (!(buffer=(byte*) my_malloc(buff_length+1,MYF(0))))
+ error=1; /* purecov: inspected */
+ else
+ {
+ end_of_buff=buffer+buff_length;
+ if (init_io_cache(&cache,(get_it_from_net) ? -1 : file, 0,
+ (get_it_from_net) ? READ_NET : READ_CACHE,0L,1,
+ MYF(MY_WME)))
+ {
+ my_free((gptr) buffer,MYF(0)); /* purecov: inspected */
+ error=1;
+ }
+ }
+}
+
+
+READ_INFO::~READ_INFO()
+{
+ if (!error)
+ {
+ end_io_cache(&cache);
+ my_free((gptr) buffer,MYF(0));
+ error=1;
+ }
+}
+
+
+#define GET (stack_pos != stack ? *--stack_pos : my_b_get(&cache))
+#define PUSH(A) *(stack_pos++)=(A)
+
+
+inline int READ_INFO::terminator(char *ptr,uint length)
+{
+ int chr=0; // Keep gcc happy
+ uint i;
+ for (i=1 ; i < length ; i++)
+ {
+ if ((chr=GET) != *++ptr)
+ {
+ break;
+ }
+ }
+ if (i == length)
+ return 1;
+ PUSH(chr);
+ while (i-- > 1)
+ PUSH((uchar) *--ptr);
+ return 0;
+}
+
+
+int READ_INFO::read_field()
+{
+ int chr,found_enclosed_char;
+ byte *to,*new_buffer;
+
+ found_null=0;
+ if (found_end_of_line)
+ return 1; // One have to call next_line
+
+ /* Skipp until we find 'line_start' */
+
+ if (start_of_line)
+ { // Skipp until line_start
+ start_of_line=0;
+ if (find_start_of_fields())
+ return 1;
+ }
+ if ((chr=GET) == my_b_EOF)
+ {
+ found_end_of_line=eof=1;
+ return 1;
+ }
+ to=buffer;
+ if (chr == enclosed_char)
+ {
+ found_enclosed_char=enclosed_char;
+ *to++=(byte) chr; // If error
+ }
+ else
+ {
+ found_enclosed_char= INT_MAX;
+ PUSH(chr);
+ }
+
+ for (;;)
+ {
+ while ( to < end_of_buff)
+ {
+ chr = GET;
+#ifdef USE_MB
+ if (use_mb(default_charset_info) &&
+ my_ismbhead(default_charset_info, chr) &&
+ to+my_mbcharlen(default_charset_info, chr) <= end_of_buff)
+ {
+ uchar* p = (uchar*)to;
+ *to++ = chr;
+ int ml = my_mbcharlen(default_charset_info, chr);
+ int i;
+ for (i=1; i<ml; i++) {
+ chr = GET;
+ if (chr == my_b_EOF)
+ goto found_eof;
+ *to++ = chr;
+ }
+ if (my_ismbchar(default_charset_info,
+ (const char *)p,
+ (const char *)to))
+ continue;
+ for (i=0; i<ml; i++)
+ PUSH((uchar) *--to);
+ chr = GET;
+ }
+#endif
+ if (chr == my_b_EOF)
+ goto found_eof;
+ if (chr == escape_char)
+ {
+ if ((chr=GET) == my_b_EOF)
+ {
+ *to++= (byte) escape_char;
+ goto found_eof;
+ }
+ *to++ = (byte) unescape((char) chr);
+ continue;
+ }
+#ifdef ALLOW_LINESEPARATOR_IN_STRINGS
+ if (chr == line_term_char)
+#else
+ if (chr == line_term_char && found_enclosed_char == INT_MAX)
+#endif
+ {
+ if (terminator(line_term_ptr,line_term_length))
+ { // Maybe unexpected linefeed
+ enclosed=0;
+ found_end_of_line=1;
+ row_start=buffer;
+ row_end= to;
+ return 0;
+ }
+ }
+ if (chr == found_enclosed_char)
+ {
+ if ((chr=GET) == found_enclosed_char)
+ { // Remove dupplicated
+ *to++ = (byte) chr;
+ continue;
+ }
+ // End of enclosed field if followed by field_term or line_term
+ if (chr == my_b_EOF ||
+ chr == line_term_char && terminator(line_term_ptr,
+ line_term_length))
+ { // Maybe unexpected linefeed
+ enclosed=1;
+ found_end_of_line=1;
+ row_start=buffer+1;
+ row_end= to;
+ return 0;
+ }
+ if (chr == field_term_char &&
+ terminator(field_term_ptr,field_term_length))
+ {
+ enclosed=1;
+ row_start=buffer+1;
+ row_end= to;
+ return 0;
+ }
+ /* Copy the found '"' character */
+ PUSH(chr);
+ chr='"';
+ }
+ else if (chr == field_term_char && found_enclosed_char == INT_MAX)
+ {
+ if (terminator(field_term_ptr,field_term_length))
+ {
+ enclosed=0;
+ row_start=buffer;
+ row_end= to;
+ return 0;
+ }
+ }
+ *to++ = (byte) chr;
+ }
+ /*
+ ** We come here if buffer is too small. Enlarge it and continue
+ */
+ if (!(new_buffer=(byte*) my_realloc((char*) buffer,buff_length+1+IO_SIZE,
+ MYF(MY_WME))))
+ return (error=1);
+ to=new_buffer + (to-buffer);
+ buffer=new_buffer;
+ buff_length+=IO_SIZE;
+ end_of_buff=buffer+buff_length;
+ }
+
+found_eof:
+ enclosed=0;
+ found_end_of_line=eof=1;
+ row_start=buffer;
+ row_end=to;
+ return 0;
+}
+
+/*
+** One can't use fixed length with multi-byte charset **
+*/
+
+int READ_INFO::read_fixed_length()
+{
+ int chr;
+ byte *to;
+ if (found_end_of_line)
+ return 1; // One have to call next_line
+
+ if (start_of_line)
+ { // Skipp until line_start
+ start_of_line=0;
+ if (find_start_of_fields())
+ return 1;
+ }
+
+ to=row_start=buffer;
+ while (to < end_of_buff)
+ {
+ if ((chr=GET) == my_b_EOF)
+ goto found_eof;
+ if (chr == escape_char)
+ {
+ if ((chr=GET) == my_b_EOF)
+ {
+ *to++= (byte) escape_char;
+ goto found_eof;
+ }
+ *to++ =(byte) unescape((char) chr);
+ continue;
+ }
+ if (chr == line_term_char)
+ {
+ if (terminator(line_term_ptr,line_term_length))
+ { // Maybe unexpected linefeed
+ found_end_of_line=1;
+ row_end= to;
+ return 0;
+ }
+ }
+ *to++ = (byte) chr;
+ }
+ row_end=to; // Found full line
+ return 0;
+
+found_eof:
+ found_end_of_line=eof=1;
+ row_start=buffer;
+ row_end=to;
+ return to == buffer ? 1 : 0;
+}
+
+
+int READ_INFO::next_line()
+{
+ line_cuted=0;
+ start_of_line= line_start_ptr != 0;
+ if (found_end_of_line || eof)
+ {
+ found_end_of_line=0;
+ return eof;
+ }
+ found_end_of_line=0;
+ if (!line_term_length)
+ return 0; // No lines
+ for (;;)
+ {
+ int chr = GET;
+#ifdef USE_MB
+ if (use_mb(default_charset_info) && my_ismbhead(default_charset_info, chr))
+ {
+ for (int i=1;
+ chr != my_b_EOF && i<my_mbcharlen(default_charset_info, chr);
+ i++)
+ chr = GET;
+ if (chr == escape_char)
+ continue;
+ }
+#endif
+ if (chr == my_b_EOF)
+ {
+ eof=1;
+ return 1;
+ }
+ if (chr == escape_char)
+ {
+ line_cuted=1;
+ if (GET == my_b_EOF)
+ return 1;
+ continue;
+ }
+ if (chr == line_term_char && terminator(line_term_ptr,line_term_length))
+ return 0;
+ line_cuted=1;
+ }
+}
+
+
+bool READ_INFO::find_start_of_fields()
+{
+ int chr;
+ try_again:
+ do
+ {
+ if ((chr=GET) == my_b_EOF)
+ {
+ found_end_of_line=eof=1;
+ return 1;
+ }
+ } while ((char) chr != line_start_ptr[0]);
+ for (char *ptr=line_start_ptr+1 ; ptr != line_start_end ; ptr++)
+ {
+ chr=GET; // Eof will be checked later
+ if ((char) chr != *ptr)
+ { // Can't be line_start
+ PUSH(chr);
+ while (--ptr != line_start_ptr)
+ { // Restart with next char
+ PUSH((uchar) *ptr);
+ }
+ goto try_again;
+ }
+ }
+ return 0;
+}
diff --git a/sql/sql_map.cc b/sql/sql_map.cc
new file mode 100644
index 00000000000..4578b85d10a
--- /dev/null
+++ b/sql/sql_map.cc
@@ -0,0 +1,147 @@
+/* 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 __GNUC__
+#pragma implementation // gcc: Class implementation
+#endif
+
+#include "mysql_priv.h"
+#ifdef HAVE_MMAP
+#include <sys/mman.h>
+#include <sys/stat.h>
+#endif
+
+#ifndef MAP_NORESERVE
+#define MAP_NORESERVE 0 // For IRIX
+#endif
+
+mapped_files::mapped_files(const my_string filename,byte *magic,uint magic_length)
+{
+#ifdef HAVE_MMAP
+ name=my_strdup(filename,MYF(0));
+ use_count=1;
+ error=0;
+ map=0;
+ size=0;
+ if ((file=my_open(name,O_RDONLY,MYF(MY_WME))) >= 0)
+ {
+ struct stat stat_buf;
+ if (!fstat(file,&stat_buf))
+ {
+ if (!(map=(byte*) mmap(0,(size=(ulong) stat_buf.st_size),PROT_READ,
+ MAP_SHARED | MAP_NORESERVE,file,
+ 0L)))
+ {
+ error=errno;
+ my_printf_error(0,"Can't map file: %s, errno: %d",MYF(0),
+ (my_string) name,error);
+ }
+ }
+ if (map && memcmp(map,magic,magic_length))
+ {
+ my_printf_error(0,"Wrong magic in %s",MYF(0),name);
+ VOID(munmap(map,size));
+ map=0;
+ }
+ if (!map)
+ {
+ VOID(my_close(file,MYF(0)));
+ file= -1;
+ }
+ }
+#endif
+}
+
+
+mapped_files::~mapped_files()
+{
+#ifdef HAVE_MMAP
+ if (file >= 0)
+ {
+ VOID(munmap((caddr_t) map,size));
+ VOID(my_close(file,MYF(0)));
+ file= -1; map=0;
+ }
+ my_free(name,MYF(0));
+#endif
+}
+
+
+static I_List<mapped_files> maps_in_use;
+
+/*
+** Check if a file is mapped. If it is, then return pointer to old map,
+** else alloc new object
+*/
+
+mapped_files *map_file(const my_string name,byte *magic,uint magic_length)
+{
+#ifdef HAVE_MMAP
+ VOID(pthread_mutex_lock(&LOCK_mapped_file));
+ I_List_iterator<mapped_files> list(maps_in_use);
+ mapped_files *map;
+ char path[FN_REFLEN];
+ sprintf(path,"%s/%s/%s.uniq",mysql_data_home,current_thd->db,name);
+ (void) unpack_filename(path,path);
+
+ while ((map=list++))
+ {
+ if (!strcmp(path,map->name))
+ break;
+ }
+ if (!map)
+ {
+ map=new mapped_files(path,magic,magic_length);
+ maps_in_use.append(map);
+ }
+ else
+ {
+ map->use_count++;
+ if (!map->map)
+ my_printf_error(0,"Can't map file: %s, error: %d",MYF(0),path,
+ map->error);
+ }
+ VOID(pthread_mutex_unlock(&LOCK_mapped_file));
+ return map;
+#else
+ return NULL;
+#endif
+}
+
+/*
+** free the map if there are no more users for it
+*/
+
+void unmap_file(mapped_files *map)
+{
+#ifdef HAVE_MMAP
+ VOID(pthread_mutex_lock(&LOCK_mapped_file));
+ if (!map->use_count--)
+ delete map;
+ VOID(pthread_mutex_unlock(&LOCK_mapped_file));
+#endif
+}
+
+/*****************************************************************************
+** Instansiate templates
+*****************************************************************************/
+
+#ifdef __GNUC__
+/* Used templates */
+template class I_List<mapped_files>;
+template class I_List_iterator<mapped_files>;
+#endif
diff --git a/sql/sql_map.h b/sql/sql_map.h
new file mode 100644
index 00000000000..e9093672fef
--- /dev/null
+++ b/sql/sql_map.h
@@ -0,0 +1,59 @@
+/* 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 */
+
+
+/* interface for memory mapped files */
+
+#ifdef __GNUC__
+#pragma interface /* gcc class implementation */
+#endif
+
+class mapped_files :public ilink {
+ byte *map;
+ ha_rows size;
+ char *name; // name of mapped file
+ File file; // >= 0 if open
+ int error; // If not mapped
+ uint use_count;
+
+public:
+ mapped_files(const my_string name,byte *magic,uint magic_length);
+ ~mapped_files();
+
+ friend class mapped_file;
+ friend mapped_files *map_file(const my_string name,byte *magic,
+ uint magic_length);
+ friend void unmap_file(mapped_files *map);
+};
+
+
+class mapped_file
+{
+ mapped_files *file;
+public:
+ mapped_file(const my_string name,byte *magic,uint magic_length)
+ {
+ file=map_file(name,magic,magic_length); /* old or new map */
+ }
+ ~mapped_file()
+ {
+ unmap_file(file); /* free map */
+ }
+ byte *map()
+ {
+ return file->map;
+ }
+};
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
new file mode 100644
index 00000000000..0c7b0b8187f
--- /dev/null
+++ b/sql/sql_parse.cc
@@ -0,0 +1,2627 @@
+/* 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 "sql_acl.h"
+#include <m_ctype.h>
+#include <thr_alarm.h>
+#include <myisam.h>
+#include <my_dir.h>
+
+#define SCRAMBLE_LENGTH 8
+
+extern pthread_handler_decl(handle_slave,arg);
+extern bool slave_running;
+extern char* master_host;
+extern pthread_t slave_real_id;
+extern MASTER_INFO glob_mi;
+extern my_string opt_bin_logname, master_info_file;
+extern I_List<i_string> binlog_do_db, binlog_ignore_db;
+
+extern int yyparse(void);
+extern "C" pthread_mutex_t THR_LOCK_keycache;
+
+static bool check_table_access(THD *thd,uint want_access,TABLE_LIST *tables);
+static bool check_lock_tables(THD *thd,TABLE_LIST *tables);
+static bool check_dup(THD *thd,const char *db,const char *name,
+ TABLE_LIST *tables);
+static void mysql_init_query(THD *thd);
+static void remove_escape(char *name);
+static void kill_one_thread(THD *thd, ulong thread);
+static void refresh_status(void);
+static int start_slave(THD* thd = 0, bool net_report = 1);
+static int stop_slave(THD* thd = 0, bool net_report = 1);
+static int change_master(THD* thd);
+static void reset_slave();
+static void reset_master();
+
+
+static const char *any_db="*any*"; // Special symbol for check_access
+
+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","Start Slave", "Abort Slave"
+};
+
+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)
+{
+#ifndef DBUG_OFF
+ MessageBox(NULL,"Test signal","DBUG",MB_OK);
+#endif
+}
+static void init_signals(void)
+{
+ int signals[7] = {SIGINT,SIGILL,SIGFPE,SIGSEGV,SIGTERM,SIGBREAK,SIGABRT } ;
+ for(int i=0 ; i < 7 ; i++)
+ signal( signals[i], test_signal) ;
+}
+#endif
+
+/*
+** 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,
+ const char *passwd, const char *db, bool check_count)
+{
+ NET *net= &thd->net;
+ thd->db=0;
+
+ if (!(thd->user = my_strdup(user, MYF(0))))
+ {
+ send_error(net,ER_OUT_OF_RESOURCES);
+ return 1;
+ }
+ thd->master_access=acl_getroot(thd->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,
+ 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,
+ passwd[0] ? ER(ER_YES) : ER(ER_NO));
+ mysql_log.write(COM_CONNECT,ER(ER_ACCESS_DENIED_ERROR),
+ thd->user,
+ thd->host ? thd->host : thd->ip ? thd->ip : "unknown ip",
+ passwd[0] ? ER(ER_YES) : ER(ER_NO));
+ return(1); // Error already given
+ }
+ if (check_count)
+ {
+ VOID(pthread_mutex_lock(&LOCK_thread_count));
+ bool tmp=(thread_count - delayed_insert_threads >= max_connections &&
+ !(thd->master_access & PROCESS_ACL));
+ VOID(pthread_mutex_unlock(&LOCK_thread_count));
+ if (tmp)
+ { // Too many connections
+ send_error(net, ER_CON_COUNT_ERROR);
+ return(1);
+ }
+ }
+ mysql_log.write(command,
+ (thd->priv_user == thd->user ?
+ (char*) "%s@%s on %s" :
+ (char*) "%s@%s as anonymous on %s"),
+ user,
+ thd->host ? thd->host : thd->ip ? thd->ip : "unknown ip",
+ db ? db : (char*) "");
+ thd->db_access=0;
+ if (db && db[0])
+ return test(mysql_change_db(thd,db));
+ else
+ send_ok(net); // Ready to handle questions
+ return 0; // ok
+}
+
+
+/*
+** 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
+ */
+ DBUG_PRINT("info", (("check_connections called by thread %d"),
+ thd->thread_id));
+ DBUG_PRINT("general",("New connection received on %s",
+ vio_description(net->vio)));
+ if (!thd->host) // If TCP/IP connection
+ {
+ char ip[17];
+
+ if (vio_peer_addr(net->vio,ip))
+ return (ER_BAD_HOST_ERROR);
+ if (!(thd->ip = my_strdup(ip,MYF(0))))
+ return (ER_OUT_OF_RESOURCES);
+#if !defined(HAVE_SYS_UN_H) || defined(HAVE_mit_thread)
+ /* Fast local hostname resolve for Win32 */
+ if (!strcmp(thd->ip,"127.0.0.1"))
+ {
+ if (!(thd->host=my_strdup("localhost",MYF(0))))
+ return (ER_OUT_OF_RESOURCES);
+ }
+ else
+#endif
+ if (!(specialflag & SPECIAL_NO_RESOLVE))
+ {
+ vio_in_addr(net->vio,&thd->remote.sin_addr);
+ thd->host=ip_to_hostname(&thd->remote.sin_addr,&connect_errors);
+ 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"));
+ if (acl_check_host(thd->host,thd->ip))
+ return(ER_HOST_NOT_PRIVILEGED);
+ }
+ else /* No hostname means that the connection was on a socket */
+ {
+ DBUG_PRINT("general",("Host: localhost"));
+ thd->ip=0;
+ bzero((char*) &thd->remote,sizeof(struct sockaddr));
+ }
+ vio_keepalive(net->vio, TRUE);
+
+ /* nasty, but any other way? */
+ uint pkt_len = 0;
+ {
+ char buff[60],*end;
+ int client_flags = CLIENT_LONG_FLAG | CLIENT_CONNECT_WITH_DB |
+ CLIENT_TRANSACTIONS;
+ LINT_INIT(pkt_len);
+
+ end=strmov(buff,server_version)+1;
+ int4store((uchar*) end,thd->thread_id);
+ end+=4;
+ memcpy(end,thd->scramble,SCRAMBLE_LENGTH+1);
+ end+=SCRAMBLE_LENGTH +1;
+#ifdef HAVE_COMPRESS
+ client_flags |= CLIENT_COMPRESS;
+#endif /* HAVE_COMPRESS */
+#ifdef HAVE_OPENSSL
+ if (ssl_acceptor_fd!=0)
+ 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;
+ int2store(end+3,thd->server_status);
+ bzero(end+5,13);
+ end+=18;
+ if (net_write_command(net,protocol_version, buff,
+ (uint) (end-buff)) ||
+ (pkt_len=my_net_read(net)) == packet_error ||
+ pkt_len < MIN_HANDSHAKE_SIZE)
+ {
+ inc_host_errors(&thd->remote.sin_addr);
+ return(ER_HANDSHAKE_ERROR);
+ }
+ }
+#ifdef _CUSTOMCONFIG_
+#include "_cust_sql_parse.h"
+#endif
+ if (connect_errors)
+ reset_host_errors(&thd->remote.sin_addr);
+ if (thd->packet.alloc(net_buffer_length))
+ return(ER_OUT_OF_RESOURCES);
+
+ thd->client_capabilities=uint2korr(net->read_pos);
+#ifdef HAVE_OPENSSL
+ DBUG_PRINT("info",
+ ("pkt_len:%d, client capabilities: %d",
+ pkt_len, 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);
+ 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"));
+ inc_host_errors(&thd->remote.sin_addr);
+ return(ER_HANDSHAKE_ERROR);
+ }
+ }
+ else
+ {
+ DBUG_PRINT("info", ("Leaving IO layer intact"));
+ if (pkt_len < NORMAL_HANDSHAKE_SIZE)
+ {
+ inc_host_errors(&thd->remote.sin_addr);
+ return ER_HANDSHAKE_ERROR;
+ }
+ }
+#endif
+
+ thd->max_packet_length=uint3korr(net->read_pos+2);
+ char *user= (char*) net->read_pos+5;
+ char *passwd= strend(user)+1;
+ char *db=0;
+ if (passwd[0] && strlen(passwd) != SCRAMBLE_LENGTH)
+ return ER_HANDSHAKE_ERROR;
+ if (thd->client_capabilities & CLIENT_CONNECT_WITH_DB)
+ db=strend(passwd)+1;
+ if (thd->client_capabilities & CLIENT_INTERACTIVE)
+ thd->inactive_timeout=net_interactive_timeout;
+ if (thd->client_capabilities & CLIENT_TRANSACTIONS)
+ thd->net.return_status= &thd->server_status;
+ net->timeout=net_read_timeout;
+ if (check_user(thd,COM_CONNECT, user, passwd, db, 1))
+ return (-1);
+ thd->password=test(passwd[0]);
+ return 0;
+}
+
+
+pthread_handler_decl(handle_one_connection,arg)
+{
+ THD *thd=(THD*) arg;
+ uint launch_time =
+ (thd->thr_create_time = time(NULL)) - thd->connect_time;
+ if (launch_time >= slow_launch_time)
+ statistic_increment(slow_launch_threads,&LOCK_status );
+
+ pthread_detach_this_thread();
+
+#ifndef __WIN__ /* Win32 calls this in pthread_create */
+ if (my_thread_init()) // needed to be called first before we call
+ // DBUG_ macros
+ {
+ close_connection(&thd->net,ER_OUT_OF_RESOURCES);
+ statistic_increment(aborted_connects,&LOCK_thread_count);
+ 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
+
+ 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_*
+
+#ifdef __WIN__
+ init_signals(); // IRENA; testing ?
+#else
+ sigset_t set;
+ VOID(sigemptyset(&set)); // Get mask in use
+ VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals));
+#endif
+ if (thd->store_globals())
+ {
+ close_connection(&thd->net,ER_OUT_OF_RESOURCES);
+ statistic_increment(aborted_connects,&LOCK_thread_count);
+ end_thread(thd,0);
+ return 0;
+ }
+
+ do
+ {
+ 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);
+#ifdef __NT__
+ if (vio_type(net->vio) == VIO_TYPE_NAMEDPIPE)
+ sleep(1); /* must wait after eof() */
+#endif
+ statistic_increment(aborted_connects,&LOCK_thread_count);
+ goto end_thread;
+ }
+
+ thd->alloc.free=thd->alloc.used=0;
+ if (thd->max_join_size == HA_POS_ERROR)
+ thd->options |= OPTION_BIG_SELECTS;
+ if (thd->client_capabilities & CLIENT_COMPRESS)
+ net->compress=1; // Use compression
+ if (thd->options & OPTION_ANSI_MODE)
+ thd->client_capabilities|=CLIENT_IGNORE_SPACE;
+
+ thd->proc_info=0;
+ thd->version=refresh_version;
+ thd->set_time();
+ while (!net->error && net->vio != 0 && !thd->killed)
+ {
+ if (do_command(thd))
+ break;
+ }
+ if (net->error && net->vio != 0)
+ {
+ sql_print_error(ER(ER_NEW_ABORTING_CONNECTION),
+ thd->thread_id,(thd->db ? thd->db : "unconnected"),
+ thd->user,
+ (thd->host ? thd->host : thd->ip ? thd->ip : "unknown"),
+ (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);
+ }
+
+end_thread:
+ close_connection(net);
+ end_thread(thd,1);
+ /*
+ If end_thread returns, we are either running with --one-thread
+ or this thread has been schedule to handle the next query
+ */
+ thd= current_thd;
+ } while (!(test_flags & TEST_NO_THREADS));
+ /* The following is only executed if we are not using --one-thread */
+ return(0); /* purecov: deadcode */
+}
+
+
+int handle_bootstrap(THD *thd,FILE *file)
+{
+ DBUG_ENTER("handle_bootstrap");
+ thd->thread_stack= (char*) &thd;
+
+ if (init_thr_lock() ||
+ my_pthread_setspecific_ptr(THR_THD, thd) ||
+ my_pthread_setspecific_ptr(THR_MALLOC, &thd->alloc) ||
+ my_pthread_setspecific_ptr(THR_NET, &thd->net))
+ {
+ close_connection(&thd->net,ER_OUT_OF_RESOURCES);
+ DBUG_RETURN(-1);
+ }
+ thd->mysys_var=my_thread_var;
+ thd->dbug_thread_id=my_thread_id();
+#ifndef __WIN__
+ sigset_t set;
+ VOID(sigemptyset(&set)); // Get mask in use
+ VOID(pthread_sigmask(SIG_UNBLOCK,&set,&thd->block_signals));
+#endif
+
+ thd->alloc.free=thd->alloc.used=0;
+ if (thd->max_join_size == (ulong) ~0L)
+ thd->options |= OPTION_BIG_SELECTS;
+
+ thd->proc_info=0;
+ thd->version=refresh_version;
+
+ char *buff= (char*) thd->net.buff;
+ while (fgets(buff, thd->net.max_packet, file))
+ {
+ uint length=strlen(buff);
+ while (length && (isspace(buff[length-1]) || buff[length-1] == ';'))
+ length--;
+ buff[length]=0;
+ init_sql_alloc(&thd->alloc,8192);
+ thd->current_tablenr=0;
+ thd->query= sql_memdup(buff,length+1);
+ thd->query_id=query_id++;
+ mysql_parse(thd,thd->query,length);
+ close_thread_tables(thd); // Free tables
+ if (thd->fatal_error)
+ {
+ DBUG_RETURN(-1);
+ }
+ free_root(&thd->alloc);
+ }
+ DBUG_RETURN(0);
+}
+
+
+static inline void free_items(THD *thd)
+{
+ /* This works because items are allocated with sql_alloc() */
+ for (Item *item=thd->free_list ; item ; item=item->next)
+ delete item;
+}
+
+int mysql_table_dump(THD* thd, char* db, char* tbl_name, int fd)
+{
+ TABLE* table;
+ TABLE_LIST* table_list;
+ 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))))
+ DBUG_RETURN(1); // out of memory
+ table_list->db = db;
+ table_list->real_name = table_list->name = tbl_name;
+ table_list->lock_type = TL_READ_NO_INSERT;
+ table_list->next = 0;
+ remove_escape(table_list->real_name);
+
+ if(!(table=open_ltable(thd, table_list, TL_READ_NO_INSERT)))
+ DBUG_RETURN(1);
+
+ if(check_access(thd, SELECT_ACL, db, &table_list->grant.privilege))
+ goto err;
+ if(grant_option && check_grant(thd, SELECT_ACL, table_list))
+ goto err;
+
+ thd->free_list = 0;
+ thd->query = tbl_name;
+ if((error = mysqld_dump_create_info(thd, table, -1)))
+ {
+ my_error(ER_GET_ERRNO, MYF(0));
+ goto err;
+ }
+ net_flush(&thd->net);
+ error = table->file->dump(thd,fd);
+ if(error)
+ my_error(ER_GET_ERRNO, MYF(0));
+
+err:
+
+ close_thread_tables(thd);
+
+ DBUG_RETURN(error);
+}
+
+
+
+ /* Execute one command from socket (query or simple command) */
+
+bool do_command(THD *thd)
+{
+ char *packet;
+ uint old_timeout,packet_length;
+ bool error=0;
+ NET *net;
+ enum enum_server_command command;
+ DBUG_ENTER("do_command");
+
+ init_sql_alloc(&thd->alloc,8192);
+ net= &thd->net;
+ thd->current_tablenr=0;
+
+ packet=0;
+ old_timeout=net->timeout;
+ net->timeout=thd->inactive_timeout; /* Wait max for 8 hours */
+ 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;
+ }
+ else
+ {
+ packet=(char*) net->read_pos;
+ command = (enum enum_server_command) (uchar) packet[0];
+ DBUG_PRINT("general",("Command on socket %s = %d (%s)",
+ vio_description(net->vio), command,
+ command_name[command]));
+ }
+ net->timeout=old_timeout; /* Timeout */
+ thd->command=command;
+ 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();
+ switch(command) {
+ case COM_INIT_DB:
+ if (!mysql_change_db(thd,packet+1))
+ mysql_log.write(command,"%s",thd->db);
+ break;
+ case COM_TABLE_DUMP:
+ {
+ char* data = packet + 1;
+ uint db_len = *data;
+ uint tbl_len = *(data + db_len + 1);
+ char* db = sql_alloc(db_len + tbl_len + 2);
+ memcpy(db, data + 1, db_len);
+ char* tbl_name = db + db_len;
+ *tbl_name++ = 0;
+ memcpy(tbl_name, data + 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
+
+ break;
+ }
+ case COM_CHANGE_USER:
+ {
+ char *user= (char*) packet+1;
+ 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;
+ char *save_user= thd->user;
+ char *save_priv_user= thd->priv_user;
+ char *save_db= thd->db;
+
+ if ((uint) ((uchar*) db - net->read_pos) > packet_length)
+ { // Check if protocol is ok
+ send_error(net, ER_UNKNOWN_COM_ERROR);
+ break;
+ }
+ if (check_user(thd, COM_CHANGE_USER, user, passwd, db,0))
+ { // Restore old user
+ x_free(thd->user);
+ x_free(thd->db);
+ thd->master_access=save_master_access;
+ thd->db_access=save_db_access;
+ thd->db=save_db;
+ thd->user=save_user;
+ thd->priv_user=save_priv_user;
+ break;
+ }
+ x_free((gptr) save_db);
+ x_free((gptr) save_user);
+ thd->password=test(passwd[0]);
+ break;
+ }
+
+ case COM_QUERY:
+ {
+ char *pos=packet+packet_length; // Point at end null
+ /* Remove garage at end of query */
+ while (packet_length > 0 && pos[-1] == ';')
+ {
+ pos--;
+ packet_length--;
+ }
+ *pos=0;
+ if (!(thd->query= (char*) sql_memdup((gptr) (packet+1),packet_length)))
+ break;
+ thd->packet.shrink(net_buffer_length); // Reclaim some memory
+ if (!(specialflag & SPECIAL_NO_PRIOR))
+ my_pthread_setprio(pthread_self(),QUERY_PRIOR);
+ mysql_log.write(command,"%s",thd->query);
+ DBUG_PRINT("query",("%s",thd->query));
+ mysql_parse(thd,thd->query,packet_length-1);
+ 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
+#ifdef DONT_ALLOW_SHOW_COMMANDS
+ send_error(&thd->net,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
+ break;
+#else
+ {
+ char *fields;
+ TABLE_LIST table_list;
+ bzero((char*) &table_list,sizeof(table_list));
+ if (!(table_list.db=thd->db))
+ {
+ send_error(net,ER_NO_DB_ERROR);
+ break;
+ }
+ thd->free_list=0;
+ table_list.name=table_list.real_name=sql_strdup(packet+1);
+ thd->query=fields=sql_strdup(strend(packet+1)+1);
+ mysql_log.write(command,"%s %s",table_list.real_name,fields);
+ remove_escape(table_list.real_name); // This can't have wildcards
+
+ if (check_access(thd,SELECT_ACL,table_list.db,&thd->col_access))
+ break;
+ table_list.grant.privilege=thd->col_access;
+ if (grant_option && check_grant(thd,SELECT_ACL,&table_list,2))
+ break;
+ mysqld_list_fields(thd,&table_list,fields);
+ free_items(thd);
+ break;
+ }
+#endif
+ case COM_QUIT:
+ mysql_log.write(command,NullS);
+ net->error=0; // Don't give 'abort' message
+ error=TRUE; // End server
+ break;
+
+ case COM_CREATE_DB:
+ {
+ char *db=sql_strdup(packet+1);
+ if (check_access(thd,CREATE_ACL,db,0,1))
+ break;
+ mysql_log.write(command,packet+1);
+ mysql_create_db(thd,db,0);
+ break;
+ }
+ case COM_DROP_DB:
+ {
+ char *db=sql_strdup(packet+1);
+ if (check_access(thd,DROP_ACL,db,0,1))
+ break;
+ mysql_log.write(command,db);
+ mysql_rm_db(thd,db,0);
+ break;
+ }
+ case COM_BINLOG_DUMP:
+ {
+ if(check_access(thd, FILE_ACL, any_db))
+ break;
+ mysql_log.write(command, 0);
+
+ ulong pos;
+ ushort flags;
+ pos = uint4korr(packet + 1);
+ flags = uint2korr(packet + 5);
+ mysql_binlog_send(thd, sql_strdup(packet + 7), pos, flags);
+ break;
+ }
+ case COM_REFRESH:
+ {
+ uint options=(uchar) packet[1];
+ if (check_access(thd,RELOAD_ACL,any_db))
+ break;
+ mysql_log.write(command,NullS);
+ if (reload_acl_and_cache(options))
+ send_error(net,0);
+ else
+ send_eof(net);
+ break;
+ }
+ case COM_SHUTDOWN:
+ if (check_access(thd,SHUTDOWN_ACL,any_db))
+ break; /* purecov: inspected */
+ DBUG_PRINT("quit",("Got shutdown command"));
+ mysql_log.write(command,NullS);
+ send_eof(net);
+#ifdef __WIN__
+ sleep(1); // must wait after eof()
+#endif
+ send_eof(net); // This is for 'quit request'
+ close_connection(net);
+ close_thread_tables(thd); // Free before kill
+ free_root(&thd->alloc);
+ kill_mysql();
+ error=TRUE;
+ break;
+
+ case COM_STATISTICS:
+ {
+ mysql_log.write(command,NullS);
+ char buff[200];
+ ulong uptime = (ulong) (time((time_t*) 0) - 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,
+ (int) thread_count,thd->query_id,long_query_count,
+ opened_tables,refresh_version, cached_tables(),
+ uptime ? (float)thd->query_id/(float)uptime : 0);
+#ifdef SAFEMALLOC
+ if (lCurMemory) // Using SAFEMALLOC
+ sprintf(strend(buff), " Memory in use: %ldK Max memory used: %ldK",
+ (lCurMemory+1023L)/1024L,(lMaxMemory+1023L)/1024L);
+ #endif
+ VOID(my_net_write(net, buff,strlen(buff)));
+ VOID(net_flush(net));
+ break;
+ }
+ case COM_PING:
+ send_ok(net); // Tell client we are alive
+ break;
+ case COM_PROCESS_INFO:
+ if (!thd->priv_user[0] && check_access(thd,PROCESS_ACL,any_db))
+ break;
+ mysql_log.write(command,NullS);
+ mysqld_list_processes(thd,thd->master_access & PROCESS_ACL ? NullS :
+ thd->priv_user,0);
+ break;
+ case COM_PROCESS_KILL:
+ {
+ ulong id=(ulong) uint4korr(packet+1);
+ kill_one_thread(thd,id);
+ break;
+ }
+ case COM_DEBUG:
+ if (check_access(thd,PROCESS_ACL,any_db))
+ break; /* purecov: inspected */
+ mysql_print_status(thd);
+ mysql_log.write(command,NullS);
+ send_eof(net);
+ break;
+ case COM_SLEEP:
+ case COM_CONNECT: // Impossible here
+ case COM_TIME: // Impossible from client
+ case COM_DELAYED_INSERT:
+ default:
+ send_error(net, ER_UNKNOWN_COM_ERROR);
+ break;
+ }
+ if (thd->lock || thd->open_tables)
+ {
+ thd->proc_info="closing tables";
+ close_thread_tables(thd); /* Free tables */
+ }
+ thd->proc_info="cleaning up";
+
+ if (thd->fatal_error)
+ send_error(net,0); // End of memory ?
+
+ time_t start_of_query=thd->start_time;
+ thd->set_time();
+ if ((ulong) (thd->start_time - start_of_query) > long_query_time)
+ {
+ long_query_count++;
+ mysql_slow_log.write(thd->query, thd->query_length,
+ (ulong) (thd->start_time - start_of_query));
+ }
+ VOID(pthread_mutex_lock(&LOCK_thread_count)); // For process list
+ thd->proc_info=0;
+ thd->command=COM_SLEEP;
+ thd->query=0;
+ thread_running--;
+ VOID(pthread_mutex_unlock(&LOCK_thread_count));
+ thd->packet.shrink(net_buffer_length); // Reclaim some memory
+ free_root(&thd->alloc);
+ DBUG_RETURN(error);
+}
+
+/****************************************************************************
+** mysql_execute_command
+** Execute command saved in thd and current_lex->sql_command
+****************************************************************************/
+
+void
+mysql_execute_command(void)
+{
+ int res=0;
+ THD *thd=current_thd;
+ LEX *lex=current_lex;
+ TABLE_LIST *tables=(TABLE_LIST*) lex->table_list.first;
+ DBUG_ENTER("mysql_execute_command");
+
+ switch (lex->sql_command) {
+ case SQLCOM_SELECT:
+ {
+ select_result *result;
+ if (lex->options & SELECT_DESCRIBE)
+ lex->exchange=0;
+ if (tables)
+ {
+ res=check_table_access(thd,
+ lex->exchange ? SELECT_ACL | FILE_ACL :
+ SELECT_ACL,
+ tables);
+ }
+ else
+ res=check_access(thd, lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL,
+ any_db);
+ if (res)
+ {
+ res=0;
+ 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->select_limit= HA_POS_ERROR; // no limit
+
+ if (lex->exchange)
+ {
+ if (lex->exchange->dumpfile)
+ {
+ if (!(result=new select_dump(lex->exchange)))
+ {
+ res= -1;
+ break;
+ }
+ }
+ else
+ {
+ if (!(result=new select_export(lex->exchange)))
+ {
+ res= -1;
+ break;
+ }
+ }
+ }
+ else if (!(result=new select_send()))
+ {
+ res= -1;
+#ifdef DELETE_ITEMS
+ delete lex->having;
+ delete lex->where;
+#endif
+ break;
+ }
+
+ if (lex->options & SELECT_HIGH_PRIORITY)
+ {
+ TABLE_LIST *table;
+ for (table = tables ; table ; table=table->next)
+ table->lock_type=TL_READ_HIGH_PRIORITY;
+ }
+
+ if (!(res=open_and_lock_tables(thd,tables)))
+ {
+ res=mysql_select(thd,tables,lex->item_list,
+ lex->where,
+ lex->ftfunc_list,
+ (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;
+#ifdef DELETE_ITEMS
+ delete lex->having;
+ delete lex->where;
+#endif
+ break;
+ }
+ case SQLCOM_CHANGE_MASTER:
+ {
+ if(check_access(thd, PROCESS_ACL, any_db))
+ goto error;
+ res = change_master(thd);
+ break;
+ }
+ case SQLCOM_SHOW_SLAVE_STAT:
+ {
+ if(check_access(thd, PROCESS_ACL, any_db))
+ goto error;
+ res = show_master_info(thd);
+ break;
+ }
+ case SQLCOM_SHOW_MASTER_STAT:
+ {
+ if(check_access(thd, PROCESS_ACL, any_db))
+ goto error;
+ res = show_binlog_info(thd);
+ break;
+ }
+ case SQLCOM_LOAD_MASTER_TABLE:
+
+ if (!tables->db)
+ tables->db=thd->db;
+ if (check_access(thd,CREATE_ACL,tables->db,&tables->grant.privilege))
+ goto error; /* purecov: inspected */
+ if (grant_option)
+ {
+ /* 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);
+ tables->next=tmp_table_list;
+ if (error)
+ goto error;
+ }
+ if (strlen(tables->name) > NAME_LEN)
+ {
+ net_printf(&thd->net,ER_WRONG_TABLE_NAME,tables->name);
+ res=0;
+ break;
+ }
+
+ thd->last_nx_table = tables->real_name;
+ thd->last_nx_db = tables->db;
+ if(fetch_nx_table(thd, &glob_mi))
+ // fetch_nx_table is responsible for sending
+ // the error
+ {
+ res = 0;
+ thd->net.no_send_ok = 0; // easier to do it here
+ // this way we make sure that when we are done, we are clean
+ break;
+ }
+
+ res = 0;
+ send_ok(&thd->net);
+ break;
+
+ case SQLCOM_CREATE_TABLE:
+#ifdef DEMO_VERSION
+ send_error(&thd->net,ER_NOT_ALLOWED_COMMAND);
+#else
+ if (check_access(thd,CREATE_ACL,tables->db,&tables->grant.privilege))
+ goto error; /* purecov: inspected */
+ if (grant_option)
+ {
+ /* 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);
+ tables->next=tmp_table_list;
+ if (error)
+ goto error;
+ }
+ if (strlen(tables->name) > NAME_LEN)
+ {
+ net_printf(&thd->net,ER_WRONG_TABLE_NAME,tables->name);
+ res=0;
+ break;
+ }
+ if (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))
+ {
+ net_printf(&thd->net,ER_INSERT_TABLE_USED,tables->real_name);
+ DBUG_VOID_RETURN;
+ }
+ if (tables->next)
+ {
+ if (check_table_access(thd, SELECT_ACL, tables->next))
+ goto error; // 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->select_limit= HA_POS_ERROR; // No limit
+
+ 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,
+ lex->ftfunc_list,
+ (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;
+ }
+ else
+ res= -1;
+ }
+ }
+ else // regular create
+ {
+ res = mysql_create_table(thd,tables->db ? tables->db : thd->db,
+ tables->real_name, &lex->create_info,
+ lex->create_list,
+ lex->key_list,0, 0); // do logging
+ if (!res)
+ send_ok(&thd->net);
+ }
+ break;
+ case SQLCOM_CREATE_INDEX:
+ if (!tables->db)
+ tables->db=thd->db;
+ if (check_access(thd,INDEX_ACL,tables->db,&tables->grant.privilege))
+ goto error; /* purecov: inspected */
+ if (grant_option && check_grant(thd,INDEX_ACL,tables))
+ goto error;
+ res = mysql_create_index(thd, tables, lex->key_list);
+#endif
+ break;
+
+ case SQLCOM_SLAVE_START:
+ start_slave(thd);
+ break;
+ case SQLCOM_SLAVE_STOP:
+ stop_slave(thd);
+ 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)
+ {
+ net_printf(&thd->net,ER_WRONG_TABLE_NAME,lex->name);
+ res=0;
+ break;
+ }
+ if (!lex->db)
+ 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))
+ goto error; /* purecov: inspected */
+ if (!tables->db)
+ tables->db=thd->db;
+ if (grant_option)
+ {
+ if (check_grant(thd,ALTER_ACL,tables))
+ goto error;
+ if (lex->name && !test_all_bits(priv,INSERT_ACL | CREATE_ACL))
+ { // Rename of table
+ TABLE_LIST tmp_table;
+ bzero((char*) &tmp_table,sizeof(tmp_table));
+ tmp_table.real_name=lex->name;
+ tmp_table.db=lex->db;
+ tmp_table.grant.privilege=priv;
+ if (check_grant(thd,INSERT_ACL | CREATE_ACL,tables))
+ goto error;
+ }
+ }
+ /* ALTER TABLE ends previous transaction */
+ if (!(thd->options & OPTION_AUTO_COMMIT) && ha_commit(thd))
+ res= -1;
+ else
+ res= mysql_alter_table(thd, lex->db, lex->name,
+ &lex->create_info,
+ tables, lex->create_list,
+ lex->key_list, lex->drop_list, lex->alter_list,
+ lex->drop_primary, lex->duplicates);
+ break;
+ }
+#endif
+ case SQLCOM_SHOW_CREATE:
+ {
+ if(! tables->db)
+ tables->db = thd->db;
+ if (!tables->db)
+ {
+ send_error(&thd->net,ER_NO_DB_ERROR); /* purecov: inspected */
+ goto error; /* purecov: inspected */
+ }
+ res = mysqld_show_create(thd, tables);
+ break;
+ }
+ case SQLCOM_REPAIR:
+ {
+ if (!tables->db)
+ tables->db=thd->db;
+ if (check_access(thd,SELECT_ACL | INSERT_ACL,tables->db,
+ &tables->grant.privilege))
+ goto error; /* purecov: inspected */
+ if (grant_option && check_grant(thd,SELECT_ACL | INSERT_ACL,tables))
+ goto error;
+ res = mysql_repair_table(thd, tables, &lex->check_opt);
+ break;
+ }
+ case SQLCOM_CHECK:
+ {
+ if (!tables->db)
+ tables->db=thd->db;
+ if (check_access(thd,SELECT_ACL,tables->db,
+ &tables->grant.privilege))
+ goto error; /* purecov: inspected */
+ if (grant_option && check_grant(thd,SELECT_ACL,tables))
+ goto error;
+ res = mysql_check_table(thd, tables, &lex->check_opt);
+ break;
+ }
+ case SQLCOM_ANALYZE:
+ {
+ if (!tables->db)
+ tables->db=thd->db;
+ if (check_access(thd,SELECT_ACL|INSERT_ACL,tables->db,
+ &tables->grant.privilege))
+ goto error; /* purecov: inspected */
+ if (grant_option && check_grant(thd,SELECT_ACL|INSERT_ACL,tables))
+ goto error;
+ res = mysql_analyze_table(thd, tables);
+ break;
+ }
+ case SQLCOM_OPTIMIZE:
+ {
+ HA_CREATE_INFO create_info;
+ /* This is now done with ALTER TABLE, but should be done with isamchk */
+ if (!tables->db)
+ tables->db=thd->db;
+ if (check_access(thd,SELECT_ACL | INSERT_ACL,tables->db,
+ &tables->grant.privilege))
+ goto error; /* purecov: inspected */
+ if (grant_option && check_grant(thd,SELECT_ACL | INSERT_ACL,tables))
+ goto error;
+
+ lex->create_list.empty();
+ lex->key_list.empty();
+ lex->col_list.empty();
+ lex->drop_list.empty();
+ lex->alter_list.empty();
+ bzero((char*) &create_info,sizeof(create_info));
+ create_info.db_type=DB_TYPE_DEFAULT;
+ create_info.row_type=ROW_TYPE_DEFAULT;
+ res= mysql_alter_table(thd, NullS, NullS, &create_info,
+ tables, lex->create_list,
+ lex->key_list, lex->drop_list, lex->alter_list,
+ 0,DUP_ERROR);
+ break;
+ }
+ case SQLCOM_UPDATE:
+ if (check_access(thd,UPDATE_ACL,tables->db,&tables->grant.privilege))
+ goto error;
+ if (grant_option && check_grant(thd,UPDATE_ACL,tables))
+ goto error;
+ if (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
+ break;
+ case SQLCOM_INSERT:
+ if (check_access(thd,INSERT_ACL,tables->db,&tables->grant.privilege))
+ goto error; /* purecov: inspected */
+ 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);
+ break;
+ case SQLCOM_REPLACE:
+ if (check_access(thd,INSERT_ACL | UPDATE_ACL | DELETE_ACL,
+ tables->db,&tables->grant.privilege))
+ goto error; /* purecov: inspected */
+ if (grant_option && check_grant(thd,INSERT_ACL | UPDATE_ACL | DELETE_ACL,
+ tables))
+
+ goto error;
+ res = mysql_insert(thd,tables,lex->field_list,lex->many_values,
+ DUP_REPLACE,
+ lex->lock_option);
+ 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
+ uint privilege= (lex->sql_command == SQLCOM_INSERT_SELECT ?
+ INSERT_ACL : INSERT_ACL | UPDATE_ACL | DELETE_ACL);
+ TABLE_LIST *save_next=tables->next;
+ tables->next=0;
+ if (check_access(thd, privilege,
+ tables->db,&tables->grant.privilege) ||
+ (grant_option && check_grant(thd, privilege, tables)))
+ goto error;
+ tables->next=save_next;
+ if ((res=check_table_access(thd, SELECT_ACL, save_next)))
+ goto error;
+
+ 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->select_limit= HA_POS_ERROR; // No limit
+
+ if (check_dup(thd,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
+ 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,
+ lex->ftfunc_list,
+ (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;
+ }
+#ifdef DELETE_ITEMS
+ delete lex->having;
+ delete lex->where;
+#endif
+ break;
+ }
+ case SQLCOM_DELETE:
+ {
+ if (check_access(thd,DELETE_ACL,tables->db,&tables->grant.privilege))
+ goto error; /* purecov: inspected */
+ if (grant_option && check_grant(thd,DELETE_ACL,tables))
+ goto error;
+ // Set privilege for the WHERE clause
+ tables->grant.want_privilege=(SELECT_ACL & ~tables->grant.privilege);
+ res = mysql_delete(thd,tables,lex->where,lex->select_limit,
+ lex->lock_option);
+#ifdef DELETE_ITEMS
+ delete lex->where;
+#endif
+ break;
+ }
+ case SQLCOM_DROP_TABLE:
+ {
+ if (check_table_access(thd,DROP_ACL,tables))
+ goto error; /* purecov: inspected */
+ res = mysql_rm_table(thd,tables,lex->drop_if_exists);
+ }
+ break;
+ case SQLCOM_DROP_INDEX:
+ if (!tables->db)
+ tables->db=thd->db;
+ if (check_access(thd,INDEX_ACL,tables->db,&tables->grant.privilege))
+ goto error; /* purecov: inspected */
+ if (grant_option && check_grant(thd,INDEX_ACL,tables))
+ goto error;
+ res = mysql_drop_index(thd, tables, lex->drop_list);
+ break;
+ case SQLCOM_SHOW_DATABASES:
+#if defined(DONT_ALLOW_SHOW_COMMANDS) || defined(DEMO_VERSION)
+ send_error(&thd->net,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
+ DBUG_VOID_RETURN;
+#else
+ if ((specialflag & SPECIAL_SKIP_SHOW_DB) &&
+ check_access(thd,PROCESS_ACL,any_db))
+ 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_access(thd,PROCESS_ACL,any_db))
+ 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);
+ break;
+ case SQLCOM_SHOW_VARIABLES:
+ res= mysqld_show(thd, (lex->wild ? lex->wild->ptr() : NullS),
+ init_vars);
+ break;
+ case SQLCOM_SHOW_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;
+ if (!db)
+ {
+ send_error(&thd->net,ER_NO_DB_ERROR); /* purecov: inspected */
+ goto error; /* purecov: inspected */
+ }
+ remove_escape(db); // Fix escaped '_'
+ if (strlen(db) > NAME_LEN)
+ {
+ net_printf(&thd->net,ER_WRONG_DB_NAME, db);
+ goto error;
+ }
+ if (check_access(thd,SELECT_ACL,db,&thd->col_access))
+ goto error; /* purecov: inspected */
+ /* grant is checked in mysqld_show_tables */
+ if (lex->options & SELECT_DESCRIBE)
+ res= mysqld_extend_show_tables(thd,db,
+ (lex->wild ? lex->wild->ptr() : NullS));
+ else
+ res= mysqld_show_tables(thd,db,
+ (lex->wild ? lex->wild->ptr() : NullS));
+ break;
+ }
+#endif
+ 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)
+ {
+ send_error(&thd->net,ER_NO_DB_ERROR); /* purecov: inspected */
+ goto error; /* purecov: inspected */
+ }
+ remove_escape(db); // Fix escaped '_'
+ remove_escape(tables->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;
+ if (grant_option && check_grant(thd,SELECT_ACL,tables,2))
+ goto error;
+ res= mysqld_show_fields(thd,tables,
+ (lex->wild ? lex->wild->ptr() : NullS));
+ break;
+ }
+#endif
+ case SQLCOM_SHOW_KEYS:
+#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)
+ {
+ send_error(&thd->net,ER_NO_DB_ERROR); /* purecov: inspected */
+ goto error; /* purecov: inspected */
+ }
+ remove_escape(db); // Fix escaped '_'
+ remove_escape(tables->name);
+ if (!tables->db)
+ tables->db=thd->db;
+ if (check_access(thd,SELECT_ACL,db,&thd->col_access))
+ goto error; /* purecov: inspected */
+ tables->grant.privilege=thd->col_access;
+ if (grant_option && check_grant(thd,SELECT_ACL,tables,2))
+ goto error;
+ res= mysqld_show_keys(thd,tables);
+ break;
+ }
+#endif
+ case SQLCOM_CHANGE_DB:
+ mysql_change_db(thd,lex->db);
+ break;
+ case SQLCOM_LOAD:
+ {
+ uint privilege= (lex->duplicates == DUP_REPLACE ?
+ INSERT_ACL | UPDATE_ACL | DELETE_ACL : INSERT_ACL);
+ if (!(lex->local_file && (thd->client_capabilities & CLIENT_LOCAL_FILES)))
+ {
+ if (check_access(thd,privilege | FILE_ACL,tables->db))
+ goto error;
+ }
+ else
+ {
+ if (check_access(thd,privilege,tables->db,&tables->grant.privilege) ||
+ grant_option && check_grant(thd,privilege,tables))
+ goto error;
+ }
+ res=mysql_load(thd, lex->exchange, tables, lex->field_list,
+ lex->duplicates, (bool) lex->local_file, lex->lock_option);
+ 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;
+ 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_AUTO_COMMIT)
+ {
+ if (org_options & OPTION_AUTO_COMMIT)
+ {
+ /* We changed to auto_commit mode */
+ thd->options&= ~OPTION_BEGIN;
+ thd->server_status|= SERVER_STATUS_AUTOCOMMIT;
+ if (ha_commit(thd))
+ {
+ res= -1;
+ break;
+ }
+ }
+ else
+ thd->server_status&= ~SERVER_STATUS_AUTOCOMMIT;
+ }
+ 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
+ }
+ 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);
+ }
+ 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);
+ }
+ if (check_lock_tables(thd,tables))
+ goto error;
+ thd->in_lock_tables=1;
+ if (!(res=open_and_lock_tables(thd,tables)))
+ {
+ thd->locked_tables=thd->lock;
+ thd->lock=0;
+ send_ok(&thd->net);
+ }
+ thd->in_lock_tables=0;
+ break;
+ case SQLCOM_CREATE_DB:
+ {
+ if (check_access(thd,CREATE_ACL,lex->name,0,1))
+ break;
+ mysql_create_db(thd,lex->name,lex->create_info.options);
+ break;
+ }
+ case SQLCOM_DROP_DB:
+ {
+ if (check_access(thd,DROP_ACL,lex->name,0,1))
+ break;
+ mysql_rm_db(thd,lex->name,lex->drop_if_exists);
+ break;
+ }
+ case SQLCOM_CREATE_FUNCTION:
+ if (check_access(thd,INSERT_ACL,"mysql",0,1))
+ break;
+#ifdef HAVE_DLOPEN
+ if (!(res = mysql_create_function(thd,&lex->udf)))
+ send_ok(&thd->net);
+#else
+ res= -1;
+#endif
+ break;
+ case SQLCOM_DROP_FUNCTION:
+ if (check_access(thd,DELETE_ACL,"mysql",0,1))
+ break;
+#ifdef HAVE_DLOPEN
+ if (!(res = mysql_drop_function(thd,lex->udf.name)))
+ send_ok(&thd->net);
+#else
+ 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 */
+
+ List_iterator <LEX_USER> user_list(lex->users_list);
+ LEX_USER *user;
+ if(thd->user)
+ 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->query,thd->query_length);
+ 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->query,thd->query_length);
+ Query_log_event qinfo(thd, thd->query);
+ mysql_bin_log.write(&qinfo);
+ }
+ }
+ break;
+ }
+ case SQLCOM_FLUSH:
+ if (check_access(thd,RELOAD_ACL,any_db))
+ goto error;
+ if (reload_acl_and_cache(lex->type))
+ send_error(&thd->net,0);
+ else
+ send_ok(&thd->net);
+ break;
+ case SQLCOM_KILL:
+ kill_one_thread(thd,lex->thread_id);
+ break;
+ case SQLCOM_SHOW_GRANTS:
+ res=0;
+ if ((thd->user && !strcmp(thd->user,lex->grant_user->user.str)) ||
+ !(check_access(thd, SELECT_ACL, "mysql")))
+ {
+ res = mysql_show_grants(thd,lex->grant_user);
+ }
+ break;
+ case SQLCOM_BEGIN:
+ thd->options|= OPTION_BEGIN;
+ thd->server_status|= SERVER_STATUS_IN_TRANS;
+ break;
+ case SQLCOM_COMMIT:
+ thd->options&= ~OPTION_BEGIN;
+ thd->server_status&= ~SERVER_STATUS_IN_TRANS;
+ if (!ha_commit(thd))
+ send_ok(&thd->net);
+ else
+ res= -1;
+ break;
+ case SQLCOM_ROLLBACK:
+ thd->options&= ~OPTION_BEGIN;
+ thd->server_status&= ~SERVER_STATUS_IN_TRANS;
+ if (!ha_rollback(thd))
+ send_ok(&thd->net);
+ else
+ res= -1;
+ break;
+ default: /* Impossible */
+ send_ok(&thd->net);
+ break;
+ }
+ thd->proc_info="query end"; // QQ
+ if (res < 0)
+ send_error(&thd->net,thd->killed ? ER_SERVER_SHUTDOWN : 0);
+
+error:
+ DBUG_VOID_RETURN;
+}
+
+
+/****************************************************************************
+** 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.
+****************************************************************************/
+
+bool
+check_access(THD *thd,uint want_access,const char *db, uint *save_priv,
+ bool no_grant)
+{
+ uint db_access,dummy;
+ if (save_priv)
+ *save_priv=0;
+ else
+ save_priv= &dummy;
+
+ if (!db && !thd->db && !no_grant)
+ {
+ send_error(&thd->net,ER_NO_DB_ERROR); /* purecov: tested */
+ return TRUE; /* purecov: tested */
+ }
+
+ if ((thd->master_access & want_access) == want_access)
+ {
+ *save_priv=thd->master_access;
+ return FALSE;
+ }
+ if ((want_access & ~thd->master_access) & ~(DB_ACLS | EXTRA_ACL) ||
+ ! db && no_grant)
+ { // 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 (db == any_db)
+ 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 */
+ else
+ db_access=thd->db_access;
+ want_access &= ~EXTRA_ACL; // Remove SHOW attribute
+ db_access= ((*save_priv=(db_access | thd->master_access)) & want_access);
+ if (db_access == want_access ||
+ ((grant_option && !no_grant) && !(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 */
+}
+
+
+/*
+** Check the privilege for all used tables. Table privileges are cached
+** in the table list for GRANT checking
+*/
+
+static bool
+check_table_access(THD *thd,uint want_access,TABLE_LIST *tables)
+{
+ uint found=0,found_access=0;
+ TABLE_LIST *org_tables=tables;
+ for (; tables ; tables=tables->next)
+ {
+ if ((thd->master_access & want_access) == want_access && thd->db)
+ tables->grant.privilege= want_access;
+ else if (tables->db)
+ {
+ if (found && !grant_option) // db already checked
+ tables->grant.privilege=found_access;
+ else
+ {
+ if (check_access(thd,want_access,tables->db,&tables->grant.privilege))
+ return TRUE; // Access denied
+ found_access=tables->grant.privilege;
+ }
+ }
+ else if (check_access(thd,want_access,tables->db,&tables->grant.privilege))
+ return TRUE; // Access denied
+ }
+ if (grant_option)
+ return check_grant(thd,want_access,org_tables);
+ return FALSE;
+}
+
+
+static bool check_lock_tables(THD *thd,TABLE_LIST *tables)
+{
+ for (; tables ; tables=tables->next)
+ {
+ if (!tables->db)
+ {
+ if (!(tables->db=thd->db))
+ {
+ send_error(&thd->net,ER_NO_DB_ERROR); /* purecov: tested */
+ return TRUE; /* purecov: tested */
+ }
+ }
+ }
+ return FALSE;
+}
+
+
+/****************************************************************************
+ Check stack size; Send error if there isn't enough stack to continue
+****************************************************************************/
+
+#if STACK_DIRECTION < 0
+#define used_stack(A,B) (long) (A - B)
+#else
+#define used_stack(A,B) (long) (B - A)
+#endif
+
+bool check_stack_overrun(THD *thd,char *buf __attribute__((unused)))
+{
+ long stack_used;
+ if ((stack_used=used_stack(thd->thread_stack,(char*) &stack_used)) >=
+ (long) thread_stack_min)
+ {
+ sprintf(errbuff[0],ER(ER_STACK_OVERRUN),stack_used,thread_stack);
+ my_message(ER_STACK_OVERRUN,errbuff[0],MYF(0));
+ thd->fatal_error=1;
+ return 1;
+ }
+ return 0;
+}
+
+#define MY_YACC_INIT 1000 // Start with big alloc
+#define MY_YACC_MAX 32000 // Because of 'short'
+
+bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, int *yystacksize)
+{
+ LEX *lex=current_lex;
+ int old_info=0;
+ if ((uint) *yystacksize >= MY_YACC_MAX)
+ return 1;
+ if (!lex->yacc_yyvs)
+ old_info= *yystacksize;
+ *yystacksize= set_zone((*yystacksize)*2,MY_YACC_INIT,MY_YACC_MAX);
+ if (!(lex->yacc_yyvs= (char*)
+ my_realloc((gptr) lex->yacc_yyvs,
+ *yystacksize*sizeof(**yyvs),
+ MYF(MY_ALLOW_ZERO_PTR | MY_FREE_ON_ERROR))) ||
+ !(lex->yacc_yyss= (char*)
+ my_realloc((gptr) lex->yacc_yyss,
+ *yystacksize*sizeof(**yyss),
+ MYF(MY_ALLOW_ZERO_PTR | MY_FREE_ON_ERROR))))
+ return 1;
+ if (old_info)
+ { // Copy old info from stack
+ memcpy(lex->yacc_yyss, (gptr) *yyss, old_info*sizeof(**yyss));
+ memcpy(lex->yacc_yyvs, (gptr) *yyvs, old_info*sizeof(**yyvs));
+ }
+ *yyss=(short*) lex->yacc_yyss;
+ *yyvs=(YYSTYPE*) lex->yacc_yyvs;
+ return 0;
+}
+
+
+/****************************************************************************
+ Initialize global thd variables neaded for query
+****************************************************************************/
+
+static void
+mysql_init_query(THD *thd)
+{
+ DBUG_ENTER("mysql_init_query");
+ thd->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.proc_list.first=0; // Needed by sql_select
+ thd->fatal_error=0; // Safety
+ thd->last_insert_id_used=thd->query_start_used=thd->insert_id_used=0;
+ DBUG_VOID_RETURN;
+}
+
+
+void
+mysql_parse(THD *thd,char *inBuf,uint length)
+{
+ DBUG_ENTER("mysql_parse");
+
+ 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);
+ 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
+******************************************************************************/
+
+bool add_field_to_list(char *field_name, enum_field_types type,
+ char *length, char *decimals,
+ uint type_modifier, Item *default_value,char *change,
+ TYPELIB *interval)
+{
+ register create_field *new_field;
+ THD *thd=current_thd;
+ LEX *lex= &thd->lex;
+ uint allowed_type_modifier=0;
+ DBUG_ENTER("add_field_to_list");
+
+ if (strlen(field_name) > NAME_LEN)
+ {
+ net_printf(&thd->net, ER_TOO_LONG_IDENT, field_name); /* purecov: inspected */
+ DBUG_RETURN(1); /* purecov: inspected */
+ }
+ if (type_modifier & PRI_KEY_FLAG)
+ {
+ lex->col_list.push_back(new key_part_spec(field_name,0));
+ lex->key_list.push_back(new Key(Key::PRIMARY,NullS,
+ lex->col_list));
+ lex->col_list.empty();
+ }
+ if (type_modifier & (UNIQUE_FLAG | UNIQUE_KEY_FLAG))
+ {
+ lex->col_list.push_back(new key_part_spec(field_name,0));
+ lex->key_list.push_back(new Key(Key::UNIQUE,NullS,
+ lex->col_list));
+ lex->col_list.empty();
+ }
+
+ if (default_value && default_value->type() == Item::NULL_ITEM)
+ {
+ if ((type_modifier & (NOT_NULL_FLAG | AUTO_INCREMENT_FLAG)) ==
+ NOT_NULL_FLAG)
+ {
+ net_printf(&thd->net,ER_INVALID_DEFAULT,field_name);
+ DBUG_RETURN(1);
+ }
+ default_value=0;
+ }
+ if (!(new_field=new create_field()))
+ DBUG_RETURN(1);
+ new_field->field=0;
+ new_field->field_name=field_name;
+ new_field->def= (type_modifier & AUTO_INCREMENT_FLAG ? 0 : default_value);
+ new_field->flags= type_modifier;
+ new_field->unireg_check= (type_modifier & AUTO_INCREMENT_FLAG ?
+ Field::NEXT_NUMBER : Field::NONE);
+ new_field->decimals= decimals ? (uint) set_zone(atoi(decimals),0,
+ NOT_FIXED_DEC-1) : 0;
+ new_field->sql_type=type;
+ new_field->length=0;
+ new_field->change=change;
+ new_field->interval=0;
+ new_field->pack_length=0;
+ if (length)
+ if (!(new_field->length= (uint) atoi(length)))
+ length=0; /* purecov: inspected */
+ uint sign_len=type_modifier & UNSIGNED_FLAG ? 0 : 1;
+
+ if (new_field->length && new_field->decimals &&
+ new_field->length < new_field->decimals+2 &&
+ new_field->decimals != NOT_FIXED_DEC)
+ new_field->length=new_field->decimals+2; /* purecov: inspected */
+
+ switch (type) {
+ case FIELD_TYPE_TINY:
+ if (!length) new_field->length=3+sign_len;
+ allowed_type_modifier= AUTO_INCREMENT_FLAG;
+ break;
+ case FIELD_TYPE_SHORT:
+ if (!length) new_field->length=5+sign_len;
+ allowed_type_modifier= AUTO_INCREMENT_FLAG;
+ break;
+ case FIELD_TYPE_INT24:
+ if (!length) new_field->length=8+sign_len;
+ allowed_type_modifier= AUTO_INCREMENT_FLAG;
+ break;
+ case FIELD_TYPE_LONG:
+ if (!length) new_field->length=10+sign_len;
+ allowed_type_modifier= AUTO_INCREMENT_FLAG;
+ break;
+ case FIELD_TYPE_LONGLONG:
+ if (!length) new_field->length=20;
+ allowed_type_modifier= AUTO_INCREMENT_FLAG;
+ break;
+ case FIELD_TYPE_STRING:
+ case FIELD_TYPE_VAR_STRING:
+ case FIELD_TYPE_NULL:
+ break;
+ case FIELD_TYPE_DECIMAL:
+ if (!length)
+ new_field->length = 10; // Default length for DECIMAL
+ new_field->length+=sign_len;
+ if (new_field->decimals)
+ new_field->length++;
+ break;
+ case FIELD_TYPE_BLOB:
+ case FIELD_TYPE_TINY_BLOB:
+ case FIELD_TYPE_LONG_BLOB:
+ case FIELD_TYPE_MEDIUM_BLOB:
+ if (default_value) // Allow empty as default value
+ {
+ String str,*res;
+ res=default_value->val_str(&str);
+ if (res->length())
+ {
+ net_printf(&thd->net,ER_BLOB_CANT_HAVE_DEFAULT,field_name); /* purecov: inspected */
+ DBUG_RETURN(1); /* purecov: inspected */
+ }
+ new_field->def=0;
+ }
+ new_field->flags|=BLOB_FLAG;
+ break;
+ case FIELD_TYPE_YEAR:
+ if (!length || new_field->length != 2)
+ new_field->length=4; // Default length
+ new_field->flags|= ZEROFILL_FLAG | UNSIGNED_FLAG;
+ break;
+ case FIELD_TYPE_FLOAT:
+ /* change FLOAT(precision) to FLOAT or DOUBLE */
+ allowed_type_modifier= AUTO_INCREMENT_FLAG;
+ if (length && !decimals)
+ {
+ uint tmp_length=new_field->length;
+ if (tmp_length > PRECISION_FOR_DOUBLE)
+ {
+ net_printf(&thd->net,ER_WRONG_FIELD_SPEC,field_name);
+ DBUG_RETURN(1);
+ }
+ else if (tmp_length > PRECISION_FOR_FLOAT)
+ {
+ new_field->sql_type=FIELD_TYPE_DOUBLE;
+ new_field->length=DBL_DIG+7; // -[digits].E+###
+ }
+ else
+ new_field->length=FLT_DIG+6; // -[digits].E+##
+ new_field->decimals= NOT_FIXED_DEC;
+ break;
+ }
+ if (!length)
+ {
+ new_field->length = FLT_DIG+6;
+ new_field->decimals= NOT_FIXED_DEC;
+ }
+ break;
+ case FIELD_TYPE_DOUBLE:
+ allowed_type_modifier= AUTO_INCREMENT_FLAG;
+ if (!length)
+ {
+ new_field->length = DBL_DIG+7;
+ new_field->decimals=NOT_FIXED_DEC;
+ }
+ break;
+ case FIELD_TYPE_TIMESTAMP:
+ if (!length)
+ new_field->length= 14; // Full date YYYYMMDDHHMMSS
+ else
+ {
+ new_field->length=((new_field->length+1)/2)*2; /* purecov: inspected */
+ new_field->length= min(new_field->length,14); /* purecov: inspected */
+ }
+ new_field->flags|= ZEROFILL_FLAG | UNSIGNED_FLAG | NOT_NULL_FLAG;
+ break;
+ case FIELD_TYPE_DATE: // Old date type
+ if (protocol_version != PROTOCOL_VERSION-1)
+ new_field->sql_type=FIELD_TYPE_NEWDATE;
+ /* fall trough */
+ case FIELD_TYPE_NEWDATE:
+ new_field->length=10;
+ break;
+ case FIELD_TYPE_TIME:
+ new_field->length=10;
+ break;
+ case FIELD_TYPE_DATETIME:
+ new_field->length=19;
+ break;
+ case FIELD_TYPE_SET:
+ {
+ if (interval->count > sizeof(longlong)*8)
+ {
+ net_printf(&thd->net,ER_TOO_BIG_SET,field_name); /* purecov: inspected */
+ DBUG_RETURN(1); /* purecov: inspected */
+ }
+ new_field->pack_length=(interval->count+7)/8;
+ if (new_field->pack_length > 4)
+ new_field->pack_length=8;
+ new_field->interval=interval;
+ new_field->length=0;
+ for (const char **pos=interval->type_names; *pos ; pos++)
+ new_field->length+=strlen(*pos)+1;
+ new_field->length--;
+ set_if_smaller(new_field->length,MAX_FIELD_WIDTH-1);
+ if (default_value)
+ {
+ thd->cuted_fields=0;
+ String str,*res;
+ res=default_value->val_str(&str);
+ (void) find_set(interval,res->ptr(),res->length());
+ if (thd->cuted_fields)
+ {
+ net_printf(&thd->net,ER_INVALID_DEFAULT,field_name);
+ DBUG_RETURN(1);
+ }
+ }
+ }
+ break;
+ case FIELD_TYPE_ENUM:
+ {
+ new_field->interval=interval;
+ new_field->pack_length=interval->count < 256 ? 1 : 2; // Should be safe
+ new_field->length=strlen(interval->type_names[0]);
+ for (const char **pos=interval->type_names+1; *pos ; pos++)
+ {
+ uint length=strlen(*pos);
+ set_if_bigger(new_field->length,length);
+ }
+ set_if_smaller(new_field->length,MAX_FIELD_WIDTH-1);
+ if (default_value)
+ {
+ String str,*res;
+ res=default_value->val_str(&str);
+ if (!find_enum(interval,res->ptr(),res->length()))
+ {
+ net_printf(&thd->net,ER_INVALID_DEFAULT,field_name);
+ DBUG_RETURN(1);
+ }
+ }
+ break;
+ }
+ }
+
+ if (new_field->length >= MAX_FIELD_WIDTH ||
+ (!new_field->length && !(new_field->flags & BLOB_FLAG) &&
+ type != FIELD_TYPE_STRING))
+ {
+ net_printf(&thd->net,ER_TOO_BIG_FIELDLENGTH,field_name,
+ MAX_FIELD_WIDTH-1); /* purecov: inspected */
+ DBUG_RETURN(1); /* purecov: inspected */
+ }
+ type_modifier&= AUTO_INCREMENT_FLAG;
+ if ((~allowed_type_modifier) & type_modifier)
+ {
+ net_printf(&thd->net,ER_WRONG_FIELD_SPEC,field_name);
+ DBUG_RETURN(1);
+ }
+ if (!new_field->pack_length)
+ new_field->pack_length=calc_pack_length(new_field->sql_type ==
+ FIELD_TYPE_VAR_STRING ?
+ FIELD_TYPE_STRING :
+ new_field->sql_type,
+ new_field->length);
+ lex->create_list.push_back(new_field);
+ lex->last_field=new_field;
+ DBUG_RETURN(0);
+}
+
+/* Store position for column in ALTER TABLE .. ADD column */
+
+void store_position_for_column(const char *name)
+{
+ current_lex->last_field->after=my_const_cast(char*) (name);
+}
+
+bool
+add_proc_to_list(Item *item)
+{
+ ORDER *order;
+ Item **item_ptr;
+
+ if (!(order = (ORDER *) sql_alloc(sizeof(ORDER)+sizeof(Item*))))
+ return 1;
+ item_ptr = (Item**) (order+1);
+ *item_ptr= item;
+ order->item=item_ptr;
+ order->free_me=0;
+ link_in_list(&current_lex->proc_list,(byte*) order,(byte**) &order->next);
+ return 0;
+}
+
+
+/* Fix escaping of _, % and \ in database and table names (for ODBC) */
+
+static void remove_escape(char *name)
+{
+ char *to;
+#ifdef USE_MB
+ char *strend=name+strlen(name);
+#endif
+ for (to=name; *name ; name++)
+ {
+#ifdef USE_MB
+ int l;
+/* if ((l = ismbchar(name, name+MBMAXLEN))) { Wei He: I think it's wrong */
+ if (use_mb(default_charset_info) &&
+ (l = my_ismbchar(default_charset_info, name, strend)))
+ {
+ while (l--)
+ *to++ = *name++;
+ name--;
+ continue;
+ }
+#endif
+ if (*name == '\\' && name[1])
+ name++; // Skipp '\\'
+ *to++= *name;
+ }
+ *to=0;
+}
+
+/****************************************************************************
+** save order by and tables in own lists
+****************************************************************************/
+
+
+bool add_to_list(SQL_LIST &list,Item *item,bool asc)
+{
+ ORDER *order;
+ Item **item_ptr;
+ DBUG_ENTER("add_to_list");
+ if (!(order = (ORDER *) sql_alloc(sizeof(ORDER)+sizeof(Item*))))
+ DBUG_RETURN(1);
+ item_ptr = (Item**) (order+1);
+ *item_ptr=item;
+ order->item= item_ptr;
+ order->asc = asc;
+ order->free_me=0;
+ order->used=0;
+ link_in_list(&list,(byte*) order,(byte**) &order->next);
+ DBUG_RETURN(0);
+}
+
+
+TABLE_LIST *add_table_to_list(Table_ident *table, LEX_STRING *alias,
+ thr_lock_type flags,
+ List<String> *use_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)
+ DBUG_RETURN(0); // End of memory
+ alias_str= alias ? alias->str : table->table.str;
+ if (table->table.length > NAME_LEN ||
+ table->db.str && table->db.length > NAME_LEN ||
+ check_table_name(table->table.str,table->table.length))
+ {
+ net_printf(&thd->net,ER_WRONG_TABLE_NAME,table->table.str);
+ DBUG_RETURN(0);
+ }
+
+#ifdef FN_LOWER_CASE
+ if (!alias) /* Alias is case sensitive */
+ if (!(alias_str=sql_strmake(alias_str,table->table.length)))
+ DBUG_RETURN(0);
+ if (lower_case_table_names)
+ casedn_str(table->table.str);
+#endif
+ if (!(ptr = (TABLE_LIST *) sql_calloc(sizeof(TABLE_LIST))))
+ DBUG_RETURN(0); /* purecov: inspected */
+ ptr->db= table->db.str;
+ ptr->real_name=table->table.str;
+ ptr->name=alias_str;
+ ptr->lock_type=flags;
+ if (use_index)
+ ptr->use_index=(List<String> *) sql_memdup((gptr) use_index,
+ sizeof(*use_index));
+ if (ignore_index)
+ ptr->ignore_index=(List<String> *) sql_memdup((gptr) ignore_index,
+ sizeof(*ignore_index));
+
+ /* check that used name is unique */
+ current_db=thd->db ? thd->db : "";
+ for (TABLE_LIST *tables=(TABLE_LIST*) thd->lex.table_list.first ; tables ;
+ tables=tables->next)
+ {
+ if (!strcmp(alias_str,tables->name) &&
+ !strcmp(ptr->db ? ptr->db : current_db,
+ tables->db ? tables->db : current_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);
+ DBUG_RETURN(ptr);
+}
+
+void add_join_on(TABLE_LIST *b,Item *expr)
+{
+ b->on_expr=expr;
+}
+
+
+void add_join_natural(TABLE_LIST *a,TABLE_LIST *b)
+{
+ b->natural_join=a;
+}
+
+ /* Check if name is used in table list */
+
+static bool check_dup(THD *thd,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))
+ return 1;
+ return 0;
+}
+
+bool reload_acl_and_cache(uint options)
+{
+ bool result=0;
+
+ select_errors=0; /* Write if more errors */
+ mysql_log.flush(); // Flush log
+ if (options & REFRESH_GRANT)
+ {
+ acl_reload();
+ grant_reload();
+ }
+ if (options & REFRESH_LOG)
+ {
+ mysql_log.new_file();
+ mysql_update_log.new_file();
+ mysql_bin_log.new_file();
+ mysql_slow_log.new_file();
+ if (ha_flush_logs())
+ result=1;
+ }
+ if (options & (REFRESH_TABLES | REFRESH_READ_LOCK))
+ {
+ if ((options & REFRESH_READ_LOCK) && ! current_thd->global_read_lock)
+ {
+ current_thd->global_read_lock=1;
+ thread_safe_increment(global_read_lock,&LOCK_open);
+ }
+ result=close_cached_tables((options & REFRESH_FAST) ? 0 : 1);
+ }
+ if (options & REFRESH_HOSTS)
+ hostname_cache_refresh();
+ if (options & REFRESH_STATUS)
+ refresh_status();
+ if (options & REFRESH_THREADS)
+ flush_thread_cache();
+ if (options & REFRESH_MASTER)
+ reset_master();
+ if (options & REFRESH_SLAVE)
+ reset_slave();
+
+ return result;
+}
+
+
+static 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;
+ while ((tmp=it++))
+ {
+ if (tmp->thread_id == id)
+ {
+ if ((thd->master_access & PROCESS_ACL) ||
+ !strcmp(thd->user,tmp->user))
+ {
+ thr_alarm_kill(tmp->real_id);
+ tmp->killed=1;
+ error=0;
+ if (tmp->mysys_var)
+ {
+ pthread_mutex_lock(&tmp->mysys_var->mutex);
+ if (!tmp->system_thread) // Don't abort locks
+ tmp->mysys_var->abort=1;
+ if (tmp->mysys_var->current_mutex)
+ {
+ 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);
+ }
+ }
+ else
+ error=ER_KILL_DENIED_ERROR;
+ break; // Found thread
+ }
+ }
+ VOID(pthread_mutex_unlock(&LOCK_thread_count));
+ if (!error)
+ send_ok(&thd->net);
+ else
+ net_printf(&thd->net,error,id);
+}
+
+/* Clear most status variables */
+
+static void refresh_status(void)
+{
+ pthread_mutex_lock(&THR_LOCK_keycache);
+ pthread_mutex_lock(&LOCK_status);
+ for (struct show_var_st *ptr=status_vars; ptr->name; ptr++)
+ {
+ if (ptr->type == SHOW_LONG)
+ *(ulong*) ptr->value=0;
+ }
+ pthread_mutex_unlock(&LOCK_status);
+ pthread_mutex_unlock(&THR_LOCK_keycache);
+}
+
+static int start_slave(THD* thd , bool net_report)
+{
+ if(!thd) thd = current_thd;
+ NET* net = &thd->net;
+ const char* err = 0;
+ if(check_access(thd, PROCESS_ACL, any_db))
+ return 1;
+ pthread_mutex_lock(&LOCK_slave);
+ if(!slave_running)
+ if(master_host)
+ {
+ pthread_t hThread;
+ if(pthread_create(&hThread, &connection_attrib, handle_slave, 0))
+ {
+ err = "cannot create slave thread";
+ }
+ }
+ else
+ err = "Master host not set";
+ else
+ err = "Slave already running";
+
+ pthread_mutex_unlock(&LOCK_slave);
+ if(err)
+ {
+ if(net_report) send_error(net, 0, err);
+ return 1;
+ }
+ else if(net_report)
+ send_ok(net);
+
+ return 0;
+}
+
+static int stop_slave(THD* thd, bool net_report )
+{
+ if(!thd) thd = current_thd;
+ NET* net = &thd->net;
+ const char* err = 0;
+
+ if(check_access(thd, PROCESS_ACL, any_db))
+ return 1;
+
+ pthread_mutex_lock(&LOCK_slave);
+ if (slave_running)
+ {
+ abort_slave = 1;
+ thr_alarm_kill(slave_real_id);
+ // 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";
+ pthread_cond_wait(&COND_slave_stopped, &LOCK_slave);
+ }
+ else
+ err = "Slave is not running";
+
+ pthread_mutex_unlock(&LOCK_slave);
+ thd->proc_info = 0;
+
+ if(err)
+ {
+ if(net_report) send_error(net, 0, err);
+ return 1;
+ }
+ else if(net_report)
+ send_ok(net);
+
+ return 0;
+}
+
+static void reset_slave()
+{
+ MY_STAT stat_area;
+ char fname[FN_REFLEN];
+ bool slave_was_running = slave_running;
+
+ if(slave_running)
+ stop_slave(0,0);
+
+ 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);
+}
+
+static int change_master(THD* thd)
+{
+ bool slave_was_running;
+ // kill slave thread
+ pthread_mutex_lock(&LOCK_slave);
+ if((slave_was_running = slave_running))
+ {
+ abort_slave = 1;
+ thr_alarm_kill(slave_real_id);
+ thd->proc_info = "waiting for slave to die";
+ pthread_cond_wait(&COND_slave_stopped, &LOCK_slave); // wait until done
+ }
+ pthread_mutex_unlock(&LOCK_slave);
+ thd->proc_info = "changing master";
+ LEX_MASTER_INFO* lex_mi = &thd->lex.mi;
+
+ 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 = 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)
+ glob_mi.pos = lex_mi->pos;
+
+ if(lex_mi->host)
+ strmake(glob_mi.host, lex_mi->host, sizeof(glob_mi.host));
+ 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);
+ thd->proc_info = 0;
+
+ send_ok(&thd->net);
+ return 0;
+}
+
+static void reset_master()
+{
+ if(!mysql_bin_log.is_open())
+ {
+ my_error(ER_FLUSH_MASTER_BINLOG_CLOSED, MYF(ME_BELL+ME_WAITTANG));
+ return;
+ }
+
+ LOG_INFO linfo;
+
+ if(mysql_bin_log.find_first_log(&linfo, ""))
+ return;
+
+ for(;;)
+ {
+ my_delete(linfo.log_file_name, MYF(MY_WME));
+ if(mysql_bin_log.find_next_log(&linfo))
+ break;
+ }
+ mysql_bin_log.close(1); // exiting close
+ my_delete(mysql_bin_log.get_index_fname(), MYF(MY_WME));
+
+ char tmp[FN_REFLEN];
+ if (!opt_bin_logname || !opt_bin_logname[0])
+ {
+ char hostname[FN_REFLEN];
+ if (gethostname(hostname,sizeof(hostname)-4) < 0)
+ strmov(hostname,"mysql");
+
+ strnmov(tmp,hostname,FN_REFLEN-5);
+ strmov(strcend(tmp,'.'),"-bin");
+ opt_bin_logname=tmp;
+ }
+
+ mysql_bin_log.open(opt_bin_logname,LOG_BIN);
+
+}
+
+int show_binlog_info(THD* thd)
+{
+ DBUG_ENTER("show_binlog_info");
+ List<Item> field_list;
+ field_list.push_back(new Item_empty_string("File", FN_REFLEN));
+ field_list.push_back(new Item_empty_string("Position",20));
+ 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))
+ 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);
+ net_store_data(packet, li.log_file_name);
+ 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);
+
+ send_eof(&thd->net);
+ DBUG_RETURN(0);
+}
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
new file mode 100644
index 00000000000..030b6f6fb5c
--- /dev/null
+++ b/sql/sql_select.cc
@@ -0,0 +1,6408 @@
+/* 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 */
+
+
+/* mysql_select and join optimization */
+
+#ifdef __GNUC__
+#pragma implementation // gcc: Class implementation
+#endif
+
+#include "mysql_priv.h"
+#include "sql_select.h"
+#include <m_ctype.h>
+#include <hash.h>
+#include <ft_global.h>
+#include <assert.h>
+
+const char *join_type_str[]={ "UNKNOWN","system","const","eq_ref","ref",
+ "MAYBE_REF","ALL","range","index" };
+
+static bool make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds,
+ DYNAMIC_ARRAY *keyuse,List<Item_func_match> &ftfuncs);
+static bool update_ref_and_keys(DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab,
+ uint tables,COND *conds,table_map table_map,
+ List<Item_func_match> &ftfuncs);
+static int sort_keyuse(KEYUSE *a,KEYUSE *b);
+static void set_position(JOIN *join,uint index,JOIN_TAB *table,KEYUSE *key);
+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,
+ KEY_PART_INFO *key_part, char *key_buff,
+ uint maybe_null);
+static bool make_simple_join(JOIN *join,TABLE *tmp_table);
+static bool make_join_select(JOIN *join,SQL_SELECT *select,COND *item);
+static void make_join_readinfo(JOIN *join,uint options);
+static void join_free(JOIN *join);
+static bool only_eq_ref_tables(JOIN *join,ORDER *order,table_map tables);
+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,
+ List<Item> &fields, bool send_row,
+ uint select_options, const char *info,
+ Item *having, Procedure *proc);
+static COND *optimize_cond(COND *conds,Item::cond_result *cond_value);
+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);
+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);
+static int sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records);
+static int flush_cached_records(JOIN *join,JOIN_TAB *join_tab,bool skipp_last);
+static int end_send(JOIN *join, JOIN_TAB *join_tab, bool end_of_records);
+static int end_send_group(JOIN *join, JOIN_TAB *join_tab,bool end_of_records);
+static int end_write(JOIN *join, JOIN_TAB *join_tab, bool end_of_records);
+static int end_update(JOIN *join, JOIN_TAB *join_tab, bool end_of_records);
+static int end_unique_update(JOIN *join,JOIN_TAB *join_tab,
+ bool end_of_records);
+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_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_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_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,
+ table_map used_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 int remove_duplicates(JOIN *join,TABLE *entry,List<Item> &fields);
+static int remove_dup_with_compare(THD *thd, TABLE *entry, Field **field,
+ ulong offset);
+static int remove_dup_with_hash_index(THD *thd, TABLE *table,
+ uint field_count, Field **first_field,
+ ulong key_length);
+static SORT_FIELD * make_unireg_sortorder(ORDER *order, uint *length);
+static int join_init_cache(THD *thd,JOIN_TAB *tables,uint table_count);
+static ulong used_blob_length(CACHE_FIELD **ptr);
+static bool store_record_in_cache(JOIN_CACHE *cache);
+static void reset_cache(JOIN_CACHE *cache);
+static void read_cached_record(JOIN_TAB *tab);
+static bool cmp_buffer_with_ref(JOIN_TAB *tab);
+static int setup_order(THD *thd,TABLE_LIST *tables, List<Item> &fields,
+ List <Item> &all_fields, ORDER *order);
+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 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);
+static bool alloc_group_fields(JOIN *join,ORDER *group);
+static bool make_sum_func_list(JOIN *join,List<Item> &fields);
+static bool change_to_use_tmp_fields(List<Item> &func);
+static bool change_refs_to_tmp_fields(THD *thd, List<Item> &func);
+static void init_tmptable_sum_functions(Item_sum **func);
+static void update_tmptable_sum_func(Item_sum **func,TABLE *tmp_table);
+static void copy_sum_funcs(Item_sum **func_ptr);
+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);
+static void describe_info(const char *info);
+
+/*****************************************************************************
+** check fields, find best join, do the select and output fields.
+** mysql_select assumes that all tables are allready opened
+*****************************************************************************/
+
+int
+mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds,
+ List<Item_func_match> &ftfuncs,
+ ORDER *order, ORDER *group,Item *having,ORDER *proc_param,
+ uint select_options,select_result *result)
+{
+ TABLE *tmp_table;
+ int error,tmp;
+ bool need_tmp,hidden_group_fields;
+ bool simple_order,simple_group,no_order;
+ Item::cond_result cond_value;
+ SQL_SELECT *select;
+ DYNAMIC_ARRAY keyuse;
+ JOIN join;
+ Procedure *procedure;
+ List<Item> all_fields(fields);
+ bool select_distinct;
+ DBUG_ENTER("mysql_select");
+
+ /* Check that all tables, fields, conds and order are ok */
+
+ select_distinct=test(select_options & SELECT_DISTINCT);
+ tmp_table=0;
+ select=0;
+ no_order=0;
+ bzero((char*) &keyuse,sizeof(keyuse));
+ thd->proc_info="init";
+
+ if (setup_fields(thd,tables,fields,1,&all_fields) ||
+ setup_conds(thd,tables,&conds) ||
+ setup_order(thd,tables,fields,all_fields,order) ||
+ setup_group(thd,tables,fields,all_fields,group,&hidden_group_fields) ||
+ setup_ftfuncs(thd,tables,ftfuncs))
+ DBUG_RETURN(-1); /* purecov: inspected */
+
+ if (having)
+ {
+ thd->where="having clause";
+ thd->allow_sum_func=1;
+ if (having->fix_fields(thd,tables) || thd->fatal_error)
+ DBUG_RETURN(-1); /* purecov: inspected */
+ if (having->with_sum_func)
+ having->split_sum_func(all_fields);
+ }
+ /*
+ Check if one one uses a not constant column with group functions
+ 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);
+ Item *item;
+ while ((item= it++))
+ {
+ if (item->with_sum_func)
+ flag|=1;
+ else if (!item->const_item())
+ flag|=2;
+ }
+ if (flag == 3)
+ {
+ my_error(ER_MIX_OF_GROUP_FUNC_AND_FIELDS,MYF(0));
+ DBUG_RETURN(-1);
+ }
+ }
+ TABLE_LIST *table;
+ for (table=tables ; table ; table=table->next)
+ join.tables++;
+ }
+ procedure=setup_procedure(thd,proc_param,result,fields,&error);
+ if (error)
+ DBUG_RETURN(-1); /* purecov: inspected */
+ if (procedure)
+ {
+ if (setup_new_fields(thd,tables,fields,all_fields,procedure->param_fields))
+ { /* purecov: inspected */
+ delete procedure; /* purecov: inspected */
+ DBUG_RETURN(-1); /* purecov: inspected */
+ }
+ if (procedure->group)
+ {
+ if (!test_if_subpart(procedure->group,group))
+ { /* purecov: inspected */
+ my_message(0,"Can't handle procedures with differents groups yet",
+ MYF(0)); /* purecov: inspected */
+ delete procedure; /* purecov: inspected */
+ DBUG_RETURN(-1); /* purecov: inspected */
+ }
+ }
+#ifdef NOT_NEEDED
+ else if (!group && procedure->flags & PROC_GROUP)
+ {
+ my_message(0,"Select must have a group with this procedure",MYF(0));
+ delete procedure;
+ DBUG_RETURN(-1);
+ }
+#endif
+ if (order && (procedure->flags & PROC_NO_SORT))
+ { /* purecov: inspected */
+ my_message(0,"Can't use order with this procedure",MYF(0)); /* purecov: inspected */
+ delete procedure; /* purecov: inspected */
+ DBUG_RETURN(-1); /* purecov: inspected */
+ }
+ }
+
+ /* Init join struct */
+ join.thd=thd;
+ join.lock=thd->lock;
+ join.join_tab=0;
+ join.tmp_table_param.copy_field=0;
+ join.sum_funcs=0;
+ join.send_records=0L;
+ join.tmp_table_param.end_write_records= HA_POS_ERROR;
+ join.first_record=join.sort_and_group=0;
+ join.select_options=select_options;
+ join.result=result;
+ count_field_types(&join.tmp_table_param,all_fields);
+ join.const_tables=0;
+ join.having=0;
+ join.group= group != 0;
+
+#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));
+ delete procedure;
+ DBUG_RETURN(-1);
+ }
+#endif
+ if (!procedure && result->prepare(fields))
+ { /* purecov: inspected */
+ DBUG_RETURN(-1); /* purecov: inspected */
+ }
+
+#ifdef HAVE_REF_TO_FIELDS // Not done yet
+ /* Add HAVING to WHERE if possible */
+ if (having && !group && ! join.sum_func_count)
+ {
+ if (!conds)
+ {
+ conds=having;
+ having=0;
+ }
+ else if ((conds=new Item_cond_and(conds,having)))
+ {
+ conds->fix_fields(thd,tables);
+ conds->change_ref_to_fields(thd,tables);
+ having=0;
+ }
+ }
+#endif
+
+ conds=optimize_cond(conds,&cond_value);
+ if (thd->fatal_error) // Out of memory
+ {
+ delete procedure;
+ DBUG_RETURN(0);
+ }
+ if (cond_value == Item::COND_FALSE || !thd->select_limit)
+ { /* Impossible cond */
+ error=return_zero_rows(result, tables, fields,
+ join.tmp_table_param.sum_func_count != 0 && !group,
+ select_options,"Impossible WHERE",join.having,
+ procedure);
+ delete procedure;
+ DBUG_RETURN(error);
+ }
+
+ /* Optimize count(*), min() and max() */
+ if (tables && join.tmp_table_param.sum_func_count && ! group)
+ {
+ int res;
+ if ((res=opt_sum_query(tables, all_fields, conds)))
+ {
+ if (res < 0)
+ {
+ error=return_zero_rows(result, tables, fields, !group,
+ select_options,"No matching min/max row",
+ join.having,procedure);
+ delete procedure;
+ DBUG_RETURN(error);
+ }
+ if (select_options & SELECT_DESCRIBE)
+ {
+ describe_info("Select tables optimized away");
+ delete procedure;
+ DBUG_RETURN(0);
+ }
+ tables=0; // All tables resolved
+ }
+ }
+ if (!tables)
+ { // Only test of functions
+ error=0;
+ if (select_options & SELECT_DESCRIBE)
+ describe_info("No tables used");
+ else
+ {
+ result->send_fields(fields,1);
+ if (!having || having->val_int())
+ {
+ if (result->send_data(fields))
+ {
+ result->send_error(0,NullS); /* purecov: inspected */
+ error=1;
+ }
+ else
+ error=(int) result->send_eof();
+ }
+ else
+ error=(int) result->send_eof();
+ }
+ delete procedure;
+ DBUG_RETURN(0);
+ }
+
+ error = -1;
+ join.sort_by_table=get_sort_by_table(order,group,tables);
+
+ /* Calculate how to do the join */
+ thd->proc_info="statistics";
+ if (make_join_statistics(&join,tables,conds,&keyuse,ftfuncs) ||
+ thd->fatal_error)
+ goto err;
+ thd->proc_info="preparing";
+ if ((tmp=join_read_const_tables(&join)) > 0)
+ goto err;
+ if (tmp && !(select_options & SELECT_DESCRIBE))
+ {
+ error=return_zero_rows(result,tables,fields,
+ join.tmp_table_param.sum_func_count != 0 &&
+ !group,0,"",join.having,procedure);
+ goto err;
+ }
+ if (!(thd->options & OPTION_BIG_SELECTS) &&
+ join.best_read > (double) thd->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)
+ mysql_unlock_some_tables(thd, join.table,join.const_tables);
+ select=make_select(*join.table, join.const_table_map,
+ join.const_table_map,conds,&error);
+ if (error)
+ { /* purecov: inspected */
+ error= -1; /* purecov: inspected */
+ goto err; /* purecov: inspected */
+ }
+ if (make_join_select(&join,select,conds))
+ {
+ error=return_zero_rows(result,tables,fields,
+ join.tmp_table_param.sum_func_count != 0 && !group,
+ select_options,
+ "Impossible WHERE noticed after reading const tables",
+ join.having,procedure);
+ goto err;
+ }
+
+ error= -1; /* if goto err */
+
+ /* Optimize distinct away if possible */
+ order=remove_const(&join,order,conds,&simple_order);
+ 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))
+ {
+ 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
+ goto err;
+ }
+ group=remove_const(&join,group,conds,&simple_group);
+ if (!group && join.group)
+ {
+ order=0; // The output has only one row
+ simple_order=1;
+ }
+
+ calc_group_buffer(&join,group);
+ join.send_group_parts=join.tmp_table_param.group_parts; /* Save org parts */
+ if (procedure && procedure->group)
+ {
+ group=procedure->group=remove_const(&join,procedure->group,conds,
+ &simple_group);
+ calc_group_buffer(&join,group);
+ }
+
+ if (test_if_subpart(group,order) ||
+ (!group && join.tmp_table_param.sum_func_count))
+ order=0;
+
+ // Can't use sort on head table if using cache
+ if (join.full_join)
+ {
+ if (group)
+ simple_group=0;
+ if (order)
+ simple_order=0;
+ }
+
+ need_tmp= (join.const_tables != join.tables &&
+ ((select_distinct || !simple_order || !simple_group) ||
+ (group && order) ||
+ test(select_options & OPTION_BUFFER_RESULT)));
+
+ make_join_readinfo(&join,
+ (select_options & SELECT_DESCRIBE) | SELECT_USE_CACHE);
+ DBUG_EXECUTE("info",TEST_join(&join););
+ /*
+ Because filesort always does a full table scan or a quick range scan
+ we must add the removed reference to the select for the table.
+ We only need to do this when we have a simple_order or simple_group
+ as in other cases the join is done before the sort.
+ */
+ if ((order || group) && join.join_tab[join.const_tables].type != JT_ALL &&
+ (order && simple_order || group && simple_group))
+ {
+ if (add_ref_to_table_cond(thd,&join.join_tab[join.const_tables]))
+ goto err;
+ }
+
+ if (!(select_options & SELECT_BIG_RESULT) &&
+ ((group && join.const_tables != join.tables &&
+ !test_if_skip_sort_order(&join.join_tab[join.const_tables], group,
+ HA_POS_ERROR)) ||
+ select_distinct) &&
+ join.tmp_table_param.quick_group && !procedure)
+ {
+ need_tmp=1; simple_order=simple_group=0; // Force tmp table without sort
+ }
+
+ if (select_options & SELECT_DESCRIBE)
+ {
+ if (!order && !no_order)
+ order=group;
+ if (order &&
+ (join.const_tables == join.tables ||
+ test_if_skip_sort_order(&join.join_tab[join.const_tables], order,
+ (having || group ||
+ join.const_tables != join.tables - 1) ?
+ HA_POS_ERROR : thd->select_limit)))
+ order=0;
+ select_describe(&join,need_tmp,
+ (order != 0 &&
+ (!need_tmp || order != group || simple_group)));
+ error=0;
+ goto err;
+ }
+
+ /* Create a tmp table if distinct or if the sort is too complicated */
+ if (need_tmp)
+ {
+ DBUG_PRINT("info",("Creating tmp table"));
+ thd->proc_info="Creating tmp table";
+
+ if (!(tmp_table =
+ create_tmp_table(thd,&join.tmp_table_param,all_fields,
+ ((!simple_group && !procedure &&
+ !(test_flags & TEST_NO_KEY_GROUP)) ?
+ group : (ORDER*) 0),
+ group ? 0 : select_distinct,
+ group && simple_group,
+ order == 0,
+ join.select_options)))
+ goto err; /* purecov: inspected */
+
+ if (having && (join.sort_and_group || (tmp_table->distinct && !group)))
+ join.having=having;
+
+ /* if group or order on first table, sort first */
+ if (group && simple_group)
+ {
+ 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) ||
+ make_sum_func_list(&join,all_fields) ||
+ alloc_group_fields(&join,group))
+ goto err;
+ group=0;
+ }
+ else
+ {
+ if (make_sum_func_list(&join,all_fields))
+ goto err;
+ if (!group && ! tmp_table->distinct && order && simple_order)
+ {
+ 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))
+ goto err; /* purecov: inspected */
+ order=0;
+ }
+ }
+ thd->proc_info="Copying to tmp table";
+ if (do_select(&join,(List<Item> *) 0,tmp_table,0))
+ goto err; /* purecov: inspected */
+ if (join.having)
+ join.having=having=0; // Allready done
+
+ /* Change sum_fields reference to calculated fields in tmp_table */
+ if (join.sort_and_group || tmp_table->group)
+ {
+ if (change_to_use_tmp_fields(all_fields))
+ goto err;
+ join.tmp_table_param.field_count+=join.tmp_table_param.sum_func_count+
+ join.tmp_table_param.func_count;
+ join.tmp_table_param.sum_func_count=join.tmp_table_param.func_count=0;
+ }
+ else
+ {
+ if (change_refs_to_tmp_fields(thd,all_fields))
+ goto err;
+ join.tmp_table_param.field_count+=join.tmp_table_param.func_count;
+ join.tmp_table_param.func_count=0;
+ }
+ if (procedure)
+ procedure->update_refs();
+ if (tmp_table->group)
+ { // Already grouped
+ if (!order && !no_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
+ */
+
+ if (group && (!test_if_subpart(group,order) || select_distinct))
+ { /* Must copy to another table */
+ TABLE *tmp_table2;
+ DBUG_PRINT("info",("Creating group table"));
+
+ /* Free first data from old join */
+ join_free(&join);
+ if (make_simple_join(&join,tmp_table))
+ goto err;
+ calc_group_buffer(&join,group);
+ count_field_types(&join.tmp_table_param,all_fields);
+
+ /* group data to new table */
+ if (!(tmp_table2 = create_tmp_table(thd,&join.tmp_table_param,all_fields,
+ (ORDER*) 0, 0 , 1, 0,
+ join.select_options)))
+ goto err; /* purecov: inspected */
+ if (group)
+ {
+ thd->proc_info="Creating sort index";
+ if (create_sort_index(join.join_tab,group,HA_POS_ERROR) ||
+ alloc_group_fields(&join,group))
+ {
+ free_tmp_table(thd,tmp_table2); /* purecov: inspected */
+ goto err; /* purecov: inspected */
+ }
+ group=0;
+ }
+ thd->proc_info="Copying to group table";
+ if (make_sum_func_list(&join,all_fields) ||
+ do_select(&join,(List<Item> *) 0,tmp_table2,0))
+ {
+ free_tmp_table(thd,tmp_table2);
+ goto err; /* purecov: inspected */
+ }
+ end_read_record(&join.join_tab->read_record);
+ free_tmp_table(thd,tmp_table);
+ join.const_tables=join.tables; // Mark free for join_free()
+ tmp_table=tmp_table2;
+ join.join_tab[0].table=0; // Table is freed
+
+ if (change_to_use_tmp_fields(all_fields)) // No sum funcs anymore
+ goto err;
+ join.tmp_table_param.field_count+=join.tmp_table_param.sum_func_count;
+ join.tmp_table_param.sum_func_count=0;
+ }
+
+ if (tmp_table->distinct)
+ select_distinct=0; /* Each row is uniq */
+
+ join_free(&join); /* Free quick selects */
+ if (select_distinct && ! group)
+ {
+ thd->proc_info="Removing duplicates";
+ if (remove_duplicates(&join,tmp_table,fields))
+ goto err; /* purecov: inspected */
+ select_distinct=0;
+ }
+ tmp_table->reginfo.lock_type=TL_UNLOCK;
+ if (make_simple_join(&join,tmp_table))
+ goto err;
+ calc_group_buffer(&join,group);
+ count_field_types(&join.tmp_table_param,all_fields);
+ }
+ if (procedure)
+ {
+ if (procedure->change_columns(fields) ||
+ result->prepare(fields))
+ goto err;
+ count_field_types(&join.tmp_table_param,all_fields);
+ }
+ if (join.group || join.tmp_table_param.sum_func_count ||
+ (procedure && (procedure->flags & PROC_GROUP)))
+ {
+ alloc_group_fields(&join,group);
+ setup_copy_fields(&join.tmp_table_param,all_fields);
+ if (make_sum_func_list(&join,all_fields) || thd->fatal_error)
+ goto err; /* purecov: inspected */
+ }
+ if (group || order)
+ {
+ DBUG_PRINT("info",("Sorting for send_fields"));
+ thd->proc_info="Sorting result";
+ /* If we have already done the group, add HAVING to sorted table */
+ if (having && ! group && ! join.sort_and_group)
+ {
+ 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;
+ 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 (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))
+ goto err; /* purecov: inspected */
+ }
+ join.having=having; // Actually a parameter
+ thd->proc_info="Sending data";
+ error=do_select(&join,&fields,NULL,procedure);
+
+err:
+ thd->proc_info="end";
+ join.lock=0; // It's faster to unlock later
+ join_free(&join);
+ thd->proc_info="end2"; // QQ
+ if (tmp_table)
+ free_tmp_table(thd,tmp_table);
+ thd->proc_info="end3"; // QQ
+ delete select;
+ delete_dynamic(&keyuse);
+ delete procedure;
+ thd->proc_info="end4"; // QQ
+ DBUG_RETURN(error);
+}
+
+/*****************************************************************************
+** 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)
+{
+ int error;
+ DBUG_ENTER("get_quick_record_count");
+ if (select)
+ {
+ select->head=table;
+ table->reginfo.impossible_range=0;
+ if ((error=select->test_quick_select(keys,(table_map) 0,HA_POS_ERROR))
+ == 1)
+ DBUG_RETURN(select->quick->records);
+ if (error == -1)
+ {
+ table->reginfo.impossible_range=1;
+ DBUG_RETURN(0);
+ }
+ DBUG_PRINT("warning",("Couldn't use record count on const keypart"));
+ }
+ DBUG_RETURN(HA_POS_ERROR); /* This shouldn't happend */
+}
+
+
+static bool
+make_join_statistics(JOIN *join,TABLE_LIST *tables,COND *conds,
+ DYNAMIC_ARRAY *keyuse_array,
+ List<Item_func_match> &ftfuncs)
+{
+ 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 **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];
+ DBUG_ENTER("make_join_statistics");
+
+ table_count=join->tables;
+ stat=(JOIN_TAB*) sql_calloc(sizeof(JOIN_TAB)*table_count);
+ stat_ref=(JOIN_TAB**) sql_alloc(sizeof(JOIN_TAB*)*MAX_TABLES);
+ table_vector=(TABLE**) sql_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;
+ const_count=0;
+
+ for (s=stat,i=0 ; tables ; s++,tables=tables->next,i++)
+ {
+ TABLE *table;
+ stat_vector[i]=s;
+ table_vector[i]=s->table=table=tables->table;
+ table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);// record count
+ table->quick_keys=0;
+ table->reginfo.join_tab=s;
+ table->reginfo.not_exists_optimize=0;
+ bzero((char*) table->const_key_parts, sizeof(key_part_map)*table->keys);
+ all_table_map|= table->map;
+ if ((s->on_expr=tables->on_expr))
+ {
+ // table->maybe_null=table->outer_join=1; // Mark for send fields
+ s->key_dependent=s->dependent=
+ s->on_expr->used_tables() & ~(table->map);
+ s->dependent|=stat_vector[i-1]->dependent | table_vector[i-1]->map;
+ outer_join|=table->map;
+ continue;
+ }
+ if (tables->straight) // We don't have to move this
+ s->dependent= table_vector[i-1]->map | stat_vector[i-1]->dependent;
+ else
+ s->dependent=(table_map) 0;
+ s->key_dependent=(table_map) 0;
+ if ((table->system || table->file->records <= 1L) && ! s->dependent)
+ {
+ s->type=JT_SYSTEM;
+ const_table_map|=table->map;
+ set_position(join,const_count++,s,(KEYUSE*) 0);
+ }
+ }
+ stat_vector[i]=0;
+
+ /*
+ ** 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)
+ {
+ table_map used_tables=0L;
+ for (i=0 ; i < join->tables-1 ; i++)
+ {
+ if (stat_vector[i]->dependent & ~used_tables)
+ {
+ JOIN_TAB *save= stat_vector[i];
+ uint j;
+ for (j=i+1;
+ j < join->tables && stat_vector[j]->dependent & ~used_tables;
+ j++)
+ {
+ JOIN_TAB *tmp=stat_vector[j]; // Move element up
+ stat_vector[j]=save;
+ save=tmp;
+ }
+ if (j == join->tables)
+ {
+ join->tables=0; // Don't use join->table
+ my_error(ER_WRONG_OUTER_JOIN,MYF(0));
+ DBUG_RETURN(1);
+ }
+ stat_vector[i]=stat_vector[j];
+ stat_vector[j]=save;
+ }
+ used_tables|= stat_vector[i]->table->map;
+ }
+ }
+
+ if (conds || outer_join)
+ if (update_ref_and_keys(keyuse_array,stat,join->tables,
+ conds,~outer_join,ftfuncs))
+ DBUG_RETURN(1);
+
+ /* loop until no more const tables are found */
+ do
+ {
+ found_ref=0;
+ for (JOIN_TAB **pos=stat_vector+const_count; (s= *pos) ; pos++)
+ {
+ if (s->dependent) // If dependent on some table
+ {
+ if (s->dependent & ~(const_table_map)) // All dep. must be constants
+ continue;
+ if (s->table->file->records <= 1L)
+ { // system table
+ s->type=JT_SYSTEM;
+ const_table_map|=s->table->map;
+ set_position(join,const_count++,s,(KEYUSE*) 0);
+ 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)
+ {
+ start_keyuse=keyuse;
+ key=keyuse->key;
+ s->keys|= (key_map) 1 << key; // QQ: remove this ?
+
+ refs=const_ref=eq_part=0;
+ do
+ {
+ if (keyuse->val->type() != Item::NULL_ITEM)
+ {
+ if (!((~const_table_map) & keyuse->used_tables))
+ const_ref|= (key_map) 1 << keyuse->keypart;
+ else
+ refs|=keyuse->used_tables;
+ eq_part|= (uint) 1 << keyuse->keypart;
+ }
+ keyuse++;
+ } while (keyuse->table == table && keyuse->key == key);
+
+ if (eq_part == PREV_BITS(uint,table->key_info[key].key_parts) &&
+ (table->key_info[key].flags & HA_NOSAME))
+ {
+ 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);
+ break;
+ }
+ else
+ found_ref|= refs; // Table is const if all refs are const
+ }
+ }
+ }
+ }
+ } while (const_table_map & found_ref);
+
+ /* Calc how many (possible) matched records in each table */
+
+ for (s=stat ; s < stat_end ; s++)
+ {
+ if (s->type == JT_SYSTEM || s->type == JT_CONST)
+ {
+ /* Only one matching row */
+ s->found_records=s->records=s->read_time=1; s->worst_seeks=1.0;
+ continue;
+ }
+ /* Approximate found rows and time to read them */
+ s->found_records=s->records=s->table->file->records;
+ s->read_time=(ha_rows) ((s->table->file->data_file_length)/IO_SIZE)+1;
+
+ /* Set a max range of how many seeks we can expect when using keys */
+ s->worst_seeks= (double) (s->read_time*2);
+ if (s->worst_seeks < 2.0) // Fix for small tables
+ s->worst_seeks=2.0;
+
+ /* if (s->type == JT_EQ_REF)
+ continue; */
+ if (s->const_keys)
+ {
+ ha_rows records;
+ if (!select)
+ select=make_select(s->table,const_table_map,
+ 0,
+ and_conds(conds,s->on_expr),&error);
+ records=get_quick_record_count(select,s->table, s->const_keys);
+ s->quick=select->quick;
+ s->needed_reg=select->needed_reg;
+ select->quick=0;
+ select->read_tables=const_table_map;
+ if (records != HA_POS_ERROR)
+ {
+ s->found_records=records;
+ s->read_time= (ha_rows) (s->quick ? s->quick->read_time : 0.0);
+ }
+ }
+ }
+ 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;
+
+ if (join->const_tables != join->tables)
+ find_best_combination(join,all_table_map & ~const_table_map);
+ else
+ {
+ memcpy((gptr) join->best_positions,(gptr) join->positions,
+ sizeof(POSITION)*join->const_tables);
+ join->best_read=1.0;
+ }
+ DBUG_RETURN(get_best_combination(join));
+}
+
+
+/*****************************************************************************
+** 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
+ Field *field;
+ Item *val; // May be empty if diff constant
+ uint level,const_level; // QQ: Remove const_level
+ bool eq_func;
+ bool exists_optimize;
+} KEY_FIELD;
+
+
+/* merge new key definitions to old ones, remove those not used in both */
+
+static KEY_FIELD *
+merge_key_fields(KEY_FIELD *start,KEY_FIELD *new_fields,KEY_FIELD *end,
+ uint and_level)
+{
+ if (start == new_fields)
+ return start; // Impossible or
+ if (new_fields == end)
+ return start; // No new fields, skipp all
+
+ KEY_FIELD *first_free=new_fields;
+
+ /* Mark all found fields in old array */
+ for (; new_fields != end ; new_fields++)
+ {
+ for (KEY_FIELD *old=start ; old != first_free ; old++)
+ {
+ if (old->field == new_fields->field)
+ {
+ if (new_fields->val->used_tables())
+ {
+ if (old->val->eq(new_fields->val))
+ {
+ old->level=old->const_level=and_level;
+ old->exists_optimize&=new_fields->exists_optimize;
+ }
+ }
+ else if (old->val->eq(new_fields->val) && old->eq_func &&
+ new_fields->eq_func)
+ {
+ old->level=old->const_level=and_level;
+ old->exists_optimize&=new_fields->exists_optimize;
+ }
+ else // Impossible; remove it
+ {
+ if (old == --first_free) // If last item
+ break;
+ *old= *first_free; // Remove old value
+ old--; // Retry this value
+ }
+ }
+ }
+ }
+ /* Remove all not used items */
+ for (KEY_FIELD *old=start ; old != first_free ;)
+ {
+ if (old->level != and_level && old->const_level != and_level)
+ { // Not used in all levels
+ if (old == --first_free)
+ break;
+ *old= *first_free; // Remove old value
+ continue;
+ }
+ old++;
+ }
+ return first_free;
+}
+
+
+static void
+add_key_field(KEY_FIELD **key_fields,uint and_level,
+ Field *field,bool eq_func,Item *value,
+ table_map usable_tables)
+{
+ bool exists_optimize=0;
+ if (!(field->flags & PART_KEY_FLAG))
+ {
+ // 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
+ exists_optimize=1;
+ }
+ else
+ {
+ table_map used_tables=0;
+ if (value && (used_tables=value->used_tables()) &
+ (field->table->map | RAND_TABLE_BIT))
+ return;
+ if (!(usable_tables & field->table->map))
+ {
+ if (!eq_func || !value || value->type() != Item::NULL_ITEM ||
+ !field->table->maybe_null || field->null_ptr)
+ return; // Can't use left join optimize
+ exists_optimize=1;
+ }
+ else
+ {
+ JOIN_TAB *stat=field->table->reginfo.join_tab;
+ stat[0].keys|=field->key_start; // Add possible keys
+
+ if (!value)
+ { // Probably BETWEEN or IN
+ stat[0].const_keys |= field->key_start;
+ return; // Can't be used as eq key
+ }
+
+ /* Save the following cases:
+ Field op constant
+ Field LIKE constant where constant doesn't start with a wildcard
+ Field = field2 where field2 is in a different table
+ Field op formula
+ Field IS NULL
+ Field IS NOT NULL
+ */
+ stat[0].key_dependent|=used_tables;
+ if (value->const_item())
+ stat[0].const_keys |= field->key_start;
+
+ /* 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 */
+ if (!eq_func ||
+ field->result_type() == STRING_RESULT &&
+ value->result_type() != STRING_RESULT &&
+ field->cmp_type() != value->result_type())
+ return;
+ }
+ }
+ /* Store possible eq field */
+ (*key_fields)->field=field;
+ (*key_fields)->eq_func=eq_func;
+ (*key_fields)->val=value;
+ (*key_fields)->level=(*key_fields)->const_level=and_level;
+ (*key_fields)->exists_optimize=exists_optimize;
+ (*key_fields)++;
+}
+
+
+static void
+add_key_fields(JOIN_TAB *stat,KEY_FIELD **key_fields,uint *and_level,
+ COND *cond, table_map usable_tables)
+{
+ if (cond->type() == Item_func::COND_ITEM)
+ {
+ List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
+ KEY_FIELD *org_key_fields= *key_fields;
+
+ if (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC)
+ {
+ Item *item;
+ while ((item=li++))
+ add_key_fields(stat,key_fields,and_level,item,usable_tables);
+ for (; org_key_fields != *key_fields ; org_key_fields++)
+ {
+ if (org_key_fields->const_level == org_key_fields->level)
+ org_key_fields->const_level=org_key_fields->level= *and_level;
+ else
+ org_key_fields->const_level= *and_level;
+ }
+ }
+ else
+ {
+ (*and_level)++;
+ add_key_fields(stat,key_fields,and_level,li++,usable_tables);
+ Item *item;
+ while ((item=li++))
+ {
+ KEY_FIELD *start_key_fields= *key_fields;
+ (*and_level)++;
+ add_key_fields(stat,key_fields,and_level,item,usable_tables);
+ *key_fields=merge_key_fields(org_key_fields,start_key_fields,
+ *key_fields,++(*and_level));
+ }
+ }
+ return;
+ }
+ /* If item is of type 'field op field/constant' add it to key_fields */
+
+ if (cond->type() != Item::FUNC_ITEM)
+ return;
+ Item_func *cond_func= (Item_func*) cond;
+ switch (cond_func->select_optimize()) {
+ case Item_func::OPTIMIZE_NONE:
+ break;
+ case Item_func::OPTIMIZE_KEY:
+ if (cond_func->key_item()->type() == Item::FIELD_ITEM)
+ add_key_field(key_fields,*and_level,
+ ((Item_field*) (cond_func->key_item()))->field,
+ 0,(Item*) 0,usable_tables);
+ break;
+ case Item_func::OPTIMIZE_OP:
+ {
+ bool equal_func=(cond_func->functype() == Item_func::EQ_FUNC ||
+ cond_func->functype() == Item_func::EQUAL_FUNC);
+
+ if (cond_func->arguments()[0]->type() == Item::FIELD_ITEM)
+ {
+ add_key_field(key_fields,*and_level,
+ ((Item_field*) (cond_func->arguments()[0]))->field,
+ equal_func,
+ (cond_func->arguments()[1]),usable_tables);
+ }
+ if (cond_func->arguments()[1]->type() == Item::FIELD_ITEM &&
+ cond_func->functype() != Item_func::LIKE_FUNC)
+ {
+ add_key_field(key_fields,*and_level,
+ ((Item_field*) (cond_func->arguments()[1]))->field,
+ equal_func,
+ (cond_func->arguments()[0]),usable_tables);
+ }
+ break;
+ }
+ case Item_func::OPTIMIZE_NULL:
+ /* column_name IS [NOT] NULL */
+ if (cond_func->arguments()[0]->type() == Item::FIELD_ITEM)
+ {
+ add_key_field(key_fields,*and_level,
+ ((Item_field*) (cond_func->arguments()[0]))->field,
+ cond_func->functype() == Item_func::ISNULL_FUNC,
+ new Item_null, usable_tables);
+ }
+ break;
+ }
+ return;
+}
+
+/*
+** 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
+max_part_bit(key_map bits)
+{
+ uint found;
+ for (found=0; bits & 1 ; found++,bits>>=1) ;
+ return found;
+}
+
+
+static void
+add_key_part(DYNAMIC_ARRAY *keyuse_array,KEY_FIELD *key_field)
+{
+ Field *field=key_field->field;
+ TABLE *form= field->table;
+ KEYUSE keyuse;
+
+ if (key_field->eq_func && !key_field->exists_optimize)
+ {
+ for (uint key=0 ; key < form->keys ; key++)
+ {
+ if (!(form->keys_in_use_for_query & (((key_map) 1) << key)))
+ continue;
+ if (form->key_info[key].flags & HA_FULLTEXT)
+ continue; // ToDo: ft-keys in non-ft queries. SerG
+
+ uint key_parts= (uint) form->key_info[key].key_parts;
+ for (uint part=0 ; part < key_parts ; part++)
+ {
+ if (field->eq(form->key_info[key].key_part[part].field))
+ {
+ keyuse.table= field->table;
+ keyuse.val = key_field->val;
+ keyuse.key = key;
+ keyuse.keypart=part;
+ keyuse.used_tables=key_field->val->used_tables();
+ VOID(insert_dynamic(keyuse_array,(gptr) &keyuse));
+ }
+ }
+ }
+ }
+ /* Mark that we can optimize LEFT JOIN */
+ if (key_field->val->type() == Item::NULL_ITEM &&
+ !key_field->field->real_maybe_null())
+ key_field->field->table->reginfo.not_exists_optimize=1;
+}
+
+static void
+add_ft_keys(DYNAMIC_ARRAY *keyuse_array,
+ JOIN_TAB *stat,COND *cond,table_map usable_tables)
+{
+ /* for now, handling only the simples WHERE MATCH (...) case */
+ /* a bit more complex WHERE MATCH (...) > const,
+ AND's and (perhaps) OR's are on the way SerG */
+
+ if (cond->type() != Item::FUNC_ITEM ||
+ ((Item_func*) cond)->functype() != Item_func::FT_FUNC)
+ return;
+
+ Item_func_match *cond_func= (Item_func_match *) cond;
+ KEYUSE keyuse;
+
+ keyuse.table= cond_func->table;
+ keyuse.val = cond_func->key_item();
+ keyuse.key = cond_func->key;
+#define FT_KEYPART (MAX_REF_PARTS+10)
+ keyuse.keypart=FT_KEYPART;
+ keyuse.used_tables=keyuse.val->used_tables();
+ VOID(insert_dynamic(keyuse_array,(gptr) &keyuse));
+}
+
+static int
+sort_keyuse(KEYUSE *a,KEYUSE *b)
+{
+ if (a->table->tablenr != b->table->tablenr)
+ return (int) (a->table->tablenr - b->table->tablenr);
+ if (a->key != b->key)
+ return (int) (a->key - b->key);
+ if (a->keypart != b->keypart)
+ return (int) (a->keypart - b->keypart);
+ return test(a->used_tables) - test(b->used_tables); // Place const first
+}
+
+
+/*
+** 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
+update_ref_and_keys(DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab,uint tables,
+ COND *cond, table_map normal_tables,List<Item_func_match> &ftfuncs)
+{
+ uint and_level,i,found_eq_constant;
+
+ {
+ KEY_FIELD *key_fields,*end;
+
+ if (!(key_fields=(KEY_FIELD*)
+ my_malloc(sizeof(key_fields[0])*
+ (current_thd->cond_count+1)*2,MYF(0))))
+ return TRUE; /* purecov: inspected */
+ and_level=0; end=key_fields;
+ if (cond)
+ add_key_fields(join_tab,&end,&and_level,cond,normal_tables);
+ for (i=0 ; i < tables ; i++)
+ {
+ if (join_tab[i].on_expr)
+ {
+ add_key_fields(join_tab,&end,&and_level,join_tab[i].on_expr,
+ join_tab[i].table->map);
+ }
+ }
+ if (init_dynamic_array(keyuse,sizeof(KEYUSE),20,64))
+ {
+ my_free((gptr) key_fields,MYF(0));
+ return TRUE;
+ }
+ /* fill keyuse with found key parts */
+ for (KEY_FIELD *field=key_fields ; field != end ; field++)
+ add_key_part(keyuse,field);
+ my_free((gptr) key_fields,MYF(0));
+ }
+
+ if (ftfuncs.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.
+ */
+ if (keyuse->elements)
+ {
+ KEYUSE end,*prev,*save_pos,*use;
+
+ qsort(keyuse->buffer,keyuse->elements,sizeof(KEYUSE),
+ (qsort_cmp) sort_keyuse);
+
+ bzero((char*) &end,sizeof(end)); /* Add for easy testing */
+ VOID(insert_dynamic(keyuse,(gptr) &end));
+
+ use=save_pos=dynamic_element(keyuse,0,KEYUSE*);
+ prev=&end;
+ found_eq_constant=0;
+ for (i=0 ; i < keyuse->elements-1 ; i++,use++)
+ {
+ if (!use->used_tables)
+ use->table->const_key_parts[use->key]|=
+ (key_part_map) 1 << use->keypart;
+ if (use->keypart != FT_KEYPART)
+ {
+ if (use->key == prev->key && use->table == prev->table)
+ {
+ if (prev->keypart+1 < use->keypart ||
+ prev->keypart == use->keypart && found_eq_constant)
+ continue; /* remove */
+ }
+ else if (use->keypart != 0) // First found must be 0
+ continue;
+ }
+
+ *save_pos= *use;
+ prev=use;
+ found_eq_constant= !use->used_tables;
+ /* Save ptr to first use */
+ if (!use->table->reginfo.join_tab->keyuse)
+ use->table->reginfo.join_tab->keyuse=save_pos;
+ use->table->reginfo.join_tab->checked_keys|= (key_map) 1 << use->key;
+ save_pos++;
+ }
+ i=(uint) (save_pos-(KEYUSE*) keyuse->buffer);
+ VOID(set_dynamic(keyuse,(gptr) &end,i));
+ keyuse->elements=i;
+ }
+ return FALSE;
+}
+
+
+/*****************************************************************************
+** Go through all combinations of not marked tables and find the one
+** which uses least records
+*****************************************************************************/
+
+/* Save const tables first as used tables */
+
+static void
+set_position(JOIN *join,uint idx,JOIN_TAB *table,KEYUSE *key)
+{
+ join->positions[idx].table= table;
+ join->positions[idx].key=key;
+ join->positions[idx].records_read=1.0; /* This is a const table */
+
+ /* 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++)
+ {
+ JOIN_TAB *tmp=pos[0];
+ pos[0]=next;
+ next=tmp;
+ }
+ join->best_ref[idx]=table;
+}
+
+
+static void
+find_best_combination(JOIN *join, table_map rest_tables)
+{
+ DBUG_ENTER("find_best_combination");
+ join->best_read=DBL_MAX;
+ find_best(join,rest_tables, join->const_tables,1.0,0.0);
+ DBUG_VOID_RETURN;
+}
+
+
+static void
+find_best(JOIN *join,table_map rest_tables,uint idx,double record_count,
+ double read_time)
+{
+ ulong rec;
+ double tmp;
+
+ if (!rest_tables)
+ {
+ DBUG_PRINT("best",("read_time: %g record_count: %g",read_time,
+ record_count));
+
+ read_time+=record_count/(double) TIME_FOR_COMPARE;
+ if (join->sort_by_table &&
+ join->sort_by_table != join->positions[join->const_tables].table->table)
+ read_time+=record_count; // We have to make a temp table
+ if (read_time < join->best_read)
+ {
+ memcpy((gptr) join->best_positions,(gptr) join->positions,
+ sizeof(POSITION)*idx);
+ join->best_read=read_time;
+ }
+ return;
+ }
+ if (read_time+record_count/(double) TIME_FOR_COMPARE >= join->best_read)
+ return; /* Found better before */
+
+ JOIN_TAB *s;
+ double best_record_count=DBL_MAX,best_read_time=DBL_MAX;
+ for (JOIN_TAB **pos=join->best_ref+idx ; (s=*pos) ; pos++)
+ {
+ table_map real_table_bit=s->table->map;
+ if ((rest_tables & real_table_bit) && !(rest_tables & s->dependent))
+ {
+ double best,best_time,records;
+ best=best_time=records=DBL_MAX;
+ KEYUSE *best_key=0;
+
+ if (s->keyuse)
+ { /* Use key if possible */
+ TABLE *table=s->table;
+ KEYUSE *keyuse,*start_key=0;
+ double best_records=DBL_MAX;
+
+ /* Test how we can use keys */
+ rec= s->records/10; /* Assume 10 records/key */
+ for (keyuse=s->keyuse ; keyuse->table == table ;)
+ {
+ key_map found_part=0;
+ table_map found_ref=0;
+ uint key=keyuse->key;
+ KEY *keyinfo=table->key_info+key;
+ bool ft_key=(keyuse->keypart == FT_KEYPART);
+
+ start_key=keyuse;
+ do
+ {
+ uint keypart=keyuse->keypart;
+ do
+ {
+ if(!ft_key)
+ {
+ table_map map;
+ if (!(rest_tables & keyuse->used_tables))
+ {
+ found_part|= (key_part_map) 1 << keypart;
+ 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 this 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)))
+ {
+ uint tablenr;
+ for (tablenr=0 ; ! (map & 1) ; map>>=1, tablenr++) ;
+ if (map == 1) // Only one table
+ {
+ TABLE *tmp_table=join->all_tables[tablenr];
+ if (rec > tmp_table->file->records)
+ rec=max(tmp_table->file->records,100);
+ }
+ }
+ }
+ keyuse++;
+ } while (keyuse->table == table && keyuse->key == key &&
+ keyuse->keypart == keypart);
+ } while (keyuse->table == table && keyuse->key == key);
+
+ /*
+ ** Assume that that each key matches a proportional part of table.
+ */
+ if (!found_part && !ft_key)
+ continue; // Nothing usable found
+ if (rec == 0)
+ rec=1L; // Fix for small tables
+
+ /*
+ ** ft-keys require special treatment
+ */
+ if (ft_key)
+ {
+ /*
+ ** 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;
+ }
+ else
+ {
+ /*
+ ** Check if we found full key
+ */
+ if (found_part == PREV_BITS(uint,keyinfo->key_parts))
+ { /* use eq key */
+ if ((keyinfo->flags & (HA_NOSAME | HA_NULL_PART_KEY)) == HA_NOSAME)
+ {
+ tmp=prev_record_reads(join,found_ref);
+ records=1.0;
+ }
+ else
+ {
+ if (!found_ref) // If not const key
+ {
+ if (table->quick_keys & ((key_map) 1 << key))
+ records= (double) table->quick_rows[key];
+ else
+ records= (double) s->records; // quick_range couldn't use key!
+ }
+ else
+ {
+ if (!(records=keyinfo->rec_per_key[keyinfo->key_parts-1]))
+ { // Prefere longer keys
+ records=
+ ((double) s->records / (double) rec *
+ (1.0 +
+ ((double) (table->max_key_length-keyinfo->key_length) /
+ (double) table->max_key_length)));
+ if (records < 2.0)
+ records=2.0; // Can't be as good as a unique
+ }
+ }
+ if (table->used_keys & ((key_map) 1 << key))
+ {
+ /* we can use only index tree */
+ uint keys_per_block= table->file->block_size/2/
+ keyinfo->key_length+1;
+ tmp=(record_count*(records+keys_per_block-1)/
+ keys_per_block);
+ }
+ else
+ tmp=record_count*min(records,s->worst_seeks);
+ }
+ }
+ 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)
+ */
+ if (found_part & 1)
+ {
+ uint max_key_part=max_part_bit(found_part);
+ /* 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];
+ else
+ {
+ /* Check if we have statistic about the distribution */
+ if ((records=keyinfo->rec_per_key[max_key_part-1]))
+ tmp=records;
+ 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)
+ */
+ double rec_per_key;
+ if (!(rec_per_key=(double)
+ keyinfo->rec_per_key[keyinfo->key_parts-1]))
+ rec_per_key=(double) s->records/rec+1;
+
+ if (!s->records)
+ tmp=0;
+ else if (rec_per_key/(double) s->records >= 0.01)
+ tmp=rec_per_key;
+ else
+ {
+ double a=s->records*0.01;
+ tmp=(max_key_part * (rec_per_key - a) +
+ a*keyinfo->key_parts - rec_per_key)/
+ (keyinfo->key_parts-1);
+ set_if_bigger(tmp,1.0);
+ }
+ records=(ulong) tmp;
+ }
+ }
+ if (table->used_keys & ((key_map) 1 << key))
+ {
+ /* we can use only index tree */
+ uint keys_per_block= table->file->block_size/2/
+ keyinfo->key_length+1;
+ tmp=record_count*(tmp+keys_per_block-1)/keys_per_block;
+ }
+ else
+ tmp=record_count*min(tmp,s->worst_seeks);
+ }
+ else
+ tmp=best_time; // Do nothing
+ }
+ } /* not ftkey */
+ if (tmp < best_time - records/(double) TIME_FOR_COMPARE)
+ {
+ best_time=tmp + records/(double) TIME_FOR_COMPARE;
+ best=tmp;
+ best_records=records;
+ best_key=start_key;
+ }
+ }
+ records=best_records;
+ }
+ if (records >= s->found_records || best > s->read_time)
+ { // Check full join
+ if (s->on_expr)
+ {
+ tmp=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));
+ }
+ if (best == DBL_MAX ||
+ (tmp + record_count/(double) TIME_FOR_COMPARE*s->found_records <
+ best + record_count/(double) TIME_FOR_COMPARE*records))
+ {
+ best=tmp;
+ records=s->found_records;
+ best_key=0;
+ }
+ }
+ join->positions[idx].records_read=(double) 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)
+ join->sort_by_table= (TABLE*) 1; // Must use temporary table
+
+ /*
+ Go to the next level only if there hasn't been a better key on
+ this level! This will cut down the search for a lot simple cases!
+ */
+ double current_record_count=record_count*records;
+ double current_read_time=read_time+best;
+ if (best_record_count > current_record_count ||
+ best_read_time > current_read_time ||
+ idx == join->const_tables && s->table == join->sort_by_table)
+ {
+ if (best_record_count >= current_record_count &&
+ best_read_time >= current_read_time &&
+ (!(s->key_dependent & rest_tables) || records < 2.0))
+ {
+ best_record_count=current_record_count;
+ best_read_time=current_read_time;
+ }
+ swap(JOIN_TAB*,join->best_ref[idx],*pos);
+ find_best(join,rest_tables & ~real_table_bit,idx+1,
+ current_record_count,current_read_time);
+ swap(JOIN_TAB*,join->best_ref[idx],*pos);
+ }
+ if (join->select_options & SELECT_STRAIGHT_JOIN)
+ break; // Don't test all combinations
+ }
+ }
+}
+
+
+/*
+** Find how much space the prevous read not const tables takes in cache
+*/
+
+static uint
+cache_record_length(JOIN *join,uint idx)
+{
+ uint length;
+ JOIN_TAB **pos,**end;
+ THD *thd=current_thd;
+
+ length=0;
+ for (pos=join->best_ref+join->const_tables,end=join->best_ref+idx ;
+ pos != end ;
+ pos++)
+ {
+ JOIN_TAB *join_tab= *pos;
+ if (!join_tab->used_fieldlength)
+ { /* Not calced yet */
+ uint null_fields,blobs,fields,rec_length;
+ null_fields=blobs=fields=rec_length=0;
+
+ Field **f_ptr,*field;
+ for (f_ptr=join_tab->table->field ; (field= *f_ptr) ; f_ptr++)
+ {
+ if (field->query_id == thd->query_id)
+ {
+ uint flags=field->flags;
+ fields++;
+ rec_length+=field->pack_length();
+ if (flags & BLOB_FLAG)
+ blobs++;
+ if (!(flags & NOT_NULL_FLAG))
+ null_fields++;
+ }
+ }
+ if (null_fields)
+ rec_length+=(join_tab->table->null_fields+7)/8;
+ if (join_tab->table->maybe_null)
+ rec_length+=sizeof(my_bool);
+ if (blobs)
+ {
+ uint blob_length=(uint) (join_tab->table->file->mean_rec_length-
+ (join_tab->table->reclength- rec_length));
+ rec_length+=(uint) max(4,blob_length);
+ }
+ join_tab->used_fields=fields;
+ join_tab->used_fieldlength=rec_length;
+ join_tab->used_blobs=blobs;
+ }
+ length+=join_tab->used_fieldlength;
+ }
+ return length;
+}
+
+
+static double
+prev_record_reads(JOIN *join,table_map found_ref)
+{
+ double found=1.0;
+
+ for (POSITION *pos=join->positions ; found_ref ; pos++)
+ {
+ if (pos->table->table->map & found_ref)
+ {
+ found_ref&= ~pos->table->table->map;
+ found*=pos->records_read;
+ }
+ }
+ return found;
+}
+
+
+/*****************************************************************************
+** Set up join struct according to best position.
+*****************************************************************************/
+
+static bool
+get_best_combination(JOIN *join)
+{
+ uint i,key,tablenr;
+ table_map used_tables;
+ TABLE *table;
+ JOIN_TAB *join_tab,*j;
+ KEYUSE *keyuse;
+ KEY *keyinfo;
+ uint table_count;
+ String *ft_tmp=0;
+ char tmp1[FT_QUERY_MAXLEN];
+ String tmp2(tmp1,sizeof(tmp1));
+
+ table_count=join->tables;
+ if (!(join->join_tab=join_tab=
+ (JOIN_TAB*) sql_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;
+ for (j=join_tab, tablenr=0 ; tablenr < table_count ; tablenr++,j++)
+ {
+ 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_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;
+
+ keyinfo=table->key_info+key;
+ if (ftkey)
+ {
+ ft_tmp=keyuse->val->val_str(&tmp2);
+ length=ft_tmp->length();
+ keyparts=1;
+ }
+ else
+ {
+ keyparts=length=0;
+ do
+ {
+ if (!((~used_tables) & keyuse->used_tables))
+ {
+ if (keyparts == keyuse->keypart)
+ {
+ keyparts++;
+ length+=keyinfo->key_part[keyuse->keypart].length +
+ test(keyinfo->key_part[keyuse->keypart].null_bit);
+ }
+ }
+ 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]=keyuse->val;
+ if (!keyuse->used_tables &&
+ !(join->select_options & SELECT_DESCRIBE))
+ {
+ // AFAIK key_buff is zeroed...
+ // We don't need to free ft_tmp as the buffer will be freed atom.
+ memcpy((gptr)key_buff, (gptr) ft_tmp->ptr(), ft_tmp->length());
+ }
+ else
+ {
+ return TRUE; // not supported yet. SerG
+ }
+ j->type=JT_FT;
+ }
+ else
+ {
+ 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 (current_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)
+ {
+ j->table->const_table=1;
+ if (join->const_tables == tablenr)
+ {
+ join->const_tables++;
+ join->const_table_map|=form->map;
+ }
+ }
+ 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 */
+ j->type=JT_CONST; /* purecov: deadcode */
+ if (join->const_tables == tablenr)
+ {
+ join->const_tables++; /* purecov: deadcode */
+ join->const_table_map|=form->map;
+ }
+ }
+ else
+ j->type=JT_EQ_REF;
+ }
+ }
+
+ 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;
+}
+
+
+static store_key *
+get_store_key(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,
+ 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,
+ 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,
+ key_buff + maybe_null,
+ maybe_null ? key_buff : 0,
+ key_part->length,
+ keyuse->val);
+}
+
+/*
+** 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)
+{
+ THD *thd=current_thd;
+ ulong cuted_fields=thd->cuted_fields;
+ thd->count_cuted_fields=1;
+ item->save_in_field(field);
+ thd->count_cuted_fields=0;
+ return cuted_fields != thd->cuted_fields;
+}
+
+
+static bool
+make_simple_join(JOIN *join,TABLE *tmp_table)
+{
+ TABLE **tableptr;
+ JOIN_TAB *join_tab;
+
+ if (!(tableptr=(TABLE**) sql_alloc(sizeof(TABLE*))) ||
+ !(join_tab=(JOIN_TAB*) sql_alloc(sizeof(JOIN_TAB))))
+ return TRUE;
+ join->join_tab=join_tab;
+ join->table=tableptr; tableptr[0]=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->first_record=join->sort_and_group=0;
+ join->sum_funcs=0;
+ join->send_records=0L;
+ join->group=0;
+
+ join_tab->cache.buff=0; /* No cacheing */
+ join_tab->table=tmp_table;
+ join_tab->select=0;
+ join_tab->select_cond=0;
+ join_tab->quick=0;
+ bzero((char*) &join_tab->read_record,sizeof(join_tab->read_record));
+ join_tab->type= JT_ALL; /* Map through all records */
+ join_tab->keys= (uint) ~0; /* test everything in quick */
+ join_tab->info=0;
+ join_tab->on_expr=0;
+ join_tab->ref.key = -1;
+ tmp_table->status=0;
+ tmp_table->null_row=0;
+ join_tab->read_first_record= join_init_read_record;
+ return FALSE;
+}
+
+
+static bool
+make_join_select(JOIN *join,SQL_SELECT *select,COND *cond)
+{
+ DBUG_ENTER("make_join_select");
+ if (select)
+ {
+ table_map used_tables;
+ if (join->tables > 1)
+ cond->update_used_tables(); // Tablenr may have changed
+ { // Check const tables
+ COND *const_cond=
+ make_cond_for_table(cond,join->const_table_map,(table_map) 0);
+ DBUG_EXECUTE("where",print_where(const_cond,"constants"););
+ if (const_cond && !const_cond->val_int())
+ DBUG_RETURN(1); // Impossible const condition
+ }
+ used_tables=(select->const_tables=join->const_table_map) | RAND_TABLE_BIT;
+ for (uint i=join->const_tables ; i < join->tables ; i++)
+ {
+ JOIN_TAB *tab=join->join_tab+i;
+ table_map current_map= tab->table->map;
+ used_tables|=current_map;
+ COND *tmp=make_cond_for_table(cond,used_tables,current_map);
+ if (tmp)
+ {
+ DBUG_EXECUTE("where",print_where(tmp,tab->table->table_name););
+ SQL_SELECT *sel=tab->select=(SQL_SELECT*)
+ sql_memdup((gptr) select, sizeof(SQL_SELECT));
+ if (!sel)
+ DBUG_RETURN(1); // End of memory
+ tab->select_cond=sel->cond=tmp;
+ sel->head=tab->table;
+ if (tab->quick)
+ {
+ if (tab->needed_reg == 0 && tab->type != JT_EQ_REF &&
+ (tab->type != JT_REF ||
+ (uint) tab->ref.key == tab->quick->index))
+ {
+ sel->quick=tab->quick; // Use value from get_quick_...
+ sel->quick_keys=0;
+ sel->needed_reg=0;
+ }
+ else
+ {
+ delete tab->quick;
+ }
+ tab->quick=0;
+ }
+ uint ref_key=(uint) sel->head->reginfo.join_tab->ref.key+1;
+ if (i == join->const_tables && ref_key)
+ {
+ if (tab->const_keys && tab->table->reginfo.impossible_range)
+ DBUG_RETURN(1);
+ }
+ else if (tab->type == JT_ALL)
+ {
+ if (tab->const_keys &&
+ tab->table->reginfo.impossible_range)
+ DBUG_RETURN(1); // Impossible range
+ /*
+ We plan to scan all rows.
+ Check again if we should use an index instead if
+ we could have used an column from a previous table in
+ the index or if we are using limit and this is the first table
+ */
+
+ 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)
+ {
+ if (sel->test_quick_select(tab->keys,
+ used_tables & ~ current_map,
+ join->thd->select_limit) < 0)
+ DBUG_RETURN(1); // Impossible range
+ }
+ else
+ {
+ sel->needed_reg=tab->needed_reg;
+ sel->quick_keys=0;
+ }
+ if ((sel->quick_keys | sel->needed_reg) & ~tab->checked_keys)
+ {
+ tab->keys=sel->quick_keys | sel->needed_reg;
+ tab->use_quick= (sel->needed_reg &&
+ (!select->quick_keys ||
+ (select->quick &&
+ (select->quick->records >= 100L)))) ?
+ 2 : 1;
+ sel->read_tables= used_tables;
+ }
+ if (i != join->const_tables && tab->use_quick != 2)
+ { /* Read with cache */
+ if ((tmp=make_cond_for_table(cond,
+ join->const_table_map |
+ current_map,
+ current_map)))
+ {
+ DBUG_EXECUTE("where",print_where(tmp,"cache"););
+ tab->cache.select=(SQL_SELECT*) sql_memdup((gptr) sel,
+ sizeof(SQL_SELECT));
+ tab->cache.select->cond=tmp;
+ tab->cache.select->read_tables=join->const_table_map;
+ }
+ }
+ }
+ if (tab->type == JT_REF && sel->quick &&
+ tab->ref.key_length < sel->quick->max_used_key_length)
+ {
+ /* Range uses longer key; Use this instead of ref on key */
+ tab->type=JT_ALL;
+ tab->use_quick=1;
+ tab->ref.key_parts=0; // Don't use ref key.
+ join->best_positions[i].records_read=sel->quick->records;
+ }
+ }
+ }
+ }
+ DBUG_RETURN(0);
+}
+
+
+static void
+make_join_readinfo(JOIN *join,uint options)
+{
+ uint i;
+ DBUG_ENTER("make_join_readinfo");
+
+ for (i=join->const_tables ; i < join->tables ; i++)
+ {
+ JOIN_TAB *tab=join->join_tab+i;
+ TABLE *table=tab->table;
+ tab->read_record.table= table;
+ tab->read_record.file=table->file;
+ tab->next_select=sub_select; /* normal select */
+ switch (tab->type) {
+ case JT_SYSTEM: // Only happens with left join
+ table->status=STATUS_NO_RECORD;
+ tab->read_first_record= join_read_system;
+ tab->read_record.read_record= join_no_more_records;
+ break;
+ case JT_CONST: // Only happens with left join
+ table->status=STATUS_NO_RECORD;
+ tab->read_first_record= join_read_const;
+ tab->read_record.read_record= join_no_more_records;
+ break;
+ case JT_EQ_REF:
+ table->status=STATUS_NO_RECORD;
+ delete tab->quick;
+ tab->quick=0;
+ 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))
+ {
+ table->key_read=1;
+ table->file->extra(HA_EXTRA_KEYREAD);
+ }
+ break;
+ case JT_REF:
+ table->status=STATUS_NO_RECORD;
+ delete tab->quick;
+ 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))
+ {
+ table->key_read=1;
+ table->file->extra(HA_EXTRA_KEYREAD);
+ }
+ break;
+ case JT_FT:
+ table->status=STATUS_NO_RECORD;
+ table->file->index_init(tab->ref.key);
+ tab->read_first_record= join_ft_read_first;
+ tab->read_record.read_record= join_ft_read_next;
+ break;
+ case JT_ALL:
+ /*
+ ** if previous table use cache
+ */
+ table->status=STATUS_NO_RECORD;
+ if (i != join->const_tables && (options & SELECT_USE_CACHE) &&
+ tab->use_quick != 2 && !tab->on_expr)
+ {
+ if ((options & SELECT_DESCRIBE) ||
+ !join_init_cache(join->thd,join->join_tab+join->const_tables,
+ i-join->const_tables))
+ {
+ tab[-1].next_select=sub_select_cache; /* Patch previous */
+ }
+ }
+ /* These init changes read_record */
+ if (tab->use_quick == 2)
+ tab->read_first_record= join_init_quick_read_record;
+ else
+ {
+ tab->read_first_record= join_init_read_record;
+ 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->read_first_record= join_init_read_first_with_key;
+ tab->type=JT_NEXT; // Read with index_first / index_next
+ }
+ }
+ break;
+ default:
+ DBUG_PRINT("error",("Table type %d found",tab->type)); /* purecov: deadcode */
+ break; /* purecov: deadcode */
+ case JT_UNKNOWN:
+ case JT_MAYBE_REF:
+ abort(); /* purecov: deadcode */
+ }
+ }
+ join->join_tab[join->tables-1].next_select=0; /* Set by do_select */
+ DBUG_VOID_RETURN;
+}
+
+
+static void
+join_free(JOIN *join)
+{
+ JOIN_TAB *tab,*end;
+
+ if (join->table)
+ {
+ /* only sorted table is cached */
+ if (join->tables > join->const_tables)
+ free_io_cache(join->table[join->const_tables]);
+ for (tab=join->join_tab,end=tab+join->tables ; tab != end ; tab++)
+ {
+ delete tab->select;
+ delete tab->quick;
+ x_free(tab->cache.buff);
+ end_read_record(&tab->read_record);
+ if (tab->table)
+ {
+ if (tab->table->key_read)
+ {
+ tab->table->key_read=0;
+ tab->table->file->extra(HA_EXTRA_NO_KEYREAD);
+ }
+ tab->table->file->index_end();
+ }
+ }
+ 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)
+ {
+ 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;
+}
+
+
+/*****************************************************************************
+** 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
+eq_ref_table(JOIN *join, ORDER *start_order, JOIN_TAB *tab)
+{
+ if (tab->cached_eq_ref_table) // If cached
+ return tab->eq_ref_table;
+ tab->cached_eq_ref_table=1;
+ if (tab->type == JT_CONST) // We can skip const tables
+ return (tab->eq_ref_table=1); /* purecov: inspected */
+ if (tab->type != JT_EQ_REF)
+ return (tab->eq_ref_table=0); // We must use this
+ Item **ref_item=tab->ref.items;
+ Item **end=ref_item+tab->ref.key_parts;
+ uint found=0;
+ table_map map=tab->table->map;
+
+ for (; ref_item != end ; ref_item++)
+ {
+ if (! (*ref_item)->const_item())
+ { // Not a const ref
+ ORDER *order;
+ for (order=start_order ; order ; order=order->next)
+ {
+ if ((*ref_item)->eq(order->item[0]))
+ break;
+ }
+ if (order)
+ {
+ found++;
+ dbug_assert(!(order->used & map));
+ order->used|=map;
+ continue; // Used in ORDER BY
+ }
+ if (!only_eq_ref_tables(join,start_order, (*ref_item)->used_tables()))
+ return (tab->eq_ref_table=0);
+ }
+ }
+ /* Check that there was no reference to table before sort order */
+ for ( ; found && start_order ; start_order=start_order->next)
+ {
+ if (start_order->used & map)
+ {
+ found--;
+ continue;
+ }
+ if (start_order->depend_map & map)
+ return (tab->eq_ref_table=0);
+ }
+ return tab->eq_ref_table=1;
+}
+
+
+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 */
+ for (JOIN_TAB **tab=join->map2table ; tables ; tab++, tables>>=1)
+ {
+ if (tables & 1 && !eq_ref_table(join, order, *tab))
+ return 0;
+ }
+ return 1;
+}
+
+
+/* Update the dependency map for the tables */
+
+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++)
+ {
+ TABLE_REF *ref= &join_tab->ref;
+ table_map depend_map=0;
+ Item **item=ref->items;
+ uint i;
+ for (i=0 ; i < ref->key_parts ; i++,item++)
+ depend_map|=(*item)->used_tables();
+ ref->depend_map=depend_map;
+ for (JOIN_TAB *join_tab2=join->join_tab;
+ depend_map ;
+ join_tab2++,depend_map>>=1 )
+ {
+ if (depend_map & 1)
+ ref->depend_map|=join_tab2->ref.depend_map;
+ }
+ }
+}
+
+
+/* Update the dependency map for the sort order */
+
+static void update_depend_map(JOIN *join, ORDER *order)
+{
+ for ( ; order ; order=order->next)
+ {
+ table_map depend_map;
+ order->item[0]->update_used_tables();
+ order->depend_map=depend_map=order->item[0]->used_tables();
+ if (!(order->depend_map & RAND_TABLE_BIT)) // Not item_sum() or RAND()
+ {
+ for (JOIN_TAB *join_tab=join->join_tab;
+ depend_map ;
+ join_tab++, depend_map>>=1)
+ {
+ if (depend_map & 1)
+ order->depend_map|=join_tab->ref.depend_map;
+ }
+ }
+ }
+}
+
+
+/*
+** simple_order is set to 1 if sort_order only uses fields from head table
+** and the head table is not a LEFT JOIN table
+*/
+
+static ORDER *
+remove_const(JOIN *join,ORDER *first_order, COND *cond, bool *simple_order)
+{
+ if (join->tables == join->const_tables)
+ return 0; // No need to sort
+ DBUG_ENTER("remove_const");
+ ORDER *order,**prev_ptr;
+ table_map first_table= join->join_tab[join->const_tables].table->map;
+ table_map not_const_tables= ~join->const_table_map;
+ table_map ref;
+ prev_ptr= &first_order;
+ *simple_order= join->join_tab[join->const_tables].on_expr ? 0 : 1;
+
+ /* NOTE: A variable of not_const_tables ^ first_table; breaks gcc 2.7 */
+
+ update_depend_map(join, first_order);
+ for (order=first_order; order ; order=order->next)
+ {
+ table_map order_tables=order->item[0]->used_tables();
+ if (order->item[0]->with_sum_func)
+ *simple_order=0; // Must do a temp table to sort
+ else if (!(order_tables & not_const_tables))
+ {
+ DBUG_PRINT("info",("removing: %s", order->item[0]->full_name()));
+ continue; // skipp const item
+ }
+ else
+ {
+ if (order_tables & RAND_TABLE_BIT)
+ *simple_order=0;
+ else
+ {
+ Item *comp_item=0;
+ if (cond && const_expression_in_where(cond,order->item[0], &comp_item))
+ {
+ DBUG_PRINT("info",("removing: %s", order->item[0]->full_name()));
+ continue;
+ }
+ if ((ref=order_tables & (not_const_tables ^ first_table)))
+ {
+ if (only_eq_ref_tables(join,first_order,ref))
+ {
+ DBUG_PRINT("info",("removing: %s", order->item[0]->full_name()));
+ continue;
+ }
+ *simple_order=0; // Must do a temp table to sort
+ }
+ }
+ }
+ *prev_ptr= order; // use this entry
+ prev_ptr= &order->next;
+ }
+ *prev_ptr=0;
+ if (!first_order) // Nothing to sort/group
+ *simple_order=1;
+ DBUG_PRINT("exit",("simple_order: %d",(int) *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)
+{
+ DBUG_ENTER("return_zero_rows");
+
+ if (select_options & SELECT_DESCRIBE)
+ {
+ describe_info(info);
+ DBUG_RETURN(0);
+ }
+ if (procedure)
+ {
+ if (result->prepare(fields)) // This hasn't been done yet
+ DBUG_RETURN(-1);
+ }
+ if (send_row)
+ {
+ for (TABLE_LIST *table=tables; table ; table=table->next)
+ mark_as_null_row(table->table); // All fields are NULL
+ if (having && having->val_int() == 0.0)
+ send_row=0;
+ }
+ if (!tables || !(result->send_fields(fields,1)))
+ {
+ if (send_row)
+ result->send_data(fields);
+ if (tables) // Not from do_select()
+ result->send_eof(); // Should be safe
+ }
+ DBUG_RETURN(0);
+}
+
+
+static void clear_tables(JOIN *join)
+{
+ for (uint i=0 ; i < join->tables ; i++)
+ mark_as_null_row(join->table[i]); // All fields are NULL
+}
+
+/*****************************************************************************
+** 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 {
+public:
+ static void *operator new(size_t size) {return (void*) sql_alloc(size); }
+ static void operator delete(void *ptr __attribute__((unused)),
+ size_t size __attribute__((unused))) {} /*lint -e715 */
+
+ Item *and_level;
+ Item_func *cmp_func;
+ COND_CMP(Item *a,Item_func *b) :and_level(a),cmp_func(b) {}
+};
+
+#ifdef __GNUC__
+template class I_List<COND_CMP>;
+template class I_List_iterator<COND_CMP>;
+template class List<Item_func_match>;
+template class List_iterator<Item_func_match>;
+#endif
+
+/*
+** change field = field to field = const for each found field = const in the
+** and_level
+*/
+
+static void
+change_cond_ref_to_const(I_List<COND_CMP> *save_list,Item *and_father,
+ Item *cond, Item *field, Item *value)
+{
+ if (cond->type() == Item::COND_ITEM)
+ {
+ bool and_level= ((Item_cond*) cond)->functype() ==
+ Item_func::COND_AND_FUNC;
+ List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
+ Item *item;
+ while ((item=li++))
+ change_cond_ref_to_const(save_list,and_level ? cond : item, item,
+ field, value);
+ return;
+ }
+ if (cond->eq_cmp_result() == Item::COND_OK)
+ return; // Not a boolean function
+
+ Item_bool_func2 *func= (Item_bool_func2*) cond;
+ Item *left_item= func->arguments()[0];
+ Item *right_item= func->arguments()[1];
+ Item_func::Functype functype= func->functype();
+
+ if (right_item->eq(field) && left_item != value)
+ {
+ Item *tmp=value->new_item();
+ if (tmp)
+ {
+ func->arguments()[1] = tmp;
+ func->update_used_tables();
+ if ((functype == Item_func::EQ_FUNC || functype == Item_func::EQUAL_FUNC)
+ && and_father != cond && !left_item->const_item())
+ {
+ cond->marker=1;
+ COND_CMP *tmp2;
+ if ((tmp2=new COND_CMP(and_father,func)))
+ save_list->push_back(tmp2);
+ }
+ func->set_cmp_func(item_cmp_type(func->arguments()[0]->result_type(),
+ func->arguments()[1]->result_type()));
+ }
+ }
+ else if (left_item->eq(field) && right_item != value)
+ {
+ Item *tmp=value->new_item();
+ if (tmp)
+ {
+ func->arguments()[0] = value = tmp;
+ func->update_used_tables();
+ if ((functype == Item_func::EQ_FUNC || functype == Item_func::EQUAL_FUNC)
+ && and_father != cond && !right_item->const_item())
+ {
+ func->arguments()[0] = func->arguments()[1]; // For easy check
+ func->arguments()[1] = value;
+ cond->marker=1;
+ COND_CMP *tmp2;
+ if ((tmp2=new COND_CMP(and_father,func)))
+ save_list->push_back(tmp2);
+ }
+ func->set_cmp_func(item_cmp_type(func->arguments()[0]->result_type(),
+ func->arguments()[1]->result_type()));
+ }
+ }
+}
+
+
+static void
+propagate_cond_constants(I_List<COND_CMP> *save_list,COND *and_level,
+ COND *cond)
+{
+ if (cond->type() == Item::COND_ITEM)
+ {
+ bool and_level= ((Item_cond*) cond)->functype() ==
+ Item_func::COND_AND_FUNC;
+ List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
+ Item *item;
+ I_List<COND_CMP> save;
+ while ((item=li++))
+ {
+ propagate_cond_constants(&save,and_level ? cond : item, item);
+ }
+ if (and_level)
+ { // Handle other found items
+ I_List_iterator<COND_CMP> cond_itr(save);
+ COND_CMP *cond_cmp;
+ while ((cond_cmp=cond_itr++))
+ if (!cond_cmp->cmp_func->arguments()[0]->const_item())
+ change_cond_ref_to_const(&save,cond_cmp->and_level,
+ cond_cmp->and_level,
+ cond_cmp->cmp_func->arguments()[0],
+ cond_cmp->cmp_func->arguments()[1]);
+ }
+ }
+ else if (and_level != cond && !cond->marker) // In a AND group
+ {
+ if (cond->type() == Item::FUNC_ITEM &&
+ (((Item_func*) cond)->functype() == Item_func::EQ_FUNC ||
+ ((Item_func*) cond)->functype() == Item_func::EQUAL_FUNC))
+ {
+ Item_func_eq *func=(Item_func_eq*) cond;
+ bool left_const= func->arguments()[0]->const_item();
+ bool right_const=func->arguments()[1]->const_item();
+ if (!(left_const && right_const))
+ {
+ if (right_const)
+ {
+ func->arguments()[1]=resolve_const_item(func->arguments()[1],
+ func->arguments()[0]);
+ func->update_used_tables();
+ change_cond_ref_to_const(save_list,and_level,and_level,
+ func->arguments()[0],
+ func->arguments()[1]);
+ }
+ else if (left_const)
+ {
+ func->arguments()[0]=resolve_const_item(func->arguments()[0],
+ func->arguments()[1]);
+ func->update_used_tables();
+ change_cond_ref_to_const(save_list,and_level,and_level,
+ func->arguments()[1],
+ func->arguments()[0]);
+ }
+ }
+ }
+ }
+}
+
+
+static COND *
+optimize_cond(COND *conds,Item::cond_result *cond_value)
+{
+ if (!conds)
+ {
+ *cond_value= Item::COND_TRUE;
+ return conds;
+ }
+ /* change field = field to field = const for each found field = const */
+ 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
+ */
+ DBUG_EXECUTE("where",print_where(conds,"after const change"););
+ conds=remove_eq_conds(conds,cond_value) ;
+ DBUG_EXECUTE("info",print_where(conds,"after remove"););
+ return conds;
+}
+
+
+/*
+** 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 *
+remove_eq_conds(COND *cond,Item::cond_result *cond_value)
+{
+ if (cond->type() == Item::COND_ITEM)
+ {
+ bool and_level= ((Item_cond*) cond)->functype()
+ == Item_func::COND_AND_FUNC;
+ List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
+ Item::cond_result tmp_cond_value;
+
+ *cond_value=Item::COND_UNDEF;
+ Item *item;
+ while ((item=li++))
+ {
+ Item *new_item=remove_eq_conds(item,&tmp_cond_value);
+ if (!new_item)
+ {
+#ifdef DELETE_ITEMS
+ delete item; // This may be shared
+#endif
+ li.remove();
+ }
+ else if (item != new_item)
+ {
+#ifdef DELETE_ITEMS
+ delete item; // This may be shared
+#endif
+ VOID(li.replace(new_item));
+ }
+ if (*cond_value == Item::COND_UNDEF)
+ *cond_value=tmp_cond_value;
+ switch (tmp_cond_value) {
+ case Item::COND_OK: // Not TRUE or FALSE
+ if (and_level || *cond_value == Item::COND_FALSE)
+ *cond_value=tmp_cond_value;
+ break;
+ case Item::COND_FALSE:
+ if (and_level)
+ {
+ *cond_value=tmp_cond_value;
+ return (COND*) 0; // Always false
+ }
+ break;
+ case Item::COND_TRUE:
+ if (!and_level)
+ {
+ *cond_value= tmp_cond_value;
+ return (COND*) 0; // Always true
+ }
+ break;
+ case Item::COND_UNDEF: // Impossible
+ break; /* purecov: deadcode */
+ }
+ }
+ if (!((Item_cond*) cond)->argument_list()->elements ||
+ *cond_value != Item::COND_OK)
+ return (COND*) 0;
+ if (((Item_cond*) cond)->argument_list()->elements == 1)
+ { // Remove list
+ item= ((Item_cond*) cond)->argument_list()->head();
+ ((Item_cond*) cond)->argument_list()->empty();
+ return item;
+ }
+ }
+ else if (cond->type() == Item::FUNC_ITEM &&
+ ((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
+ */
+
+ Item_func_isnull *func=(Item_func_isnull*) cond;
+ Item **args= func->arguments();
+ if (args[0]->type() == Item::FIELD_ITEM)
+ {
+ Field *field=((Item_field*) args[0])->field;
+ if (field->flags & AUTO_INCREMENT_FLAG && !field->table->maybe_null &&
+ (current_thd->options & OPTION_AUTO_IS_NULL) &&
+ current_thd->insert_id())
+ {
+ COND *new_cond;
+ if ((new_cond= new Item_func_eq(args[0],
+ new Item_int("last_insert_id()",
+ current_thd->insert_id(),
+ 21))))
+ {
+ cond=new_cond;
+ cond->fix_fields(current_thd,0);
+ }
+ current_thd->insert_id(0); // Clear for next request
+ }
+ /* fix to replace 'NULL' dates with '0' (shreeve@uci.edu) */
+ else if (((field->type() == FIELD_TYPE_DATE) ||
+ (field->type() == FIELD_TYPE_DATETIME)) &&
+ (field->flags & NOT_NULL_FLAG))
+ {
+ COND *new_cond;
+ if ((new_cond= new Item_func_eq(args[0],new Item_int("0", 0, 2))))
+ {
+ cond=new_cond;
+ cond->fix_fields(current_thd,0);
+ }
+ }
+ }
+ }
+ else if (cond->const_item())
+ {
+ *cond_value= eval_const_cond(cond) ? Item::COND_TRUE : Item::COND_FALSE;
+ return (COND*) 0;
+ }
+ else if ((*cond_value= cond->eq_cmp_result()) != Item::COND_OK)
+ { // boolan compare function
+ Item *left_item= ((Item_func*) cond)->arguments()[0];
+ Item *right_item= ((Item_func*) cond)->arguments()[1];
+ if (left_item->eq(right_item))
+ {
+ if (!left_item->maybe_null ||
+ ((Item_func*) cond)->functype() == Item_func::EQUAL_FUNC)
+ return (COND*) 0; // Compare of identical items
+ }
+ }
+ *cond_value=Item::COND_OK;
+ return cond; /* Point at next and level */
+}
+
+/*
+** Return 1 if the item is a const value in all the WHERE clause
+*/
+
+static bool
+const_expression_in_where(COND *cond, Item *comp_item, Item **const_item)
+{
+ if (cond->type() == Item::COND_ITEM)
+ {
+ bool and_level= (((Item_cond*) cond)->functype()
+ == Item_func::COND_AND_FUNC);
+ List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
+ Item *item;
+ while ((item=li++))
+ {
+ bool res=const_expression_in_where(item, comp_item, const_item);
+ if (res) // Is a const value
+ {
+ if (and_level)
+ return 1;
+ }
+ else if (!and_level)
+ return 0;
+ }
+ return and_level ? 0 : 1;
+ }
+ else if (cond->eq_cmp_result() != Item::COND_OK)
+ { // boolan compare function
+ Item_func* func= (Item_func*) cond;
+ if (func->functype() != Item_func::EQUAL_FUNC &&
+ func->functype() != Item_func::EQ_FUNC)
+ return 0;
+ Item *left_item= ((Item_func*) cond)->arguments()[0];
+ Item *right_item= ((Item_func*) cond)->arguments()[1];
+ if (left_item->eq(comp_item))
+ {
+ if (right_item->const_item())
+ {
+ if (*const_item)
+ return right_item->eq(*const_item);
+ *const_item=right_item;
+ return 1;
+ }
+ }
+ else if (right_item->eq(comp_item))
+ {
+ if (left_item->const_item())
+ {
+ if (*const_item)
+ return left_item->eq(*const_item);
+ *const_item=left_item;
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+
+/****************************************************************************
+** 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
+****************************************************************************/
+
+Field *create_tmp_field(TABLE *table,Item *item, Item::Type type,
+ Item_result_field ***copy_func, Field **from_field,
+ bool group, bool modify_item)
+{
+ switch (type) {
+ case Item::SUM_FUNC_ITEM:
+ {
+ Item_sum *item_sum=(Item_sum*) item;
+ bool maybe_null=item_sum->maybe_null;
+ switch (item_sum->sum_func()) {
+ case Item_sum::AVG_FUNC: /* Place for sum & count */
+ if (group)
+ return new Field_string(sizeof(double)+sizeof(longlong),
+ maybe_null, item->name,table,1);
+ else
+ return new Field_double(item_sum->max_length,maybe_null,
+ item->name, table, item_sum->decimals);
+ case Item_sum::STD_FUNC: /* Place for sum & count */
+ if (group)
+ return new Field_string(sizeof(double)*2+sizeof(longlong),
+ maybe_null, item->name,table,1);
+ else
+ return new Field_double(item_sum->max_length, maybe_null,
+ item->name,table,item_sum->decimals);
+ case Item_sum::UNIQUE_USERS_FUNC:
+ return new Field_long(9,maybe_null,item->name,table,1);
+ default:
+ switch (item_sum->result_type()) {
+ case REAL_RESULT:
+ return new Field_double(item_sum->max_length,maybe_null,
+ item->name,table,item_sum->decimals);
+ case INT_RESULT:
+ return new Field_longlong(item_sum->max_length,maybe_null,
+ item->name,table);
+ case STRING_RESULT:
+ if (item_sum->max_length > 255)
+ return new Field_blob(item_sum->max_length,maybe_null,
+ item->name,table,item->binary);
+ return new Field_string(item_sum->max_length,maybe_null,
+ item->name,table,item->binary);
+ }
+ }
+ current_thd->fatal_error=1;
+ return 0; // Error
+ }
+ case Item::FIELD_ITEM:
+ {
+ 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
+ {
+ if (modify_item)
+ ((Item_field*) item)->result_field= new_field;
+ else
+ new_field->field_name=item->name;
+ if (org_field->maybe_null())
+ new_field->flags&= ~NOT_NULL_FLAG; // Because of outer join
+ }
+ 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::INT_ITEM:
+ case Item::REAL_ITEM:
+ case Item::STRING_ITEM:
+ case Item::REF_ITEM:
+ {
+ bool maybe_null=item->maybe_null;
+ Field *new_field;
+ LINT_INIT(new_field);
+
+ switch (item->result_type()) {
+ case REAL_RESULT:
+ new_field=new Field_double(item->max_length,maybe_null,
+ item->name,table,item->decimals);
+ break;
+ case INT_RESULT:
+ new_field=new Field_longlong(item->max_length,maybe_null,
+ item->name,table);
+ break;
+ case STRING_RESULT:
+ if (item->max_length > 255)
+ new_field= new Field_blob(item->max_length,maybe_null,
+ item->name,table,item->binary);
+ else
+ new_field= new Field_string(item->max_length,maybe_null,
+ item->name,table,item->binary);
+ break;
+ }
+ if (copy_func)
+ *((*copy_func)++) = (Item_result_field*) item; // Save for copy_funcs
+ if (modify_item)
+ ((Item_result_field*) item)->result_field=new_field;
+ return new_field;
+ }
+ default: // Dosen't have to be stored
+ return 0;
+ }
+}
+
+
+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)
+{
+ TABLE *table;
+ uint i,field_count,reclength,null_count,null_pack_length,
+ blob_count,group_null_items;
+ bool using_unique_constraint=0;
+ char *tmpname,path[FN_REFLEN];
+ byte *pos,*group_buff;
+ uchar *null_flags;
+ Field **reg_field,**from_field;
+ Copy_field *copy=0;
+ KEY *keyinfo;
+ KEY_PART_INFO *key_part_info;
+ Item_result_field **copy_func;
+ MI_COLUMNDEF *recinfo;
+ DBUG_ENTER("create_tmp_table");
+ DBUG_PRINT("enter",("distinct: %d save_sum_fields: %d allow_distinct_limit: %d group: %d",
+ (int) distinct, (int) save_sum_fields,
+ (int) allow_distinct_limit,test(group)));
+
+ created_tmp_tables++; // Global, but should be safe
+ sprintf(path,"%s%s%lx_%lx_%x",mysql_tmpdir,tmp_file_prefix,current_pid,
+ thd->thread_id, thd->tmp_table++);
+ if (group)
+ {
+ if (!param->quick_group)
+ group=0; // Can't use group key
+ else for (ORDER *tmp=group ; tmp ; tmp=tmp->next)
+ (*tmp->item)->marker=4; // Store null in key
+ if (param->group_length >= MAX_BLOB_WIDTH)
+ using_unique_constraint=1;
+ }
+
+ field_count=param->field_count+param->func_count+param->sum_func_count;
+ if (!my_multi_malloc(MYF(MY_WME),
+ &table,sizeof(*table),
+ &reg_field,sizeof(Field*)*(field_count+1),
+ &from_field,sizeof(Field*)*field_count,
+ &copy_func,sizeof(*copy_func)*(param->func_count+1),
+ &param->keyinfo,sizeof(*param->keyinfo),
+ &key_part_info,
+ sizeof(*key_part_info)*(param->group_parts+1),
+ &param->start_recinfo,
+ sizeof(*param->recinfo)*(field_count*2+4),
+ &tmpname,(uint) strlen(path)+1,
+ &group_buff,group && ! using_unique_constraint ?
+ param->group_length : 0,
+ NullS))
+ {
+ DBUG_RETURN(NULL); /* purecov: inspected */
+ }
+ if (!(param->copy_field=copy=new Copy_field[field_count]))
+ {
+ my_free((gptr) table,MYF(0)); /* purecov: inspected */
+ DBUG_RETURN(NULL); /* purecov: inspected */
+ }
+ param->funcs=copy_func;
+ strmov(tmpname,path);
+ /* make table according to fields */
+
+ bzero((char*) table,sizeof(*table));
+ bzero((char*) reg_field,sizeof(Field*)*(field_count+1));
+ bzero((char*) from_field,sizeof(Field*)*field_count);
+ table->field=reg_field;
+ table->real_name=table->path=tmpname;
+ table->table_name=base_name(tmpname);
+ table->reginfo.lock_type=TL_WRITE; /* Will be updated */
+ table->db_stat=HA_OPEN_KEYFILE+HA_OPEN_RNDFILE;
+ table->blob_ptr_size=mi_portable_sizeof_char_ptr;
+ table->map=1;
+ table->tmp_table=1;
+ table->db_low_byte_first=1; // True for HEAP and MyISAM
+
+ /* Calculate with type of fields we will need in heap table */
+
+ reclength=blob_count=null_count=group_null_items=0;
+
+ List_iterator<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 ||
+ item->const_item())
+ continue;
+
+ if (type == Item::SUM_FUNC_ITEM && !group && !save_sum_fields)
+ { /* Can't calc group yet */
+ ((Item_sum*) item)->result_field=0;
+ for (i=0 ; i < ((Item_sum*) item)->arg_count ; i++)
+ {
+ Item *arg= ((Item_sum*) item)->args[i];
+ if (!arg->const_item())
+ {
+ Field *new_field=
+ create_tmp_field(table,arg,arg->type(),&copy_func,tmp_from_field,
+ group != 0,1);
+ if (!new_field)
+ goto err; // Should be OOM
+ tmp_from_field++;
+ *(reg_field++)= new_field;
+ reclength+=new_field->pack_length();
+ if (!(new_field->flags & NOT_NULL_FLAG))
+ null_count++;
+ if (new_field->flags & BLOB_FLAG)
+ blob_count++;
+ ((Item_sum*) item)->args[i]= new Item_field(new_field);
+ }
+ }
+ }
+ else
+ {
+ Field *new_field=create_tmp_field(table,item,type,&copy_func,
+ tmp_from_field, group != 0,1);
+ if (!new_field)
+ {
+ if (thd->fatal_error)
+ goto err; // Got OOM
+ continue; // Some kindf of const item
+ }
+ if (type == Item::SUM_FUNC_ITEM)
+ ((Item_sum *) item)->result_field= new_field;
+ tmp_from_field++;
+ reclength+=new_field->pack_length();
+ if (!(new_field->flags & NOT_NULL_FLAG))
+ null_count++;
+ if (new_field->flags & BLOB_FLAG)
+ blob_count++;
+ if (item->marker == 4 && item->maybe_null)
+ {
+ group_null_items++;
+ new_field->flags|= GROUP_FLAG;
+ }
+ *(reg_field++) =new_field;
+ }
+ }
+ field_count=reg_field - table->field;
+
+ /* If result table is small; use a heap */
+ if (blob_count || using_unique_constraint ||
+ (select_options & (OPTION_BIG_TABLES | SELECT_SMALL_RESULT)) ==
+ OPTION_BIG_TABLES)
+ {
+ table->file=get_new_handler(table,table->db_type=DB_TYPE_MYISAM);
+ if (group &&
+ (param->group_parts > table->file->max_key_parts() ||
+ param->group_length > table->file->max_key_length()))
+ using_unique_constraint=1;
+ }
+ else
+ {
+ table->file=get_new_handler(table,table->db_type=DB_TYPE_HEAP);
+ }
+
+ if (!using_unique_constraint)
+ reclength+= group_null_items; // null flag is stored separately
+
+ table->blob_fields=blob_count;
+ if (blob_count == 0)
+ null_count++; // For delete link
+ reclength+=(null_pack_length=(null_count+7)/8);
+ if (!reclength)
+ reclength=1; // Dummy select
+
+ table->fields=field_count;
+ table->reclength=reclength;
+ {
+ uint alloc_length=ALIGN_SIZE(reclength+MI_UNIQUE_HASH_LENGTH+1);
+ table->rec_buff_length=alloc_length;
+ if (!(table->record[0]= (byte *) my_malloc(alloc_length*3, MYF(MY_WME))))
+ goto err;
+ table->record[1]= table->record[0]+alloc_length;
+ table->record[2]= table->record[1]+alloc_length;
+ }
+ copy_func[0]=0; // End marker
+
+ recinfo=param->start_recinfo;
+ null_flags=(uchar*) table->record[0];
+ pos=table->record[0]+ null_pack_length;
+ if (null_pack_length)
+ {
+ bzero((byte*) recinfo,sizeof(*recinfo));
+ recinfo->type=FIELD_NORMAL;
+ recinfo->length=null_pack_length;
+ recinfo++;
+ bfill(null_flags,null_pack_length,255); // Set null fields
+ }
+ null_count= (blob_count == 0) ? 1 : 0;
+ for (i=0,reg_field=table->field; i < field_count; i++,reg_field++,recinfo++)
+ {
+ Field *field= *reg_field;
+ uint length;
+ bzero((byte*) recinfo,sizeof(*recinfo));
+
+ if (!(field->flags & NOT_NULL_FLAG))
+ {
+ if (field->flags & GROUP_FLAG && !using_unique_constraint)
+ {
+ *pos++=0; // Null is stored here
+ recinfo->length=1;
+ recinfo->type=FIELD_NORMAL;
+ recinfo++;
+ bzero((byte*) recinfo,sizeof(*recinfo));
+ }
+ else
+ {
+ recinfo->null_bit= 1 << (null_count & 7);
+ recinfo->null_pos= null_count/8;
+ }
+ field->move_field((char*) pos,null_flags+null_count/8,
+ 1 << (null_count & 7));
+ null_count++;
+ }
+ else
+ field->move_field((char*) pos,(uchar*) 0,0);
+ field->reset();
+ if (from_field[i])
+ { /* Not a table Item */
+ copy->set(field,from_field[i],save_sum_fields);
+ copy++;
+ }
+ length=field->pack_length();
+ pos+= length;
+
+ /* Make entry for create table */
+ recinfo->length=length;
+ if (field->flags & BLOB_FLAG)
+ recinfo->type= (int) FIELD_BLOB;
+ else if (!field->zero_pack() &&
+ (field->type() == FIELD_TYPE_STRING ||
+ field->type() == FIELD_TYPE_VAR_STRING) &&
+ length >= 10 && blob_count)
+ recinfo->type=FIELD_SKIPP_ENDSPACE;
+ else
+ recinfo->type=FIELD_NORMAL;
+ }
+
+ param->copy_field_count=(uint) (copy - param->copy_field);
+ param->recinfo=recinfo;
+ store_record(table,2); // Make empty default record
+
+ table->max_rows=(((table->db_type == DB_TYPE_HEAP) ?
+ min(tmp_table_size, max_heap_table_size) : tmp_table_size)/
+ table->reclength);
+ set_if_bigger(table->max_rows,1); // For dummy start options
+ keyinfo=param->keyinfo;
+
+ if (group)
+ {
+ DBUG_PRINT("info",("Creating group key in temporary table"));
+ table->group=group; /* Table is grouped by key */
+ param->group_buff=group_buff;
+ table->keys=1;
+ table->uniques= test(using_unique_constraint);
+ table->key_info=keyinfo;
+ keyinfo->key_part=key_part_info;
+ keyinfo->flags=HA_NOSAME;
+ keyinfo->usable_key_parts=keyinfo->key_parts= param->group_parts;
+ keyinfo->key_length=0;
+ keyinfo->rec_per_key=0;
+ for (; group ; group=group->next,key_part_info++)
+ {
+ Field *field=(*group->item)->tmp_table_field();
+ bool maybe_null=(*group->item)->maybe_null;
+ key_part_info->field= field;
+ key_part_info->offset= field->offset();
+ key_part_info->length= (uint16) field->pack_length();
+ key_part_info->type= (uint8) field->key_type();
+ key_part_info->key_type =
+ ((ha_base_keytype) key_part_info->type == HA_KEYTYPE_TEXT ||
+ (ha_base_keytype) key_part_info->type == HA_KEYTYPE_VARTEXT) ?
+ 0 : FIELDFLAG_BINARY;
+ if (!using_unique_constraint)
+ {
+ group->buff=(char*) group_buff;
+ if (!(group->field=field->new_field(table)))
+ goto err; /* purecov: inspected */
+ if (maybe_null)
+ {
+ /*
+ To be able to group on NULL, we move the null bit to be
+ just before the column and extend the key to cover the null bit
+ */
+ *group_buff= 0; // Init null byte
+ key_part_info->offset--;
+ key_part_info->length++;
+ group->field->move_field((char*) group_buff+1, (uchar*) group_buff,
+ 1);
+ }
+ else
+ group->field->move_field((char*) group_buff);
+ group_buff+= key_part_info->length;
+ }
+ keyinfo->key_length+= key_part_info->length;
+ }
+ }
+
+ if (distinct && !group)
+ {
+ /* Create an unique key or an unique constraint over all columns */
+ keyinfo->key_parts=field_count+ test(null_count);
+ if (distinct && allow_distinct_limit)
+ {
+ set_if_smaller(table->max_rows,thd->select_limit);
+ param->end_write_records=thd->select_limit;
+ }
+ else
+ param->end_write_records= HA_POS_ERROR;
+ table->distinct=1;
+ table->keys=1;
+ if (blob_count)
+ {
+ using_unique_constraint=1;
+ table->uniques=1;
+ }
+ if (!(key_part_info= (KEY_PART_INFO*)
+ sql_calloc((keyinfo->key_parts)*sizeof(KEY_PART_INFO))))
+ goto err;
+ table->key_info=keyinfo;
+ keyinfo->key_part=key_part_info;
+ keyinfo->flags=HA_NOSAME;
+ keyinfo->key_length=(uint16) reclength;
+ keyinfo->name=(char*) "tmp";
+ if (null_count)
+ {
+ key_part_info->offset=0;
+ key_part_info->length=(null_count+7)/8;
+ key_part_info->field=new Field_string((char*) table->record[0],
+ (uint32) key_part_info->length,
+ (uchar*) 0,
+ (uint) 0,
+ Field::NONE,
+ NullS, table, (bool) 1);
+ key_part_info->key_type=FIELDFLAG_BINARY;
+ key_part_info->type= HA_KEYTYPE_BINARY;
+ key_part_info++;
+ }
+ for (i=0,reg_field=table->field; i < field_count;
+ i++, reg_field++, key_part_info++)
+ {
+ key_part_info->field= *reg_field;
+ key_part_info->offset= (*reg_field)->offset();
+ key_part_info->length= (uint16) (*reg_field)->pack_length();
+ key_part_info->type= (uint8) (*reg_field)->key_type();
+ key_part_info->key_type =
+ ((ha_base_keytype) key_part_info->type == HA_KEYTYPE_TEXT ||
+ (ha_base_keytype) key_part_info->type == HA_KEYTYPE_VARTEXT) ?
+ 0 : FIELDFLAG_BINARY;
+ }
+ }
+ if (thd->fatal_error) // If end of memory
+ goto err; /* purecov: inspected */
+ table->db_record_offset=1;
+ if (table->db_type == DB_TYPE_MYISAM)
+ {
+ if (create_myisam_tmp_table(table,param,select_options))
+ goto err;
+ }
+ if (!open_tmp_table(table))
+ DBUG_RETURN(table);
+
+ err:
+ free_tmp_table(thd,table); /* purecov: inspected */
+ DBUG_RETURN(NULL); /* purecov: inspected */
+}
+
+
+static bool open_tmp_table(TABLE *table)
+{
+ int error;
+ if ((error=table->file->ha_open(table->real_name,O_RDWR,HA_OPEN_TMP_TABLE)))
+ {
+ table->file->print_error(error,MYF(0)); /* purecov: inspected */
+ 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)
+{
+ int error;
+ MI_KEYDEF keydef;
+ MI_UNIQUEDEF uniquedef;
+ KEY *keyinfo=param->keyinfo;
+
+ DBUG_ENTER("create_myisam_tmp_table");
+ if (table->keys)
+ { // Get keys for ni_create
+ bool using_unique_constraint=0;
+ MI_KEYSEG *seg= (MI_KEYSEG*) sql_calloc(sizeof(*seg) *
+ keyinfo->key_parts);
+ if (!seg)
+ goto err;
+
+ if (keyinfo->key_length >= table->file->max_key_length() ||
+ keyinfo->key_parts > table->file->max_key_parts() ||
+ table->uniques)
+ {
+ /* Can't create a key; Make a unique constraint instead of a key */
+ table->keys=0;
+ table->uniques=1;
+ using_unique_constraint=1;
+ bzero((char*) &uniquedef,sizeof(uniquedef));
+ uniquedef.keysegs=keyinfo->key_parts;
+ uniquedef.seg=seg;
+ uniquedef.null_are_equal=1;
+
+ /* Create extra column for hash value */
+ bzero((byte*) param->recinfo,sizeof(*param->recinfo));
+ param->recinfo->type= FIELD_CHECK;
+ param->recinfo->length=MI_UNIQUE_HASH_LENGTH;
+ param->recinfo++;
+ table->reclength+=MI_UNIQUE_HASH_LENGTH;
+ }
+ else
+ {
+ /* Create an unique key */
+ bzero((char*) &keydef,sizeof(keydef));
+ keydef.flag=HA_NOSAME | HA_BINARY_PACK_KEY | HA_PACK_KEY;
+ keydef.keysegs= keyinfo->key_parts;
+ keydef.seg= seg;
+ }
+ 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;
+ if (field->flags & BLOB_FLAG)
+ {
+ seg->type=
+ ((keyinfo->key_part[i].key_type & FIELDFLAG_BINARY) ?
+ HA_KEYTYPE_VARBINARY : HA_KEYTYPE_VARTEXT);
+ seg->bit_start=seg->length - table->blob_ptr_size;
+ seg->flag= HA_BLOB_PART;
+ seg->length=0; // Whole blob in unique constraint
+ }
+ else
+ {
+ seg->type=
+ ((keyinfo->key_part[i].key_type & FIELDFLAG_BINARY) ?
+ HA_KEYTYPE_BINARY : HA_KEYTYPE_TEXT);
+ if (!(field->flags & ZEROFILL_FLAG) &&
+ (field->type() == FIELD_TYPE_STRING ||
+ field->type() == FIELD_TYPE_VAR_STRING) &&
+ keyinfo->key_part[i].length > 4)
+ seg->flag|=HA_SPACE_PACK;
+ }
+ if (using_unique_constraint &&
+ !(field->flags & NOT_NULL_FLAG))
+ {
+ seg->null_bit= field->null_bit;
+ seg->null_pos= (uint) (field->null_ptr - (uchar*) table->record[0]);
+ }
+ }
+ }
+ MI_CREATE_INFO create_info;
+ bzero((char*) &create_info,sizeof(create_info));
+ if (options & (OPTION_BIG_TABLES | SELECT_SMALL_RESULT) == OPTION_BIG_TABLES)
+ create_info.data_file_length= ~(ulonglong) 0;
+
+ if ((error=mi_create(table->real_name,table->keys,&keydef,
+ (uint) (param->recinfo-param->start_recinfo),
+ param->start_recinfo,
+ table->uniques, &uniquedef,
+ &create_info,
+ HA_CREATE_TMP_TABLE)))
+ {
+ table->file->print_error(error,MYF(0)); /* purecov: inspected */
+ table->db_stat=0;
+ goto err;
+ }
+ table->db_record_offset=1;
+ DBUG_RETURN(0);
+ err:
+ DBUG_RETURN(1);
+}
+
+
+void
+free_tmp_table(THD *thd, TABLE *entry)
+{
+ const char *save_proc_info;
+ DBUG_ENTER("free_tmp_table");
+ DBUG_PRINT("enter",("table: %s",entry->table_name));
+
+ save_proc_info=thd->proc_info;
+ thd->proc_info="removing tmp table";
+ if (entry->db_stat && entry->file)
+ {
+ (void) entry->file->close();
+ delete entry->file;
+ }
+ if (!(test_flags & TEST_KEEP_TMP_TABLES) || entry->db_type == DB_TYPE_HEAP)
+ (void) ha_delete_table(entry->db_type,entry->real_name);
+ /* free blobs */
+ for (Field **ptr=entry->field ; *ptr ; ptr++)
+ delete *ptr;
+ my_free((gptr) entry->record[0],MYF(0));
+ free_io_cache(entry);
+ my_free((gptr) entry,MYF(0));
+ thd->proc_info=save_proc_info;
+
+ DBUG_VOID_RETURN;
+}
+
+/*
+* If a HEAP table gets full, create a MyISAM table and copy all rows to this
+*/
+
+bool create_myisam_from_heap(TABLE *table, TMP_TABLE_PARAM *param, int error,
+ bool ignore_last_dupp_key_error)
+{
+ TABLE new_table;
+ const char *save_proc_info;
+ THD *thd=current_thd;
+ int write_err;
+ DBUG_ENTER("create_myisam_from_heap");
+
+ if (table->db_type != DB_TYPE_HEAP || error != HA_ERR_RECORD_FILE_FULL)
+ {
+ table->file->print_error(error,MYF(0));
+ DBUG_RETURN(1);
+ }
+ new_table= *table;
+ new_table.db_type=DB_TYPE_MYISAM;
+ if (!(new_table.file=get_new_handler(&new_table,DB_TYPE_MYISAM)))
+ DBUG_RETURN(1); // End of memory
+
+ save_proc_info=thd->proc_info;
+ thd->proc_info="converting HEAP to MyISAM";
+
+ if (create_myisam_tmp_table(&new_table,param,
+ thd->lex.options | thd->options))
+ goto err2;
+ if (open_tmp_table(&new_table))
+ goto err1;
+ table->file->index_end();
+ table->file->rnd_init();
+ /* copy all old rows */
+ while (!table->file->rnd_next(new_table.record[1]))
+ {
+ if ((write_err=new_table.file->write_row(new_table.record[1])))
+ goto err;
+ }
+ /* copy row that filled HEAP table */
+ if ((write_err=new_table.file->write_row(table->record[0])))
+ {
+ if (write_err != HA_ERR_FOUND_DUPP_KEY &&
+ write_err != HA_ERR_FOUND_DUPP_UNIQUE || !ignore_last_dupp_key_error)
+ goto err;
+ }
+
+ /* remove heap table and change to use myisam table */
+ (void) table->file->rnd_end();
+ (void) table->file->close();
+ (void) table->file->delete_table(table->real_name);
+ delete table->file;
+ table->file=0;
+ *table =new_table;
+ table->file->change_table_ptr(table);
+
+ thd->proc_info=save_proc_info;
+ DBUG_RETURN(0);
+
+ err:
+ DBUG_PRINT("error",("Got error: %d",write_err));
+ table->file->print_error(error,MYF(0)); // Give table is full error
+ (void) table->file->rnd_end();
+ (void) new_table.file->close();
+ err1:
+ new_table.file->delete_table(new_table.real_name);
+ delete new_table.file;
+ err2:
+ thd->proc_info=save_proc_info;
+ DBUG_RETURN(1);
+}
+
+
+/*****************************************************************************
+** Make a join of all tables and write it on socket or to table
+*****************************************************************************/
+
+static int
+do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
+{
+ int error;
+ JOIN_TAB *join_tab;
+ int (*end_select)(JOIN *, struct st_join_table *,bool);
+ DBUG_ENTER("do_select");
+
+ join->procedure=procedure;
+ /*
+ ** Tell the client how many fields there are in a row
+ */
+ if (!table)
+ join->result->send_fields(*fields,1);
+ else
+ {
+ VOID(table->file->extra(HA_EXTRA_WRITE_CACHE));
+ empty_record(table);
+ }
+ join->tmp_table=table; /* Save for easy recursion */
+ join->fields= fields;
+
+ /* Set up select_end */
+ if (table)
+ {
+ if (table->group && join->tmp_table_param.sum_func_count)
+ {
+ DBUG_PRINT("info",("Using end_update"));
+ if (table->keys)
+ {
+ end_select=end_update;
+ table->file->index_init(0);
+ }
+ else
+ end_select=end_unique_update;
+ }
+ else if (join->sort_and_group)
+ {
+ DBUG_PRINT("info",("Using end_write_group"));
+ end_select=end_write_group;
+ }
+ else
+ {
+ DBUG_PRINT("info",("Using end_write"));
+ end_select=end_write;
+ }
+ }
+ else
+ {
+ if (join->sort_and_group || (join->procedure &&
+ join->procedure->flags & PROC_GROUP))
+ end_select=end_send_group;
+ else
+ end_select=end_send;
+ }
+ join->join_tab[join->tables-1].next_select=end_select;
+
+ join_tab=join->join_tab+join->const_tables;
+ join->send_records=0;
+ if (join->tables == join->const_tables)
+ {
+ if (!(error=(*end_select)(join,join_tab,0)) || error == -3)
+ error=(*end_select)(join,join_tab,1);
+ }
+ else
+ {
+ error=sub_select(join,join_tab,0);
+ if (error >= 0)
+ error=sub_select(join,join_tab,1);
+ if (error == -3)
+ error=0; /* select_limit used */
+ }
+ if (!table)
+ {
+ if (error < 0)
+ join->result->send_error(0,NullS); /* purecov: inspected */
+ else if (join->result->send_eof())
+ error= -1;
+ }
+ 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;
+ if ((tmp=table->file->extra(HA_EXTRA_NO_CACHE)))
+ {
+ my_errno=tmp;
+ error= -1;
+ }
+ if (table->file->index_end())
+ {
+ my_errno=tmp;
+ error= -1;
+ }
+ if (error != old_error)
+ table->file->print_error(my_errno,MYF(0));
+ }
+ DBUG_RETURN(error < 0);
+}
+
+
+static int
+sub_select_cache(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
+{
+ int error;
+
+ if (end_of_records)
+ {
+ if ((error=flush_cached_records(join,join_tab,FALSE)) < 0)
+ return error; /* purecov: inspected */
+ return sub_select(join,join_tab,end_of_records);
+ }
+ if (join_tab->use_quick != 2 || test_if_quick_select(join_tab) <= 0)
+ {
+ if (join->thd->killed)
+ {
+ my_error(ER_SERVER_SHUTDOWN,MYF(0)); /* purecov: inspected */
+ return -2; // Aborted by user /* purecov: inspected */
+ }
+ if (!store_record_in_cache(&join_tab->cache))
+ return 0; // There is more room in cache
+ return flush_cached_records(join,join_tab,FALSE);
+ }
+ if ((error=flush_cached_records(join,join_tab,TRUE)) < 0)
+ return error; /* purecov: inspected */
+ return sub_select(join,join_tab,end_of_records); /* Use ordinary select */
+}
+
+
+static int
+sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
+{
+
+ join_tab->table->null_row=0;
+ if (end_of_records)
+ return (*join_tab->next_select)(join,join_tab+1,end_of_records);
+
+ /* Cache variables for faster loop */
+ int error;
+ bool found=0;
+ COND *on_expr=join_tab->on_expr, *select_cond=join_tab->select_cond;
+ int (*next_select)(JOIN *,struct st_join_table *,bool)=
+ join_tab->next_select;
+
+ if (!(error=(*join_tab->read_first_record)(join_tab)))
+ {
+ bool not_exists_optimize=join_tab->table->reginfo.not_exists_optimize;
+ READ_RECORD *info= &join_tab->read_record;
+
+ do
+ {
+ if (join->thd->killed) // Aborted by user
+ {
+ my_error(ER_SERVER_SHUTDOWN,MYF(0)); /* purecov: inspected */
+ return -2; /* purecov: inspected */
+ }
+ if (!on_expr || on_expr->val_int())
+ {
+ found=1;
+ if (not_exists_optimize)
+ break; // Searching after not null columns
+ if (!select_cond || select_cond->val_int())
+ {
+ if ((error=(*next_select)(join,join_tab+1,0)) < 0)
+ return error;
+ }
+ }
+ } while (!(error=info->read_record(info)));
+ if (error > 0) // Fatal error
+ return -1;
+ }
+ else if (error > 0)
+ return -1;
+
+ if (!found && on_expr)
+ { // OUTER JOIN
+ restore_record(join_tab->table,2); // Make empty record
+ mark_as_null_row(join_tab->table); // For group by without error
+ if (!select_cond || select_cond->val_int())
+ {
+ if ((error=(*next_select)(join,join_tab+1,0)) < 0)
+ return error; /* purecov: inspected */
+ }
+ }
+ return 0;
+}
+
+
+static int
+flush_cached_records(JOIN *join,JOIN_TAB *join_tab,bool skipp_last)
+{
+ int error;
+ READ_RECORD *info;
+
+ if (!join_tab->cache.records)
+ return 0; /* Nothing to do */
+ if (skipp_last)
+ (void) store_record_in_cache(&join_tab->cache); // Must save this for later
+ if (join_tab->use_quick == 2)
+ {
+ if (join_tab->select->quick)
+ { /* Used quick select last. reset it */
+ delete join_tab->select->quick;
+ join_tab->select->quick=0;
+ }
+ }
+ /* read through all records */
+ if ((error=join_init_read_record(join_tab)))
+ {
+ reset_cache(&join_tab->cache);
+ join_tab->cache.records=0; join_tab->cache.ptr_record= (uint) ~0;
+ return -error; /* No records or error */
+ }
+
+ for (JOIN_TAB *tmp=join->join_tab; tmp != join_tab ; tmp++)
+ {
+ tmp->status=tmp->table->status;
+ tmp->table->status=0;
+ }
+
+ info= &join_tab->read_record;
+ do
+ {
+ if (join->thd->killed)
+ {
+ my_error(ER_SERVER_SHUTDOWN,MYF(0)); /* purecov: inspected */
+ return -2; // Aborted by user /* purecov: inspected */
+ }
+ SQL_SELECT *select=join_tab->select;
+ if (!error && (!join_tab->cache.select ||
+ !join_tab->cache.select->skipp_record()))
+ {
+ uint i;
+ reset_cache(&join_tab->cache);
+ for (i=(join_tab->cache.records- (skipp_last ? 1 : 0)) ; i-- > 0 ;)
+ {
+ read_cached_record(join_tab);
+ if (!select || !select->skipp_record())
+ if ((error=(join_tab->next_select)(join,join_tab+1,0)) < 0)
+ return error; /* purecov: inspected */
+ }
+ }
+ } while (!(error=info->read_record(info)));
+
+ if (skipp_last)
+ read_cached_record(join_tab); // Restore current record
+ reset_cache(&join_tab->cache);
+ join_tab->cache.records=0; join_tab->cache.ptr_record= (uint) ~0;
+ if (error > 0) // Fatal error
+ return -1; /* purecov: inspected */
+ for (JOIN_TAB *tmp2=join->join_tab; tmp2 != join_tab ; tmp2++)
+ tmp2->table->status=tmp2->status;
+ return 0;
+}
+
+
+/*****************************************************************************
+** 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)
+{
+ 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);
+ }
+ }
+ if (join->join_tab[i].on_expr && !form->null_row)
+ {
+ if ((form->null_row= test(join->join_tab[i].on_expr->val_int() == 0)))
+ empty_record(form);
+ }
+ if (!form->null_row)
+ form->maybe_null=0;
+ }
+ DBUG_RETURN(0);
+}
+
+
+static int
+join_read_system(JOIN_TAB *tab)
+{
+ TABLE *table= tab->table;
+ int error;
+ if (table->status & STATUS_GARBAGE) // If first read
+ {
+ if ((error=table->file->rnd_first(table->record[0])))
+ {
+ if (error != HA_ERR_END_OF_FILE)
+ {
+ table->file->print_error(error,MYF(0));
+ return 1;
+ }
+ table->null_row=1; // This is ok.
+ empty_record(table); // Make empty record
+ return -1;
+ }
+ store_record(table,1);
+ }
+ else if (!table->status) // Only happens with left join
+ restore_record(table,1); // restore old record
+ table->null_row=0;
+ return table->status ? -1 : 0;
+}
+
+
+static int
+join_read_const(JOIN_TAB *tab)
+{
+ int error;
+ TABLE *table= tab->table;
+ if (table->status & STATUS_GARBAGE) // If first read
+ {
+ if (cp_buffer_from_ref(&tab->ref))
+ error=HA_ERR_KEY_NOT_FOUND;
+ else
+ {
+ error=table->file->index_read_idx(table->record[0],tab->ref.key,
+ (byte*) tab->ref.key_buff,
+ tab->ref.key_length,HA_READ_KEY_EXACT);
+ }
+ if (error)
+ {
+ table->null_row=1;
+ empty_record(table);
+ if (error != HA_ERR_KEY_NOT_FOUND)
+ {
+ sql_print_error("read_const: Got error %d when reading table %s",
+ error, table->real_name);
+ table->file->print_error(error,MYF(0));
+ return 1;
+ }
+ return -1;
+ }
+ store_record(table,1);
+ }
+ else if (!table->status) // Only happens with left join
+ restore_record(table,1); // restore old record
+ table->null_row=0;
+ return table->status ? -1 : 0;
+}
+
+
+static int
+join_read_key(JOIN_TAB *tab)
+{
+ int error;
+ TABLE *table= tab->table;
+
+ if (cmp_buffer_with_ref(tab) ||
+ (table->status & (STATUS_GARBAGE | STATUS_NO_PARENT)))
+ {
+ if (tab->ref.key_err)
+ {
+ table->status=STATUS_NOT_FOUND;
+ return -1;
+ }
+ error=table->file->index_read(table->record[0],
+ tab->ref.key_buff,
+ tab->ref.key_length,HA_READ_KEY_EXACT);
+ if (error && error != HA_ERR_KEY_NOT_FOUND)
+ {
+ sql_print_error("read_key: Got error %d when reading table '%s'",error,
+ table->real_name);
+ table->file->print_error(error,MYF(0));
+ return 1;
+ }
+ }
+ return table->status ? -1 : 0;
+}
+
+
+static int
+join_read_always_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(table->record[0],
+ tab->ref.key_buff,
+ tab->ref.key_length,HA_READ_KEY_EXACT)))
+ {
+ if (error != HA_ERR_KEY_NOT_FOUND)
+ {
+ sql_print_error("read_const: Got error %d when reading table %s",error,
+ table->real_name);
+ table->file->print_error(error,MYF(0));
+ return 1;
+ }
+ return -1; /* purecov: inspected */
+ }
+ return 0;
+}
+
+
+ /* ARGSUSED */
+static int
+join_no_more_records(READ_RECORD *info __attribute__((unused)))
+{
+ return -1;
+}
+
+
+static int
+join_read_next(READ_RECORD *info)
+{
+ int error;
+ TABLE *table= info->table;
+ JOIN_TAB *tab=table->reginfo.join_tab;
+
+ if ((error=table->file->index_next_same(table->record[0],
+ tab->ref.key_buff,
+ tab->ref.key_length)))
+ {
+ if (error != HA_ERR_END_OF_FILE)
+ {
+ sql_print_error("read_next: Got error %d when reading table %s",error,
+ table->real_name);
+ table->file->print_error(error,MYF(0));
+ return 1;
+ }
+ table->status= STATUS_GARBAGE;
+ return -1;
+ }
+ return 0;
+}
+
+
+static int
+join_init_quick_read_record(JOIN_TAB *tab)
+{
+ if (test_if_quick_select(tab) == -1)
+ return -1; /* No possible records */
+ return join_init_read_record(tab);
+}
+
+
+static int
+test_if_quick_select(JOIN_TAB *tab)
+{
+ delete tab->select->quick;
+ tab->select->quick=0;
+ return tab->select->test_quick_select(tab->keys,(table_map) 0,HA_POS_ERROR);
+}
+
+
+static int
+join_init_read_record(JOIN_TAB *tab)
+{
+ if (tab->select && tab->select->quick)
+ tab->select->quick->reset();
+ init_read_record(&tab->read_record,current_thd, tab->table, tab->select,1,1);
+ return (*tab->read_record.read_record)(&tab->read_record);
+}
+
+static int
+join_init_read_first_with_key(JOIN_TAB *tab)
+{
+ int error;
+ TABLE *table=tab->table;
+ if (!table->key_read && (table->used_keys & ((key_map) 1 << tab->index)))
+ {
+ 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.table=table;
+ tab->read_record.file=table->file;
+ tab->read_record.index=tab->index;
+ tab->read_record.record=table->record[0];
+ tab->table->file->index_init(tab->index);
+ error=tab->table->file->index_first(tab->table->record[0]);
+ if (error)
+ {
+ 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",error);
+ table->file->print_error(error,MYF(0));
+ return 1;
+ }
+ return -1;
+ }
+ return 0;
+}
+
+static int
+join_init_read_next_with_key(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",
+ error, info->table->real_name);
+ info->file->print_error(error,MYF(0));
+ return 1;
+ }
+ return -1;
+ }
+ return 0;
+}
+
+static int
+join_init_read_last_with_key(JOIN_TAB *tab)
+{
+ TABLE *table=tab->table;
+ int error;
+ if (!table->key_read && (table->used_keys & ((key_map) 1 << tab->index)))
+ {
+ 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.table=table;
+ tab->read_record.file=table->file;
+ tab->read_record.index=tab->index;
+ tab->read_record.record=table->record[0];
+ tab->table->file->index_init(tab->index);
+ error=tab->table->file->index_last(tab->table->record[0]);
+ if (error)
+ {
+ if (error != HA_ERR_END_OF_FILE)
+ {
+ sql_print_error("read_first_with_key: Got error %d when reading table",
+ error, table->real_name);
+ table->file->print_error(error,MYF(0));
+ return 1;
+ }
+ return -1;
+ }
+ return 0;
+}
+
+static int
+join_init_read_prev_with_key(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",
+ error,info->table->real_name);
+ info->file->print_error(error,MYF(0));
+ return 1;
+ }
+ return -1;
+ }
+ return 0;
+}
+
+static int
+join_ft_read_first(JOIN_TAB *tab)
+{
+ int error;
+ TABLE *table= tab->table;
+
+#if 0
+ if (cp_buffer_from_ref(&tab->ref)) // as ft-key doesn't use store_key's
+ return -1;
+#endif
+ if ((error=table->file->ft_init(tab->ref.key,
+ tab->ref.key_buff,
+ tab->ref.key_length,TRUE)))
+ {
+ if (error != HA_ERR_KEY_NOT_FOUND)
+ {
+ sql_print_error("ft_read_first/init: Got error %d when reading table %s",error,
+ table->real_name);
+ table->file->print_error(error,MYF(0));
+ return 1;
+ }
+ return -1;
+ }
+
+ error=table->file->ft_read(table->record[0]);
+ if (error)
+ {
+ if (error != HA_ERR_END_OF_FILE)
+ {
+ sql_print_error("ft_read_first/read: Got error %d when reading table %s",
+ error, table->real_name);
+ table->file->print_error(error,MYF(0));
+ return 1;
+ }
+ table->file->ft_close();
+ return -1;
+ }
+ return 0;
+}
+
+static int
+join_ft_read_next(READ_RECORD *info)
+{
+ int error=info->file->ft_read(info->table->record[0]);
+ if (error)
+ {
+ if (error != HA_ERR_END_OF_FILE)
+ {
+ sql_print_error("ft_read_next: Got error %d when reading table %s",
+ error, info->table->real_name);
+ info->file->print_error(error,MYF(0));
+ return 1;
+ }
+ info->file->ft_close();
+ return -1;
+ }
+ return 0;
+}
+
+
+/*****************************************************************************
+** 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 */
+static int
+end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
+ bool end_of_records)
+{
+ DBUG_ENTER("end_send");
+ if (!end_of_records)
+ {
+ int error;
+ if (join->having && join->having->val_int() == 0.0)
+ DBUG_RETURN(0); // Didn't match having
+ if (join->procedure)
+ error=join->procedure->send_row(*join->fields);
+ else
+ error=join->result->send_data(*join->fields);
+ if (error)
+ DBUG_RETURN(-1); /* purecov: inspected */
+ if (++join->send_records >= join->thd->select_limit)
+ DBUG_RETURN(-3); // Abort nicely
+ }
+ else
+ {
+ if (join->procedure && join->procedure->end_of_records())
+ DBUG_RETURN(-1);
+ }
+ DBUG_RETURN(0);
+}
+
+
+ /* ARGSUSED */
+static int
+end_send_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
+ bool end_of_records)
+{
+ int idx= -1;
+ DBUG_ENTER("end_send_group");
+
+ if (!join->first_record || end_of_records ||
+ (idx=test_if_group_changed(join->group_fields)) >= 0)
+ {
+ if (join->first_record || (end_of_records && !join->group))
+ {
+ if (join->procedure)
+ join->procedure->end_group();
+ if (idx < (int) join->send_group_parts)
+ {
+ int error;
+ if (join->procedure)
+ {
+ if (join->having && join->having->val_int() == 0.0)
+ error= -1; // Didn't satisfy having
+ else
+ error=join->procedure->send_row(*join->fields) ? 1 : 0;
+ if (end_of_records && join->procedure->end_of_records())
+ error= 1; // Fatal error
+ }
+ else
+ {
+ if (!join->first_record)
+ clear_tables(join);
+ if (join->having && join->having->val_int() == 0.0)
+ error= -1; // Didn't satisfy having
+ else
+ error=join->result->send_data(*join->fields) ? 1 : 0;
+ }
+ if (error > 0)
+ DBUG_RETURN(-1); /* purecov: inspected */
+ if (end_of_records)
+ DBUG_RETURN(0);
+ if (!error && ++join->send_records >= join->thd->select_limit)
+ DBUG_RETURN(-3); /* Abort nicely */
+ }
+ }
+ else
+ {
+ if (end_of_records)
+ DBUG_RETURN(0);
+ join->first_record=1;
+ VOID(test_if_group_changed(join->group_fields));
+ }
+ if (idx < (int) join->send_group_parts)
+ {
+ copy_fields(&join->tmp_table_param);
+ init_sum_functions(join->sum_funcs);
+ if (join->procedure)
+ join->procedure->add();
+ DBUG_RETURN(0);
+ }
+ }
+ if (update_sum_func(join->sum_funcs))
+ DBUG_RETURN(-1);
+ if (join->procedure)
+ join->procedure->add();
+ DBUG_RETURN(0);
+}
+
+
+ /* ARGSUSED */
+static int
+end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
+ bool end_of_records)
+{
+ TABLE *table=join->tmp_table;
+ int error;
+ DBUG_ENTER("end_write");
+
+ if (join->thd->killed) // Aborted by user
+ {
+ my_error(ER_SERVER_SHUTDOWN,MYF(0)); /* purecov: inspected */
+ DBUG_RETURN(-2); /* purecov: inspected */
+ }
+ if (!end_of_records)
+ {
+ copy_fields(&join->tmp_table_param);
+ copy_funcs(join->tmp_table_param.funcs);
+
+ if (!table->uniques) // If not unique handling
+ {
+ /* Copy null values from group to row */
+ ORDER *group;
+ for (group=table->group ; group ; group=group->next)
+ {
+ Item *item= *group->item;
+ if (item->maybe_null)
+ {
+ Field *field=item->tmp_table_field();
+ field->ptr[-1]= (byte) (field->is_null() ? 0 : 1);
+ }
+ }
+ }
+ if (!join->having || join->having->val_int())
+ {
+ if ((error=table->file->write_row(table->record[0])))
+ {
+ if (error != HA_ERR_FOUND_DUPP_KEY &&
+ error != HA_ERR_FOUND_DUPP_UNIQUE)
+ {
+ if (create_myisam_from_heap(table, &join->tmp_table_param, error,1))
+ DBUG_RETURN(1); // Not a table_is_full error
+ table->uniques=0; // To ensure rows are the same
+ }
+ }
+ else
+ {
+ if (++join->send_records >= join->tmp_table_param.end_write_records)
+ DBUG_RETURN(-3);
+ }
+ }
+ }
+ DBUG_RETURN(0);
+}
+
+/* Group by searching after group record and updating it if possible */
+/* ARGSUSED */
+
+static int
+end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
+ bool end_of_records)
+{
+ TABLE *table=join->tmp_table;
+ ORDER *group;
+ int error;
+ DBUG_ENTER("end_update");
+
+ if (end_of_records)
+ DBUG_RETURN(0);
+ if (join->thd->killed) // Aborted by user
+ {
+ my_error(ER_SERVER_SHUTDOWN,MYF(0)); /* purecov: inspected */
+ DBUG_RETURN(-2); /* purecov: inspected */
+ }
+
+ copy_fields(&join->tmp_table_param); // Groups are copied twice.
+ /* Make a key of group index */
+ for (group=table->group ; group ; group=group->next)
+ {
+ Item *item= *group->item;
+ item->save_org_in_field(group->field);
+ if (item->maybe_null)
+ group->buff[0]=item->null_value ? 0: 1; // Save reversed value
+ }
+ // table->file->index_init(0);
+ if (!table->file->index_read(table->record[1],
+ join->tmp_table_param.group_buff,0,
+ HA_READ_KEY_EXACT))
+ { /* Update old record */
+ restore_record(table,1);
+ update_tmptable_sum_func(join->sum_funcs,table);
+ if ((error=table->file->update_row(table->record[1],
+ table->record[0])))
+ {
+ table->file->print_error(error,MYF(0)); /* purecov: inspected */
+ DBUG_RETURN(-1); /* purecov: inspected */
+ }
+ DBUG_RETURN(0);
+ }
+
+ /* The null bits are already set */
+ KEY_PART_INFO *key_part;
+ for (group=table->group,key_part=table->key_info[0].key_part;
+ group ;
+ group=group->next,key_part++)
+ 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);
+ if ((error=table->file->write_row(table->record[0])))
+ {
+ if (create_myisam_from_heap(table, &join->tmp_table_param, error, 0))
+ DBUG_RETURN(-1); // Not a table_is_full error
+ /* Change method to update rows */
+ table->file->index_init(0);
+ join->join_tab[join->tables-1].next_select=end_unique_update;
+ }
+ join->send_records++;
+ DBUG_RETURN(0);
+}
+
+/* Like end_update, but this is done with unique constraints instead of keys */
+
+static int
+end_unique_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
+ bool end_of_records)
+{
+ TABLE *table=join->tmp_table;
+ int error;
+ DBUG_ENTER("end_unique_update");
+
+ if (end_of_records)
+ DBUG_RETURN(0);
+ if (join->thd->killed) // Aborted by user
+ {
+ my_error(ER_SERVER_SHUTDOWN,MYF(0)); /* purecov: inspected */
+ DBUG_RETURN(-2); /* purecov: inspected */
+ }
+
+ init_tmptable_sum_functions(join->sum_funcs);
+ copy_fields(&join->tmp_table_param); // Groups are copied twice.
+ copy_funcs(join->tmp_table_param.funcs);
+
+ if (!(error=table->file->write_row(table->record[0])))
+ join->send_records++; // New group
+ else
+ {
+ if ((int) table->file->get_dup_key(error) < 0)
+ {
+ table->file->print_error(error,MYF(0)); /* purecov: inspected */
+ DBUG_RETURN(-1); /* purecov: inspected */
+ }
+ if (table->file->rnd_pos(table->record[1],table->file->dupp_ref))
+ {
+ table->file->print_error(error,MYF(0)); /* purecov: inspected */
+ DBUG_RETURN(-1); /* purecov: inspected */
+ }
+ restore_record(table,1);
+ update_tmptable_sum_func(join->sum_funcs,table);
+ if ((error=table->file->update_row(table->record[1],
+ table->record[0])))
+ {
+ table->file->print_error(error,MYF(0)); /* purecov: inspected */
+ DBUG_RETURN(-1); /* purecov: inspected */
+ }
+ }
+ DBUG_RETURN(0);
+}
+
+
+ /* ARGSUSED */
+static int
+end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
+ bool end_of_records)
+{
+ TABLE *table=join->tmp_table;
+ int error;
+ int idx= -1;
+ DBUG_ENTER("end_write_group");
+
+ if (join->thd->killed)
+ { // Aborted by user
+ my_error(ER_SERVER_SHUTDOWN,MYF(0)); /* purecov: inspected */
+ DBUG_RETURN(-2); /* purecov: inspected */
+ }
+ if (!join->first_record || end_of_records ||
+ (idx=test_if_group_changed(join->group_fields)) >= 0)
+ {
+ if (join->first_record || (end_of_records && !join->group))
+ {
+ if (join->procedure)
+ join->procedure->end_group();
+ if (idx < (int) join->send_group_parts)
+ {
+ if (!join->first_record)
+ clear_tables(join);
+ copy_sum_funcs(join->sum_funcs);
+ if (!join->having || join->having->val_int())
+ {
+ if ((error=table->file->write_row(table->record[0])))
+ {
+ if (create_myisam_from_heap(table, &join->tmp_table_param,
+ error, 0))
+ DBUG_RETURN(1); // Not a table_is_full error
+ }
+ else
+ join->send_records++;
+ }
+ if (end_of_records)
+ DBUG_RETURN(0);
+ }
+ }
+ else
+ {
+ join->first_record=1;
+ VOID(test_if_group_changed(join->group_fields));
+ }
+ if (idx < (int) join->send_group_parts)
+ {
+ copy_fields(&join->tmp_table_param);
+ copy_funcs(join->tmp_table_param.funcs);
+ init_sum_functions(join->sum_funcs);
+ if (join->procedure)
+ join->procedure->add();
+ DBUG_RETURN(0);
+ }
+ }
+ if (update_sum_func(join->sum_funcs))
+ DBUG_RETURN(-1);
+ if (join->procedure)
+ join->procedure->add();
+ DBUG_RETURN(0);
+}
+
+
+/*****************************************************************************
+** Remove calculation with tables that aren't yet read. Remove also tests
+** against fields that are read through key.
+** 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 */
+
+static bool test_if_ref(Item_field *left_item,Item *right_item)
+{
+ Field *field=left_item->field;
+ if (!field->table->const_table) // No need to change const test
+ {
+ Item *ref_item=part_of_refkey(field->table,field);
+ if (ref_item && ref_item->eq(right_item))
+ {
+ if (right_item->type() == Item::FIELD_ITEM)
+ return field->eq_def(((Item_field *) right_item)->field);
+ if (right_item->const_item())
+ {
+ // 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))
+ {
+ return !store_val_in_field(field,right_item);
+ }
+ }
+ }
+ }
+ return 0; // keep test
+}
+
+
+static COND *
+make_cond_for_table(COND *cond,table_map tables,table_map used_table)
+{
+ if (used_table && !(cond->used_tables() & used_table))
+ return (COND*) 0; // Already checked
+ if (cond->type() == Item::COND_ITEM)
+ {
+ if (((Item_cond*) cond)->functype() == Item_func::COND_AND_FUNC)
+ {
+ Item_cond_and *new_cond=new Item_cond_and;
+ if (!new_cond)
+ return (COND*) 0; // OOM /* purecov: inspected */
+ List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
+ Item *item;
+ while ((item=li++))
+ {
+ Item *fix=make_cond_for_table(item,tables,used_table);
+ if (fix)
+ new_cond->argument_list()->push_back(fix);
+ }
+ switch (new_cond->argument_list()->elements) {
+ case 0:
+ return (COND*) 0; // Always true
+ case 1:
+ return new_cond->argument_list()->head();
+ default:
+ new_cond->used_tables_cache=((Item_cond*) cond)->used_tables_cache &
+ tables;
+ return new_cond;
+ }
+ }
+ else
+ { // Or list
+ Item_cond_or *new_cond=new Item_cond_or;
+ if (!new_cond)
+ return (COND*) 0; // OOM /* purecov: inspected */
+ List_iterator<Item> li(*((Item_cond*) cond)->argument_list());
+ Item *item;
+ while ((item=li++))
+ {
+ Item *fix=make_cond_for_table(item,tables,0L);
+ if (!fix)
+ return (COND*) 0; // Always true
+ new_cond->argument_list()->push_back(fix);
+ }
+ new_cond->used_tables_cache=((Item_cond_or*) cond)->used_tables_cache;
+ 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
+ */
+
+ if (cond->marker == 3 || (cond->used_tables() & ~tables))
+ return (COND*) 0; // Can't check this yet
+ if (cond->marker == 2 || cond->eq_cmp_result() == Item::COND_OK)
+ return cond; // Not boolean op
+
+ if (((Item_func*) cond)->functype() == Item_func::EQ_FUNC)
+ {
+ Item *left_item= ((Item_func*) cond)->arguments()[0];
+ Item *right_item= ((Item_func*) cond)->arguments()[1];
+ if (left_item->type() == Item::FIELD_ITEM &&
+ test_if_ref((Item_field*) left_item,right_item))
+ {
+ cond->marker=3; // Checked when read
+ return (COND*) 0;
+ }
+ if (right_item->type() == Item::FIELD_ITEM &&
+ test_if_ref((Item_field*) right_item,left_item))
+ {
+ cond->marker=3; // Checked when read
+ return (COND*) 0;
+ }
+ }
+ cond->marker=2;
+ return cond;
+}
+
+static Item *
+part_of_refkey(TABLE *table,Field *field)
+{
+ uint ref_parts=table->reginfo.join_tab->ref.key_parts;
+ if (ref_parts)
+ {
+ KEY_PART_INFO *key_part=
+ table->key_info[table->reginfo.join_tab->ref.key].key_part;
+
+ for (uint part=0 ; part < ref_parts ; part++,key_part++)
+ if (field->eq(key_part->field) &&
+ !(key_part->key_part_flag & HA_PART_KEY))
+ return table->reginfo.join_tab->ref.items[part];
+ }
+ return (Item*) 0;
+}
+
+
+/*****************************************************************************
+** 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
+*****************************************************************************/
+
+static int test_if_order_by_key(ORDER *order, TABLE *table, uint idx)
+{
+ KEY_PART_INFO *key_part,*key_part_end;
+ key_part=table->key_info[idx].key_part;
+ key_part_end=key_part+table->key_info[idx].key_parts;
+ key_part_map const_key_parts=table->const_key_parts[idx];
+ int reverse=0;
+
+ for (; order ; order=order->next, const_key_parts>>=1)
+ {
+ Field *field=((Item_field*) (*order->item))->field;
+ int flag;
+
+ /*
+ Skip key parts that are constants in the WHERE clause.
+ These are already skipped in the ORDER BY by const_expression_in_where()
+ */
+ while (const_key_parts & 1)
+ {
+ key_part++; const_key_parts>>=1;
+ }
+ if (key_part == key_part_end || key_part->field != field)
+ return 0;
+
+ /* set flag to 1 if we can use read-next on key, else to -1 */
+ flag=(order->asc == !(key_part->key_part_flag & HA_REVERSE_SORT))
+ ? 1 : -1;
+ if (reverse && flag != reverse)
+ return 0;
+ reverse=flag; // Remember if reverse
+ key_part++;
+ }
+ return reverse;
+}
+
+static uint find_shortest_key(TABLE *table, key_map usable_keys)
+{
+ uint min_length= (uint) ~0;
+ uint best= MAX_KEY;
+ for (uint nr=0; usable_keys ; usable_keys>>=1, nr++)
+ {
+ if (usable_keys & 1)
+ {
+ if (table->key_info[nr].key_length < min_length)
+ {
+ min_length=table->key_info[nr].key_length;
+ best=nr;
+ }
+ }
+ }
+ return best;
+}
+
+
+/*****************************************************************************
+** If not selecting by given key, create a index how records should be read
+** return: 0 ok
+** -1 some fatal error
+** 1 no records
+*****************************************************************************/
+
+/* Return 1 if we don't have to do file sorting */
+
+static bool
+test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order,ha_rows select_limit)
+{
+ int ref_key;
+ TABLE *table=tab->table;
+ SQL_SELECT *select=tab->select;
+ key_map usable_keys;
+ DBUG_ENTER("test_if_skip_sort_order");
+
+ /* Check which keys can be used to resolve ORDER BY */
+ usable_keys= ~(key_map) 0;
+ for (ORDER *tmp_order=order; tmp_order ; tmp_order=tmp_order->next)
+ {
+ if ((*tmp_order->item)->type() != Item::FIELD_ITEM)
+ {
+ usable_keys=0;
+ break;
+ }
+ usable_keys&=((Item_field*) (*tmp_order->item))->field->part_of_key;
+ }
+
+ ref_key= -1;
+ if (tab->ref.key >= 0) // Constant range in WHERE
+ ref_key=tab->ref.key;
+ else if (select && select->quick) // Range found by opt_range
+ ref_key=select->quick->index;
+
+ if (ref_key >= 0)
+ {
+ /* 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)
+ DBUG_RETURN(1); /* No need to sort */
+ }
+ else
+ {
+ /* check if we can use a key to resolve the group */
+ /* Tables using JT_NEXT are handled here */
+ uint nr;
+ key_map keys=usable_keys;
+
+ /*
+ If not used with LIMIT, only use keys if the whole query can be
+ resolved with a key; This is because filesort() is usually faster than
+ retrieving all rows through an index.
+ */
+ if (select_limit >= table->file->records)
+ keys&= table->used_keys;
+
+ for (nr=0; keys ; keys>>=1, nr++)
+ {
+ if (keys & 1)
+ {
+ int flag;
+ if ((flag=test_if_order_by_key(order,table,nr)))
+ {
+ tab->index=nr;
+ tab->read_first_record= (flag > 0 ? join_init_read_first_with_key:
+ join_init_read_last_with_key);
+ tab->type=JT_NEXT; // Read with index_first(), index_next()
+ DBUG_RETURN(1);
+ }
+ }
+ }
+ }
+ DBUG_RETURN(0); // Can't use index.
+}
+
+
+static int
+create_sort_index(JOIN_TAB *tab,ORDER *order,ha_rows select_limit)
+{
+ SORT_FIELD *sortorder;
+ uint length;
+ TABLE *table=tab->table;
+ SQL_SELECT *select=tab->select;
+ DBUG_ENTER("create_sort_index");
+
+ if (test_if_skip_sort_order(tab,order,select_limit))
+ DBUG_RETURN(0);
+ if (!(sortorder=make_unireg_sortorder(order,&length)))
+ goto err; /* purecov: inspected */
+ /* It's not fatal if the following alloc fails */
+ table->io_cache=(IO_CACHE*) my_malloc(sizeof(IO_CACHE),
+ MYF(MY_FAE | MY_ZEROFILL));
+ table->status=0; // May be wrong if quick_select
+
+ // If table has a range, move it to select
+ if (select && !select->quick && tab->ref.key >= 0)
+ {
+ if (tab->quick)
+ {
+ select->quick=tab->quick;
+ tab->quick=0;
+ /* We can only use 'Only index' if quick key is same as ref_key */
+ if (table->key_read && (uint) tab->ref.key != select->quick->index)
+ {
+ table->key_read=0;
+ table->file->extra(HA_EXTRA_NO_KEYREAD);
+ }
+ }
+ else
+ {
+ /*
+ We have a ref on a const; Change this to a range that filesort
+ can use.
+ */
+ if (!(select->quick=get_quick_select_for_ref(table, &tab->ref)))
+ goto err;
+ }
+ }
+ table->found_records=filesort(&table,sortorder,length,
+ select, 0L, select_limit);
+ delete select; // filesort did select
+ tab->select=0;
+ tab->select_cond=0;
+ tab->type=JT_ALL; // Read with normal read_record
+ tab->read_first_record= join_init_read_record;
+ if (table->key_read) // Restore if we used indexes
+ {
+ table->key_read=0;
+ table->file->extra(HA_EXTRA_NO_KEYREAD);
+ }
+ DBUG_RETURN(table->found_records == HA_POS_ERROR);
+err:
+ DBUG_RETURN(-1);
+}
+
+
+/*****************************************************************************
+** 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)
+*****************************************************************************/
+
+static bool compare_record(TABLE *table, Field **ptr)
+{
+ for (; *ptr ; ptr++)
+ {
+ if ((*ptr)->cmp_offset(table->rec_buff_length))
+ return 1;
+ }
+ return 0;
+}
+
+static bool copy_blobs(Field **ptr)
+{
+ for (; *ptr ; ptr++)
+ {
+ if ((*ptr)->flags & BLOB_FLAG)
+ if (((Field_blob *) (*ptr))->copy())
+ return 1; // Error
+ }
+ return 0;
+}
+
+static void free_blobs(Field **ptr)
+{
+ for (; *ptr ; ptr++)
+ {
+ if ((*ptr)->flags & BLOB_FLAG)
+ ((Field_blob *) (*ptr))->free();
+ }
+}
+
+
+static int
+remove_duplicates(JOIN *join, TABLE *entry,List<Item> &fields)
+{
+ int error;
+ ulong reclength,offset;
+ uint field_count;
+ 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;
+ List_iterator<Item> it(fields);
+ Item *item;
+ while ((item=it++))
+ if (item->tmp_table_field())
+ field_count++;
+
+ if (!field_count)
+ { // only const items
+ join->thd->select_limit=1; // Only send first row
+ DBUG_RETURN(0);
+ }
+ Field **first_field=entry->field+entry->fields - field_count;
+ offset=entry->field[entry->fields - field_count]->offset();
+ reclength=entry->reclength-offset;
+
+ free_io_cache(entry); // Safety
+ entry->file->info(HA_STATUS_VARIABLE);
+ if (entry->db_type == DB_TYPE_HEAP ||
+ (!entry->blob_fields &&
+ ((ALIGN_SIZE(reclength) +sizeof(HASH_LINK)) * entry->file->records <
+ sortbuff_size)))
+ error=remove_dup_with_hash_index(join->thd, entry,
+ field_count, first_field,
+ reclength);
+ else
+ error=remove_dup_with_compare(join->thd, entry, first_field, offset);
+
+ free_blobs(first_field);
+ DBUG_RETURN(error);
+}
+
+
+static int remove_dup_with_compare(THD *thd, TABLE *table, Field **first_field,
+ ulong offset)
+{
+ handler *file=table->file;
+ char *org_record,*new_record;
+ int error;
+ ulong reclength=table->reclength-offset;
+ DBUG_ENTER("remove_dup_with_compare");
+
+ org_record=(char*) table->record[0]+offset;
+ new_record=(char*) table->record[1]+offset;
+
+ file->rnd_init();
+ error=file->rnd_next(table->record[0]);
+ for (;;)
+ {
+ if (thd->killed)
+ {
+ my_error(ER_SERVER_SHUTDOWN,MYF(0));
+ error=0;
+ goto err;
+ }
+ if (error)
+ {
+ if (error == HA_ERR_RECORD_DELETED)
+ continue;
+ if (error == HA_ERR_END_OF_FILE)
+ break;
+ goto err;
+ }
+ if (copy_blobs(first_field))
+ {
+ my_error(ER_OUT_OF_SORTMEMORY,MYF(0));
+ error=0;
+ goto err;
+ }
+ memcpy(new_record,org_record,reclength);
+
+ /* Read through rest of file and mark duplicated rows deleted */
+ bool found=0;
+ for (;;)
+ {
+ if ((error=file->rnd_next(table->record[0])))
+ {
+ if (error == HA_ERR_RECORD_DELETED)
+ continue;
+ if (error == HA_ERR_END_OF_FILE)
+ break;
+ goto err;
+ }
+ if (compare_record(table, first_field) == 0)
+ {
+ if ((error=file->delete_row(table->record[0])))
+ goto err;
+ }
+ else if (!found)
+ {
+ found=1;
+ file->position(table->record[0]); // Remember position
+ }
+ }
+ if (!found)
+ break; // End of file
+ /* Restart search on next row */
+ error=file->restart_rnd_next(table->record[0],file->ref);
+ }
+
+ file->extra(HA_EXTRA_NO_CACHE);
+ DBUG_RETURN(0);
+err:
+ file->extra(HA_EXTRA_NO_CACHE);
+ if (error)
+ file->print_error(error,MYF(0));
+ DBUG_RETURN(1);
+}
+
+
+/*
+ Generate a hash index for each row to quickly find duplicate rows
+ Note that this will not work on tables with blobs!
+*/
+
+static int remove_dup_with_hash_index(THD *thd, TABLE *table,
+ uint field_count,
+ Field **first_field,
+ ulong key_length)
+{
+ byte *key_buffer, *key_pos, *record=table->record[0];
+ int error;
+ handler *file=table->file;
+ ulong extra_length=ALIGN_SIZE(key_length)-key_length;
+ uint *field_lengths,*field_length;
+ HASH hash;
+ DBUG_ENTER("remove_dup_with_hash_index");
+
+ if (!my_multi_malloc(MYF(MY_WME),
+ &key_buffer,
+ (uint) ((key_length + extra_length) *
+ (long) file->records),
+ &field_lengths,
+ (uint) (field_count*sizeof(*field_lengths)),
+ NullS))
+ DBUG_RETURN(1);
+ if (hash_init(&hash, (uint) file->records, 0, key_length,
+ (hash_get_key) 0, 0, 0))
+ {
+ my_free((char*) key_buffer,MYF(0));
+ DBUG_RETURN(1);
+ }
+ {
+ Field **ptr;
+ for (ptr= first_field, field_length=field_lengths ; *ptr ; ptr++)
+ (*field_length++)= (*ptr)->pack_length();
+ }
+
+ file->rnd_init();
+ key_pos=key_buffer;
+ for (;;)
+ {
+ if (thd->killed)
+ {
+ my_error(ER_SERVER_SHUTDOWN,MYF(0));
+ error=0;
+ goto err;
+ }
+ if ((error=file->rnd_next(record)))
+ {
+ if (error == HA_ERR_RECORD_DELETED)
+ continue;
+ if (error == HA_ERR_END_OF_FILE)
+ break;
+ goto err;
+ }
+
+ /* copy fields to key buffer */
+ field_length=field_lengths;
+ for (Field **ptr= first_field ; *ptr ; ptr++)
+ {
+ (*ptr)->sort_string((char*) key_pos,*field_length);
+ key_pos+= *field_length++;
+ }
+ /* Check if it exists before */
+ if (hash_search(&hash,key_pos-key_length,key_length))
+ {
+ /* Duplicated found ; Remove the row */
+ if ((error=file->delete_row(record)))
+ goto err;
+ }
+ (void) hash_insert(&hash, key_pos-key_length);
+ key_pos+=extra_length;
+ }
+ my_free((char*) key_buffer,MYF(0));
+ hash_free(&hash);
+ file->extra(HA_EXTRA_NO_CACHE);
+ (void) file->rnd_end();
+ DBUG_RETURN(0);
+
+err:
+ my_free((char*) key_buffer,MYF(0));
+ hash_free(&hash);
+ file->extra(HA_EXTRA_NO_CACHE);
+ (void) file->rnd_end();
+ if (error)
+ file->print_error(error,MYF(0));
+ DBUG_RETURN(1);
+}
+
+
+static SORT_FIELD *
+make_unireg_sortorder(ORDER *order, uint *length)
+{
+ uint count;
+ SORT_FIELD *sort,*pos;
+ DBUG_ENTER("make_unireg_sortorder");
+
+ count=0;
+ for (ORDER *tmp = order; tmp; tmp=tmp->next)
+ count++;
+ pos=sort=(SORT_FIELD*) sql_alloc(sizeof(SORT_FIELD)*(count+1));
+ if (!pos)
+ return 0;
+
+ for (;order;order=order->next,pos++)
+ {
+ pos->field=0; pos->item=0;
+ if (order->item[0]->type() == Item::FIELD_ITEM)
+ pos->field= ((Item_field*) (*order->item))->field;
+ else if (order->item[0]->type() == Item::SUM_FUNC_ITEM &&
+ !order->item[0]->const_item())
+ pos->field= ((Item_sum*) order->item[0])->tmp_table_field();
+ else if (order->item[0]->type() == Item::COPY_STR_ITEM)
+ { // Blob patch
+ pos->item= ((Item_copy_string*) (*order->item))->item;
+ }
+ else
+ pos->item= *order->item;
+ pos->reverse=! order->asc;
+ }
+ *length=count;
+ DBUG_RETURN(sort);
+}
+
+
+/*****************************************************************************
+** Fill join cache with packed records
+** Records are stored in tab->cache.buffer and last record in
+** last record is stored with pointers to blobs to support very big
+** records
+******************************************************************************/
+
+static int
+join_init_cache(THD *thd,JOIN_TAB *tables,uint table_count)
+{
+ reg1 uint i;
+ uint length,blobs,size;
+ CACHE_FIELD *copy,**blob_ptr;
+ JOIN_CACHE *cache;
+ DBUG_ENTER("join_init_cache");
+
+ cache= &tables[table_count].cache;
+ cache->fields=blobs=0;
+
+ for (i=0 ; i < table_count ; i++)
+ {
+ cache->fields+=tables[i].used_fields;
+ blobs+=tables[i].used_blobs;
+ }
+ if (!(cache->field=(CACHE_FIELD*)
+ sql_alloc(sizeof(CACHE_FIELD)*(cache->fields+table_count*2)+(blobs+1)*
+ sizeof(CACHE_FIELD*))))
+ {
+ my_free((gptr) cache->buff,MYF(0)); /* purecov: inspected */
+ cache->buff=0; /* purecov: inspected */
+ DBUG_RETURN(1); /* purecov: inspected */
+ }
+ copy=cache->field;
+ blob_ptr=cache->blob_ptr=(CACHE_FIELD**)
+ (cache->field+cache->fields+table_count*2);
+
+ length=0;
+ for (i=0 ; i < table_count ; i++)
+ {
+ uint null_fields=0,used_fields;
+
+ Field **f_ptr,*field;
+ for (f_ptr=tables[i].table->field,used_fields=tables[i].used_fields ;
+ used_fields ;
+ f_ptr++)
+ {
+ field= *f_ptr;
+ if (field->query_id == thd->query_id)
+ {
+ used_fields--;
+ length+=field->fill_cache_field(copy);
+ if (copy->blob_field)
+ (*blob_ptr++)=copy;
+ if (field->maybe_null())
+ null_fields++;
+ copy++;
+ }
+ }
+ /* Copy null bits from table */
+ if (null_fields && tables[i].table->null_fields)
+ { /* must copy null bits */
+ copy->str=(char*) tables[i].table->null_flags;
+ copy->length=(tables[i].table->null_fields+7)/8;
+ copy->strip=0;
+ copy->blob_field=0;
+ length+=copy->length;
+ copy++;
+ cache->fields++;
+ }
+ /* If outer join table, copy null_row flag */
+ if (tables[i].table->maybe_null)
+ {
+ copy->str= (char*) &tables[i].table->null_row;
+ copy->length=sizeof(tables[i].table->null_row);
+ copy->strip=0;
+ copy->blob_field=0;
+ length+=copy->length;
+ copy++;
+ cache->fields++;
+ }
+ }
+
+ cache->records=0; cache->ptr_record= (uint) ~0;
+ cache->length=length+blobs*sizeof(char*);
+ cache->blobs=blobs;
+ *blob_ptr=0; /* End sequentel */
+ size=max(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;
+ reset_cache(cache);
+ DBUG_RETURN(0);
+}
+
+
+static ulong
+used_blob_length(CACHE_FIELD **ptr)
+{
+ uint length,blob_length;
+ for (length=0 ; *ptr ; ptr++)
+ {
+ (*ptr)->blob_length=blob_length=(*ptr)->blob_field->get_length();
+ length+=blob_length;
+ (*ptr)->blob_field->get_ptr(&(*ptr)->str);
+ }
+ return length;
+}
+
+
+static bool
+store_record_in_cache(JOIN_CACHE *cache)
+{
+ ulong length;
+ uchar *pos;
+ CACHE_FIELD *copy,*end_field;
+ bool last_record;
+
+ pos=cache->pos;
+ end_field=cache->field+cache->fields;
+
+ length=cache->length;
+ if (cache->blobs)
+ length+=used_blob_length(cache->blob_ptr);
+ if ((last_record=(length+cache->length > (uint) (cache->end - pos))))
+ cache->ptr_record=cache->records;
+
+ /*
+ ** There is room in cache. Put record there
+ */
+ cache->records++;
+ for (copy=cache->field ; copy < end_field; copy++)
+ {
+ if (copy->blob_field)
+ {
+ if (last_record)
+ {
+ copy->blob_field->get_image((char*) pos,copy->length+sizeof(char*));
+ pos+=copy->length+sizeof(char*);
+ }
+ else
+ {
+ copy->blob_field->get_image((char*) pos,copy->length); // blob length
+ memcpy(pos+copy->length,copy->str,copy->blob_length); // Blob data
+ pos+=copy->length+copy->blob_length;
+ }
+ }
+ else
+ {
+ if (copy->strip)
+ {
+ char *str,*end;
+ for (str=copy->str,end= str+copy->length;
+ end > str && end[-1] == ' ' ;
+ end--) ;
+ length=(uint) (end-str);
+ memcpy(pos+1,str,length);
+ *pos=(uchar) length;
+ pos+=length+1;
+ }
+ else
+ {
+ memcpy(pos,copy->str,copy->length);
+ pos+=copy->length;
+ }
+ }
+ }
+ cache->pos=pos;
+ return last_record || (uint) (cache->end -pos) < cache->length;
+}
+
+
+static void
+reset_cache(JOIN_CACHE *cache)
+{
+ cache->record_nr=0;
+ cache->pos=cache->buff;
+}
+
+
+static void
+read_cached_record(JOIN_TAB *tab)
+{
+ uchar *pos;
+ uint length;
+ bool last_record;
+ CACHE_FIELD *copy,*end_field;
+
+ last_record=tab->cache.record_nr++ == tab->cache.ptr_record;
+ pos=tab->cache.pos;
+
+ for (copy=tab->cache.field,end_field=copy+tab->cache.fields ;
+ copy < end_field;
+ copy++)
+ {
+ if (copy->blob_field)
+ {
+ if (last_record)
+ {
+ copy->blob_field->set_image((char*) pos,copy->length+sizeof(char*));
+ pos+=copy->length+sizeof(char*);
+ }
+ else
+ {
+ copy->blob_field->set_ptr((char*) pos,(char*) pos+copy->length);
+ pos+=copy->length+copy->blob_field->get_length();
+ }
+ }
+ else
+ {
+ if (copy->strip)
+ {
+ memcpy(copy->str,pos+1,length=(uint) *pos);
+ memset(copy->str+length,' ',copy->length-length);
+ pos+=1+length;
+ }
+ else
+ {
+ memcpy(copy->str,pos,copy->length);
+ pos+=copy->length;
+ }
+ }
+ }
+ tab->cache.pos=pos;
+ return;
+}
+
+
+static bool
+cmp_buffer_with_ref(JOIN_TAB *tab)
+{
+ bool diff;
+ if (!(diff=tab->ref.key_err))
+ {
+ memcpy(tab->ref.key_buff2, tab->ref.key_buff, tab->ref.key_length);
+ }
+ if ((tab->ref.key_err=cp_buffer_from_ref(&tab->ref)) || diff)
+ return 1;
+ return memcmp(tab->ref.key_buff2, tab->ref.key_buff, tab->ref.key_length)
+ != 0;
+}
+
+
+bool
+cp_buffer_from_ref(TABLE_REF *ref)
+{
+ for (store_key **copy=ref->key_copy ; *copy ; copy++)
+ if ((*copy)->copy())
+ return 1; // Something went wrong
+ return 0;
+}
+
+
+/*****************************************************************************
+** 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.
+*/
+
+static int
+find_order_in_list(THD *thd,TABLE_LIST *tables,ORDER *order,List<Item> &fields,
+ List<Item> &all_fields)
+{
+ if ((*order->item)->type() == Item::INT_ITEM)
+ { /* Order by position */
+ Item *item=0;
+ List_iterator<Item> li(fields);
+
+ for (uint count= (uint) ((Item_int*) (*order->item))->value ;
+ count-- && (item=li++) ;) ;
+ if (!item)
+ {
+ my_printf_error(ER_BAD_FIELD_ERROR,ER(ER_BAD_FIELD_ERROR),
+ MYF(0),(*order->item)->full_name(),
+ thd->where);
+ return 1;
+ }
+ order->item=li.ref();
+ order->in_field_list=1;
+ return 0;
+ }
+ const char *save_where=thd->where;
+ thd->where=0; // No error if not found
+ Item **item=find_item_in_list(*order->item,fields);
+ thd->where=save_where;
+ if (item)
+ {
+ order->item=item; // use it
+ order->in_field_list=1;
+ return 0;
+ }
+ order->in_field_list=0;
+ if ((*order->item)->fix_fields(thd,tables) || thd->fatal_error)
+ return 1; // Wrong field
+ all_fields.push_front(*order->item); // Add new field to field list
+ order->item=(Item**) all_fields.head_ref();
+ return 0;
+}
+
+
+/*
+** 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.
+*/
+
+static int
+setup_order(THD *thd,TABLE_LIST *tables,List<Item> &fields,
+ List<Item> &all_fields, ORDER *order)
+{
+ thd->where="order clause";
+ for (; order; order=order->next)
+ {
+ if (find_order_in_list(thd,tables,order,fields,all_fields))
+ return 1;
+ }
+ return 0;
+}
+
+
+static int
+setup_group(THD *thd,TABLE_LIST *tables,List<Item> &fields,
+ List<Item> &all_fields, ORDER *order, bool *hidden_group_fields)
+{
+ *hidden_group_fields=0;
+ if (!order)
+ return 0; /* Everything is ok */
+
+ if (thd->options & OPTION_ANSI_MODE)
+ {
+ Item *item;
+ List_iterator<Item> li(fields);
+ while ((item=li++))
+ item->marker=0; /* Marker that field is not used */
+ }
+ uint org_fields=all_fields.elements;
+
+ thd->where="group statement";
+ for ( ; order; order=order->next)
+ {
+ if (find_order_in_list(thd,tables,order,fields,all_fields))
+ return 1;
+ (*order->item)->marker=1; /* Mark found */
+ if ((*order->item)->with_sum_func)
+ {
+ my_printf_error(ER_WRONG_GROUP_FIELD, ER(ER_WRONG_GROUP_FIELD),MYF(0),
+ (*order->item)->full_name());
+ return 1;
+ }
+ }
+ if (thd->options & OPTION_ANSI_MODE)
+ {
+ /* Don't allow one to use fields that is not used in GROUP BY */
+ Item *item;
+ List_iterator<Item> li(fields);
+
+ while ((item=li++))
+ {
+ if (item->type() != Item::SUM_FUNC_ITEM && !item->marker)
+ {
+ my_printf_error(ER_WRONG_FIELD_WITH_GROUP,
+ ER(ER_WRONG_FIELD_WITH_GROUP),
+ MYF(0),item->full_name());
+ return 1;
+ }
+ }
+ }
+ if (org_fields != all_fields.elements)
+ *hidden_group_fields=1; // group fields is not used
+ return 0;
+}
+
+/*
+** Add fields with aren't used at start of field list. Return FALSE if ok
+*/
+
+static bool
+setup_new_fields(THD *thd,TABLE_LIST *tables,List<Item> &fields,
+ List<Item> &all_fields, ORDER *new_field)
+{
+ Item **item;
+ DBUG_ENTER("setup_new_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)
+ {
+ if ((item=find_item_in_list(*new_field->item,fields)))
+ new_field->item=item; /* Change to shared Item */
+ else
+ {
+ thd->where="procedure list";
+ if ((*new_field->item)->fix_fields(thd,tables))
+ DBUG_RETURN(1); /* purecov: inspected */
+ thd->where=0;
+ all_fields.push_front(*new_field->item);
+ new_field->item=all_fields.head_ref();
+ }
+ }
+ DBUG_RETURN(0);
+}
+
+/*
+** 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)
+{
+ List_iterator<Item> li(fields);
+ Item *item;
+ ORDER *order,*group,**prev;
+
+ while ((item=li++))
+ item->marker=0; /* Marker that field is not used */
+
+ prev= &group; group=0;
+ for (order=order_list ; order; order=order->next)
+ {
+ if (order->in_field_list)
+ {
+ ORDER *ord=(ORDER*) sql_memdup(order,sizeof(ORDER));
+ if (!ord)
+ return 0;
+ *prev=ord;
+ prev= &ord->next;
+ (*ord->item)->marker=1;
+ }
+ }
+
+ li.rewind();
+ while ((item=li++))
+ {
+ if (item->const_item() || item->with_sum_func)
+ continue;
+ if (!item->marker)
+ {
+ ORDER *ord=(ORDER*) sql_calloc(sizeof(ORDER));
+ if (!ord)
+ return 0;
+ ord->item=li.ref();
+ ord->asc=1;
+ *prev=ord;
+ prev= &ord->next;
+ }
+ }
+ *prev=0;
+ return group;
+}
+
+
+/*****************************************************************************
+** Update join with count of the different type of fields
+*****************************************************************************/
+
+void
+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=0;
+ param->quick_group=1;
+ while ((field=li++))
+ {
+ Item::Type type=field->type();
+ if (type == Item::FIELD_ITEM)
+ param->field_count++;
+ else if (type == Item::SUM_FUNC_ITEM)
+ {
+ if (! field->const_item())
+ {
+ Item_sum *sum_item=(Item_sum*) field;
+ if (!sum_item->quick_group)
+ param->quick_group=0; // UDF SUM function
+ param->sum_func_count++;
+
+ for (uint i=0 ; i < sum_item->arg_count ; i++)
+ {
+ if (sum_item->args[0]->type() == Item::FIELD_ITEM)
+ param->field_count++;
+ else
+ param->func_count++;
+ }
+ }
+ }
+ else
+ param->func_count++;
+ }
+}
+
+
+/*
+ Return 1 if second is a subpart of first argument
+ If first parts has different direction, change it to second part
+ (group is sorted like order)
+*/
+
+static bool
+test_if_subpart(ORDER *a,ORDER *b)
+{
+ for (; a && b; a=a->next,b=b->next)
+ {
+ if ((*a->item)->eq(*b->item))
+ a->asc=b->asc;
+ else
+ return 0;
+ }
+ return test(!b);
+}
+
+/*
+ Return table number if there is only one table in sort order
+ and group and order is compatible
+ else return 0;
+*/
+
+static TABLE *
+get_sort_by_table(ORDER *a,ORDER *b,TABLE_LIST *tables)
+{
+ table_map map= (table_map) 0;
+ DBUG_ENTER("get_sort_by_table");
+
+ if (!a)
+ a=b; // Only one need to be given
+ else if (!b)
+ b=a;
+
+ for (; a && b; a=a->next,b=b->next)
+ {
+ if (!(*a->item)->eq(*b->item))
+ DBUG_RETURN(0);
+ map|=a->item[0]->used_tables();
+ }
+ if (!map || (map & RAND_TABLE_BIT))
+ DBUG_RETURN(0);
+
+ 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));
+ DBUG_RETURN(tables->table);
+}
+
+
+ /* calc how big buffer we need for comparing group entries */
+
+static void
+calc_group_buffer(JOIN *join,ORDER *group)
+{
+ uint key_length=0,parts=0;
+ if (group)
+ join->group= 1;
+ for (; group ; group=group->next)
+ {
+ Field *field=(*group->item)->tmp_table_field();
+ if (field)
+ {
+ if (field->type() == FIELD_TYPE_BLOB)
+ key_length+=MAX_BLOB_WIDTH; // Can't be used as a key
+ else
+ key_length+=field->pack_length();
+ }
+ else if ((*group->item)->result_type() == REAL_RESULT)
+ key_length+=sizeof(double);
+ else if ((*group->item)->result_type() == INT_RESULT)
+ key_length+=sizeof(longlong);
+ else
+ key_length+=(*group->item)->max_length;
+ parts++;
+ if ((*group->item)->maybe_null)
+ key_length++;
+ }
+ join->tmp_table_param.group_length=key_length;
+ join->tmp_table_param.group_parts=parts;
+}
+
+
+/*
+** Get a list of buffers for saveing last group
+** Groups are saved in reverse order for easyer check loop
+*/
+
+static bool
+alloc_group_fields(JOIN *join,ORDER *group)
+{
+ if (group)
+ {
+ for (; group ; group=group->next)
+ {
+ Item_buff *tmp=new_Item_buff(*group->item);
+ if (!tmp || join->group_fields.push_front(tmp))
+ return TRUE;
+ }
+ }
+ join->sort_and_group=1; /* Mark for do_select */
+ return FALSE;
+}
+
+
+static int
+test_if_group_changed(List<Item_buff> &list)
+{
+ List_iterator<Item_buff> li(list);
+ int idx= -1,i;
+ Item_buff *buff;
+
+ for (i=(int) list.elements-1 ; (buff=li++) ; i--)
+ {
+ if (buff->cmp())
+ idx=i;
+ }
+ return idx;
+}
+
+
+
+/*
+** 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)
+{
+ Item *pos;
+ List_iterator<Item> li(fields);
+ Copy_field *copy;
+ DBUG_ENTER("setup_copy_fields");
+
+ if (!(copy=param->copy_field= new Copy_field[param->field_count]))
+ goto err;
+
+ param->copy_funcs.empty();
+ while ((pos=li++))
+ {
+ if (pos->type() == Item::FIELD_ITEM)
+ {
+ Item_field *item=(Item_field*) pos;
+ if (item->field->flags & BLOB_FLAG)
+ {
+ if (!(pos=new Item_copy_string(pos)))
+ goto err;
+ VOID(li.replace(pos));
+ if (param->copy_funcs.push_back(pos))
+ goto err;
+ continue;
+ }
+
+ /* 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);
+ char *tmp=(char*) sql_alloc(field->pack_length()+1);
+ if (!tmp)
+ goto err;
+ copy->set(tmp, item->result_field);
+ item->result_field->move_field(copy->to_ptr,copy->to_null_ptr,1);
+ copy++;
+ }
+ else if ((pos->type() == Item::FUNC_ITEM ||
+ pos->type() == Item::COND_ITEM) &&
+ !pos->with_sum_func)
+ { // Save for send fields
+ /* TODO:
+ In most cases this result will be sent to the user.
+ This should be changed to use copy_int or copy_real depending
+ on how the value is to be used: In some cases this may be an
+ argument in a group function, like: IF(ISNULL(col),0,COUNT(*))
+ */
+ if (!(pos=new Item_copy_string(pos)))
+ goto err;
+ VOID(li.replace(pos));
+ if (param->copy_funcs.push_back(pos))
+ goto err;
+ }
+ }
+ param->copy_field_count= (uint) (copy - param->copy_field);
+ DBUG_RETURN(0);
+
+ err:
+ delete [] param->copy_field;
+ param->copy_field=0;
+ DBUG_RETURN(TRUE);
+}
+
+
+/*
+** 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;
+
+ for ( ; ptr != end; ptr++)
+ (*ptr->do_copy)(ptr);
+
+ List_iterator<Item> it(param->copy_funcs);
+ 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
+*****************************************************************************/
+
+static bool
+make_sum_func_list(JOIN *join,List<Item> &fields)
+{
+ DBUG_ENTER("make_sum_func_list");
+ Item_sum **func =
+ (Item_sum**) sql_alloc(sizeof(Item_sum*)*
+ (join->tmp_table_param.sum_func_count+1));
+ if (!func)
+ DBUG_RETURN(TRUE);
+ List_iterator<Item> it(fields);
+ join->sum_funcs=func;
+
+ Item *field;
+ while ((field=it++))
+ {
+ if (field->type() == Item::SUM_FUNC_ITEM && !field->const_item())
+ {
+ *func++=(Item_sum*) field;
+ /* let COUNT(DISTINCT) create the temporary table */
+ if (((Item_sum*) field)->setup(join->thd))
+ DBUG_RETURN(TRUE);
+ }
+ }
+ *func=0; // End marker
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+** Change all funcs and sum_funcs to fields in tmp table
+*/
+
+static bool
+change_to_use_tmp_fields(List<Item> &items)
+{
+ List_iterator<Item> it(items);
+ Item *item_field,*item;
+
+ while ((item=it++))
+ {
+ Field *field;
+ if (item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM)
+ continue;
+ if (item->type() == Item::FIELD_ITEM)
+ {
+ ((Item_field*) item)->field=
+ ((Item_field*) item)->result_field;
+ }
+ else if ((field=item->tmp_table_field()))
+ {
+ if (item->type() == Item::SUM_FUNC_ITEM && field->table->group)
+ item_field=((Item_sum*) item)->result_item(field);
+ else
+ item_field=(Item*) new Item_field(field);
+ if (!item_field)
+ return TRUE; // Fatal error
+ item_field->name=item->name; /*lint -e613 */
+#ifndef DBUG_OFF
+ if (_db_on_ && !item_field->name)
+ {
+ char buff[256];
+ String str(buff,sizeof(buff));
+ str.length(0);
+ item->print(&str);
+ item_field->name=sql_strmake(str.ptr(),str.length());
+ }
+#endif
+#ifdef DELETE_ITEMS
+ delete it.replace(item_field); /*lint -e613 */
+#else
+ (void) it.replace(item_field); /*lint -e613 */
+#endif
+ }
+ }
+ return FALSE;
+}
+
+
+/*
+** 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
+change_refs_to_tmp_fields(THD *thd,List<Item> &items)
+{
+ List_iterator<Item> it(items);
+ Item *item;
+
+ while ((item= it++))
+ {
+ if (item->type() == Item::SUM_FUNC_ITEM)
+ {
+ if (!item->const_item())
+ {
+ Item_sum *sum_item= (Item_sum*) item;
+ if (sum_item->result_field) // If not a const sum func
+ {
+ Field *result_field=sum_item->result_field;
+ for (uint i=0 ; i < sum_item->arg_count ; i++)
+ {
+ Item *arg= sum_item->args[i];
+ if (!arg->const_item())
+ {
+ if (arg->type() == Item::FIELD_ITEM)
+ ((Item_field*) arg)->field= result_field++;
+ else
+ sum_item->args[i]= new Item_field(result_field++);
+ }
+ }
+ }
+ }
+ }
+ else if (item->with_sum_func)
+ continue;
+ else if ((item->type() == Item::FUNC_ITEM ||
+ item->type() == Item::COND_ITEM) &&
+ !item->const_item())
+ { /* All funcs are stored */
+#ifdef DELETE_ITEMS
+ delete it.replace(new Item_field(((Item_func*) item)->result_field));
+#else
+ (void) it.replace(new Item_field(((Item_func*) item)->result_field));
+#endif
+ }
+ else if (item->type() == Item::FIELD_ITEM) /* Change refs */
+ {
+ ((Item_field*)item)->field=((Item_field*) item)->result_field;
+ }
+ }
+ return thd->fatal_error;
+}
+
+
+
+/******************************************************************************
+** code for calculating functions
+******************************************************************************/
+
+static void
+init_tmptable_sum_functions(Item_sum **func_ptr)
+{
+ Item_sum *func;
+ while ((func= *(func_ptr++)))
+ func->reset_field();
+}
+
+
+ /* Update record 0 in tmp_table from record 1 */
+
+static void
+update_tmptable_sum_func(Item_sum **func_ptr,
+ TABLE *tmp_table __attribute__((unused)))
+{
+ Item_sum *func;
+ while ((func= *(func_ptr++)))
+ func->update_field(0);
+}
+
+
+ /* Copy result of sum functions to record in tmp_table */
+
+static void
+copy_sum_funcs(Item_sum **func_ptr)
+{
+ Item_sum *func;
+ for (; (func = *func_ptr) ; func_ptr++)
+ (void) func->save_in_field(func->result_field);
+ return;
+}
+
+
+static void
+init_sum_functions(Item_sum **func_ptr)
+{
+ Item_sum *func;
+ for (; (func= (Item_sum*) *func_ptr) ; func_ptr++)
+ func->reset();
+}
+
+
+static bool
+update_sum_func(Item_sum **func_ptr)
+{
+ Item_sum *func;
+ for (; (func= (Item_sum*) *func_ptr) ; func_ptr++)
+ if (func->add())
+ return 1;
+ return 0;
+}
+
+ /* Copy result of functions to record in tmp_table */
+
+void
+copy_funcs(Item_result_field **func_ptr)
+{
+ Item_result_field *func;
+ for (; (func = *func_ptr) ; func_ptr++)
+ (void) func->save_in_field(func->result_field);
+ return;
+}
+
+
+/*****************************************************************************
+** 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)
+{
+ DBUG_ENTER("add_ref_to_table_cond");
+ if (!join_tab->ref.key_parts)
+ DBUG_RETURN(FALSE);
+
+ Item_cond_and *cond=new Item_cond_and();
+ TABLE *table=join_tab->table;
+ int error;
+ if (!cond)
+ DBUG_RETURN(TRUE);
+
+ for (uint i=0 ; i < join_tab->ref.key_parts ; i++)
+ {
+ Field *field=table->field[table->key_info[join_tab->ref.key].key_part[i].fieldnr-1];
+ Item *value=join_tab->ref.items[i];
+ cond->add(new Item_func_equal(new Item_field(field),value));
+ }
+ if (thd->fatal_error)
+ DBUG_RETURN(TRUE);
+ cond->fix_fields((THD *) 0,(TABLE_LIST *) 0);
+ if (join_tab->select)
+ {
+ error=(int) cond->add(join_tab->select->cond);
+ join_tab->select_cond=join_tab->select->cond=cond;
+ }
+ else if ((join_tab->select=make_select(join_tab->table, 0, 0, cond,&error)))
+ join_tab->select_cond=cond;
+
+ DBUG_RETURN(error ? TRUE : FALSE);
+}
+
+/****************************************************************************
+** 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)
+{
+ DBUG_ENTER("select_describe");
+
+ List<Item> field_list;
+ Item *item;
+
+ 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(join->thd,field_list,1))
+ return; /* purecov: inspected */
+
+ char buff[512],*buff_ptr;
+ String tmp(buff,sizeof(buff)),*packet= &join->thd->packet;
+ for (uint i=0 ; i < join->tables ; i++)
+ {
+ 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)
+ {
+ 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,"where used");
+ }
+ if (key_read)
+ {
+ if (buff != buff_ptr)
+ {
+ buff_ptr[0]=';' ; buff_ptr[1]=' '; buff_ptr+=2;
+ }
+ buff_ptr=strmov(buff_ptr,"Using index");
+ }
+ if (table->reginfo.not_exists_optimize)
+ {
+ if (buff != buff_ptr)
+ {
+ buff_ptr[0]=';' ; buff_ptr[1]=' '; buff_ptr+=2;
+ }
+ buff_ptr=strmov(buff_ptr,"Not exists");
+ }
+ if (need_tmp_table)
+ {
+ need_tmp_table=0;
+ if (buff != buff_ptr)
+ {
+ buff_ptr[0]=';' ; buff_ptr[1]=' '; buff_ptr+=2;
+ }
+ buff_ptr=strmov(buff_ptr,"Using temporary");
+ }
+ if (need_order)
+ {
+ need_order=0;
+ if (buff != buff_ptr)
+ {
+ buff_ptr[0]=';' ; buff_ptr[1]=' '; buff_ptr+=2;
+ }
+ buff_ptr=strmov(buff_ptr,"Using filesort");
+ }
+ net_store_data(packet,buff,(uint) (buff_ptr - buff));
+ if (my_net_write(&join->thd->net,(char*) packet->ptr(),packet->length()))
+ DBUG_VOID_RETURN; /* purecov: inspected */
+ }
+ send_eof(&join->thd->net);
+ DBUG_VOID_RETURN;
+}
+
+
+static void describe_info(const char *info)
+{
+ List<Item> field_list;
+ THD *thd=current_thd;
+ String *packet= &thd->packet;
+
+ field_list.push_back(new Item_empty_string("Comment",80));
+ if (send_fields(thd,field_list,1))
+ return; /* purecov: inspected */
+ packet->length(0);
+ net_store_data(packet,info);
+ if (!my_net_write(&thd->net,(char*) packet->ptr(),packet->length()))
+ send_eof(&thd->net);
+}
diff --git a/sql/sql_select.h b/sql/sql_select.h
new file mode 100644
index 00000000000..8daba5b939e
--- /dev/null
+++ b/sql/sql_select.h
@@ -0,0 +1,291 @@
+/* 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 use when handling where clause */
+
+#ifdef __GNUC__
+#pragma interface /* gcc class implementation */
+#endif
+
+#include "procedure.h"
+#include <myisam.h>
+
+typedef struct keyuse_t {
+ TABLE *table;
+ Item *val; /* or value if no field */
+ uint key,keypart;
+ table_map used_tables;
+} KEYUSE;
+
+class store_key;
+
+typedef struct st_table_ref
+{
+ bool key_err;
+ uint key_parts; // num of ...
+ uint key_length; // length of key_buff
+ int key; // key no
+ byte *key_buff; // value to look for with key
+ byte *key_buff2; // key_buff+key_length
+ store_key **key_copy; //
+ Item **items; // val()'s for each keypart
+ table_map depend_map; // Table depends on these tables.
+} TABLE_REF;
+
+/*
+** CACHE_FIELD and JOIN_CACHE is used on full join to cache records in outer
+** table
+*/
+
+
+typedef struct st_cache_field {
+ char *str;
+ uint length,blob_length;
+ Field_blob *blob_field;
+ bool strip;
+} CACHE_FIELD;
+
+
+typedef struct st_join_cache {
+ uchar *buff,*pos,*end;
+ uint records,record_nr,ptr_record,fields,length,blobs;
+ CACHE_FIELD *field,**blob_ptr;
+ SQL_SELECT *select;
+} JOIN_CACHE;
+
+
+/*
+** The structs which holds the join connections and join states
+*/
+
+enum join_type { JT_UNKNOWN,JT_SYSTEM,JT_CONST,JT_EQ_REF,JT_REF,JT_MAYBE_REF,
+ JT_ALL, JT_RANGE, JT_NEXT, JT_FT};
+
+class JOIN;
+
+typedef struct st_join_table {
+ TABLE *table;
+ int (*read_first_record)(struct st_join_table *tab);
+ int (*next_select)(JOIN *,struct st_join_table *,bool);
+ bool cached_eq_ref_table,eq_ref_table;
+ READ_RECORD read_record;
+ uint keys; /* all keys with can be used */
+ key_map const_keys; /* Keys with constant part */
+ key_map checked_keys; /* Keys checked in find_best */
+ key_map needed_reg;
+ ha_rows records,found_records,read_time;
+ table_map dependent,key_dependent;
+ uint use_quick,index;
+ uint status; // Save status for cache
+ enum join_type type;
+ JOIN_CACHE cache;
+ KEYUSE *keyuse; /* pointer to first used key */
+ SQL_SELECT *select;
+ COND *select_cond;
+ QUICK_SELECT *quick;
+ Item *on_expr;
+ uint used_fields,used_fieldlength,used_blobs;
+ const char *info;
+ double worst_seeks;
+ TABLE_REF ref;
+} JOIN_TAB;
+
+
+typedef struct st_position { /* Used in find_best */
+ JOIN_TAB *table;
+ KEYUSE *key;
+ double records_read;
+} POSITION;
+
+
+/* Param to create temporary tables when doing SELECT:s */
+
+class TMP_TABLE_PARAM {
+ public:
+ uint copy_field_count,field_count,sum_func_count,func_count;
+ uint group_parts,group_length;
+ uint quick_group;
+ Copy_field *copy_field;
+ byte *group_buff;
+ ha_rows end_write_records;
+ Item_result_field **funcs;
+ List<Item> copy_funcs;
+ MI_COLUMNDEF *recinfo,*start_recinfo;
+ KEY *keyinfo;
+
+ TMP_TABLE_PARAM() :group_parts(0),group_length(0),copy_field(0) {}
+ ~TMP_TABLE_PARAM()
+ {
+ cleanup();
+ }
+ inline void cleanup(void)
+ {
+ delete [] copy_field;
+ copy_field=0;
+ }
+};
+
+
+class JOIN {
+ public:
+ JOIN_TAB *join_tab,**best_ref,**map2table;
+ TABLE **table,**all_tables,*sort_by_table;
+ 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;
+ ha_rows send_records;
+ POSITION positions[MAX_TABLES+1],best_positions[MAX_TABLES+1];
+ double best_read;
+ List<Item> *fields;
+ List<Item_buff> group_fields;
+ TABLE *tmp_table;
+ THD *thd;
+ Item_sum **sum_funcs;
+ Procedure *procedure;
+ Item *having;
+ uint select_options;
+ select_result *result;
+ TMP_TABLE_PARAM tmp_table_param;
+ MYSQL_LOCK *lock;
+};
+
+
+typedef struct st_select_check {
+ uint const_ref,reg_ref;
+} SELECT_CHECK;
+
+extern const char *join_type_str[];
+void TEST_join(JOIN *join);
+
+/* Extern functions in sql_select.cc */
+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);
+void free_tmp_table(THD *thd, TABLE *entry);
+void count_field_types(TMP_TABLE_PARAM *param, List<Item> &fields);
+bool setup_copy_fields(TMP_TABLE_PARAM *param,List<Item> &fields);
+void copy_fields(TMP_TABLE_PARAM *param);
+void copy_funcs(Item_result_field **func_ptr);
+bool create_myisam_from_heap(TABLE *table, TMP_TABLE_PARAM *param, int error,
+ bool ignore_last_dupp_error);
+
+/* functions from opt_sum.cc */
+int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds);
+
+
+/* class to copying an field/item to a key struct */
+
+class store_key :public Sql_alloc
+{
+ protected:
+ Field *to_field; // Store data here
+ Field *key_field; // Copy of key field
+ char *null_ptr;
+ char err;
+ public:
+ store_key(Field *field_arg, char *ptr, char *null, uint length)
+ :null_ptr(null),err(0)
+ {
+ if (field_arg->type() == FIELD_TYPE_BLOB)
+ to_field=new Field_varstring(ptr, length, (uchar*) null, 1,
+ Field::NONE, field_arg->field_name,
+ field_arg->table, field_arg->binary());
+ else
+ {
+ to_field=field_arg->new_field(field_arg->table);
+ if (to_field)
+ to_field->move_field(ptr, (uchar*) null, 1);
+ }
+ }
+ virtual ~store_key() {} /* Not actually needed */
+ virtual bool copy()=0;
+ virtual const char *name() const=0;
+};
+
+
+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,
+ uint length, Field *from_field, const char *name_arg)
+ :store_key(to_field_arg,ptr,
+ null_ptr_arg ? null_ptr_arg : from_field->maybe_null() ? &err
+ : NullS,length), field_name(name_arg)
+ {
+ if (to_field)
+ {
+ copy_field.set(to_field,from_field,0);
+ }
+ }
+ bool copy()
+ {
+ copy_field.do_copy(&copy_field);
+ return err != 0;
+ }
+ const char *name() const { return field_name; }
+};
+
+
+class store_key_item :public store_key
+{
+ protected:
+ Item *item;
+public:
+ store_key_item(Field *to_field_arg, char *ptr, char *null_ptr_arg,
+ uint length, Item *item_arg)
+ :store_key(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;
+ }
+ const char *name() const { return "func"; }
+};
+
+
+class store_key_const_item :public store_key_item
+{
+ bool inited;
+public:
+ store_key_const_item(Field *to_field_arg, char *ptr,
+ char *null_ptr_arg, uint length,
+ Item *item_arg)
+ :store_key_item(to_field_arg,ptr,
+ null_ptr_arg ? null_ptr_arg : item_arg->maybe_null ?
+ &err : NullS, length, item_arg), inited(0)
+ {
+ }
+ bool copy()
+ {
+ if (!inited)
+ {
+ inited=1;
+ item->save_in_field(to_field);
+ }
+ return err != 0;
+ }
+ const char *name() const { return "const"; }
+};
+
+bool cp_buffer_from_ref(TABLE_REF *ref);
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
new file mode 100644
index 00000000000..55142e21dad
--- /dev/null
+++ b/sql/sql_show.cc
@@ -0,0 +1,1032 @@
+/* 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 */
+
+
+/* Function with list databases, tables or fields */
+
+#undef USE_RAID
+#define USE_RAID
+#include "mysql_priv.h"
+#include "sql_select.h" // For select_describe
+#include "sql_acl.h"
+#include <my_dir.h>
+extern "C" pthread_mutex_t THR_LOCK_keycache;
+
+static const char *grant_names[]={
+ "select","insert","update","delete","create","drop","reload","shutdown",
+ "process","file","grant","references","index","alter"};
+
+static TYPELIB grant_types = { sizeof(grant_names)/sizeof(char **),
+ "grant_types",
+ grant_names};
+
+static int mysql_find_files(THD *thd,List<char> *files, const char *db,
+ const char *path, const char *wild, bool dir);
+
+static int
+store_create_info(THD *thd, TABLE *table, String* packet);
+
+/****************************************************************************
+** Send list of databases
+** A database is a directory in the mysql_data_home directory
+****************************************************************************/
+
+
+int
+mysqld_show_dbs(THD *thd,const char *wild)
+{
+ Item_string *field=new Item_string("",0);
+ List<Item> field_list;
+ char *end;
+ List<char> files;
+ char *file_name;
+ DBUG_ENTER("mysqld_show_dbs");
+
+ field->name=(char*) sql_alloc(20+ (wild ? strlen(wild)+4: 0));
+ field->max_length=NAME_LEN;
+ end=strmov(field->name,"Database");
+ if (wild && wild[0])
+ strxmov(end," (",wild,")",NullS);
+ field_list.push_back(field);
+
+ if (send_fields(thd,field_list,1))
+ DBUG_RETURN(1);
+ if (mysql_find_files(thd,&files,NullS,mysql_data_home,wild,1))
+ DBUG_RETURN(1);
+ List_iterator<char> it(files);
+ while ((file_name=it++))
+ {
+ thd->packet.length(0);
+ net_store_data(&thd->packet,file_name);
+ if (my_net_write(&thd->net,(char*) thd->packet.ptr(),thd->packet.length()))
+ DBUG_RETURN(-1);
+ }
+ send_eof(&thd->net);
+ DBUG_RETURN(0);
+}
+
+/***************************************************************************
+** List all tables in a database (fast version)
+** A table is a .frm file in the current databasedir
+***************************************************************************/
+
+int mysqld_show_tables(THD *thd,const char *db,const char *wild)
+{
+ Item_string *field=new Item_string("",0);
+ List<Item> field_list;
+ char path[FN_LEN],*end;
+ List<char> files;
+ char *file_name;
+ DBUG_ENTER("mysqld_show_tables");
+
+ field->name=(char*) sql_alloc(20+strlen(db)+(wild ? strlen(wild)+4:0));
+ end=strxmov(field->name,"Tables_in_",db,NullS);
+ if (wild && wild[0])
+ strxmov(end," (",wild,")",NullS);
+ field->max_length=NAME_LEN;
+ (void) sprintf(path,"%s/%s",mysql_data_home,db);
+ (void) unpack_dirname(path,path);
+ field_list.push_back(field);
+ if (send_fields(thd,field_list,1))
+ DBUG_RETURN(1);
+ if (mysql_find_files(thd,&files,db,path,wild,0))
+ DBUG_RETURN(-1);
+ List_iterator<char> it(files);
+ while ((file_name=it++))
+ {
+ thd->packet.length(0);
+ net_store_data(&thd->packet,file_name);
+ if (my_net_write(&thd->net,(char*) thd->packet.ptr(),thd->packet.length()))
+ DBUG_RETURN(-1);
+ }
+ send_eof(&thd->net);
+ DBUG_RETURN(0);
+}
+
+
+static int
+mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path,
+ const char *wild, bool dir)
+{
+ uint i;
+ char *ext;
+ MY_DIR *dirp;
+ FILEINFO *file;
+ uint col_access=thd->col_access;
+ TABLE_LIST table_list;
+ DBUG_ENTER("mysql_find_files");
+
+ bzero((char*) &table_list,sizeof(table_list));
+
+ if (!(dirp = my_dir(path,MYF(MY_WME | (dir ? MY_WANT_STAT : 0)))))
+ DBUG_RETURN(-1);
+
+ for (i=0 ; i < (uint) dirp->number_off_files ; i++)
+ {
+ file=dirp->dir_entry+i;
+ if (dir)
+ { /* Return databases */
+#ifdef USE_SYMDIR
+ char *ext;
+ if (my_use_symdir && !strcmp(ext=fn_ext(file->name), ".sym"))
+ *ext=0; /* Remove extension */
+ else
+#endif
+ {
+ if (file->name[0] == '.' || !MY_S_ISDIR(file->mystat.st_mode) ||
+ (wild && wild[0] && wild_compare(file->name,wild)))
+ continue;
+ }
+ }
+ else
+ {
+ // Return only .frm files which isn't temp files.
+ if (my_strcasecmp(ext=fn_ext(file->name),reg_ext) ||
+ is_prefix(file->name,tmp_file_prefix)) // Mysql temp table
+ continue;
+ *ext=0;
+ if (wild && wild[0] && wild_compare(file->name,wild))
+ continue;
+ }
+ /* Don't show tables where we don't have any privileges */
+ if (db && !(col_access & TABLE_ACLS))
+ {
+ table_list.db= (char*) db;
+ table_list.real_name=file->name;
+ table_list.grant.privilege=col_access;
+ if (check_grant(thd,TABLE_ACLS,&table_list,1))
+ continue;
+ }
+ if (files->push_back(sql_strdup(file->name)))
+ {
+ my_dirend(dirp);
+ DBUG_RETURN(-1);
+ }
+ }
+ DBUG_PRINT("info",("found: %d files", files->elements));
+ my_dirend(dirp);
+ DBUG_RETURN(0);
+}
+
+/***************************************************************************
+** Extended version of mysqld_show_tables
+***************************************************************************/
+
+int mysqld_extend_show_tables(THD *thd,const char *db,const char *wild)
+{
+ Item *item;
+ List<char> files;
+ List<Item> field_list;
+ char path[FN_LEN];
+ char *file_name;
+ TABLE *table;
+ String *packet= &thd->packet;
+ DBUG_ENTER("mysqld_extend_show_tables");
+
+ (void) sprintf(path,"%s/%s",mysql_data_home,db);
+ (void) unpack_dirname(path,path);
+
+ field_list.push_back(item=new Item_empty_string("Name",NAME_LEN));
+ item->maybe_null=1;
+ field_list.push_back(item=new Item_empty_string("Type",10));
+ item->maybe_null=1;
+ field_list.push_back(item=new Item_empty_string("Row_format",10));
+ item->maybe_null=1;
+ field_list.push_back(item=new Item_int("Rows",(longlong) 1,21));
+ item->maybe_null=1;
+ field_list.push_back(item=new Item_int("Avg_row_length",(int32) 0,21));
+ item->maybe_null=1;
+ field_list.push_back(item=new Item_int("Data_length",(longlong) 1,21));
+ item->maybe_null=1;
+ field_list.push_back(item=new Item_int("Max_data_length",(longlong) 1,21));
+ item->maybe_null=1;
+ field_list.push_back(item=new Item_int("Index_length",(longlong) 1,21));
+ item->maybe_null=1;
+ field_list.push_back(item=new Item_int("Data_free",(longlong) 1,21));
+ item->maybe_null=1;
+ field_list.push_back(item=new Item_int("Auto_increment",(longlong) 1,21));
+ item->maybe_null=1;
+ field_list.push_back(item=new Item_datetime("Create_time"));
+ item->maybe_null=1;
+ field_list.push_back(item=new Item_datetime("Update_time"));
+ item->maybe_null=1;
+ field_list.push_back(item=new Item_datetime("Check_time"));
+ item->maybe_null=1;
+ field_list.push_back(item=new Item_empty_string("Create_options",255));
+ item->maybe_null=1;
+ field_list.push_back(item=new Item_empty_string("Comment",80));
+ if (send_fields(thd,field_list,1))
+ DBUG_RETURN(1);
+
+ if (mysql_find_files(thd,&files,db,path,wild,0))
+ DBUG_RETURN(-1);
+ List_iterator<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);
+ table_list.db=(char*) db;
+ table_list.real_name=table_list.name=file_name;
+ if (!(table = open_ltable(thd, &table_list, TL_READ)))
+ {
+ for (uint i=0 ; i < field_list.elements ; i++)
+ net_store_null(packet);
+ net_store_data(packet,thd->net.last_error);
+ thd->net.last_error[0]=0;
+ }
+ else
+ {
+ 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,
+ (table->db_options_in_use & HA_OPTION_PACK_RECORD) ?
+ "Dynamic" :
+ (table->db_options_in_use & HA_OPTION_COMPRESS_RECORD)
+ ? "Compressed" : "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);
+ if (file->max_data_file_length)
+ net_store_data(packet, (longlong) file->max_data_file_length);
+ else
+ net_store_null(packet);
+ net_store_data(packet, (longlong) file->index_file_length);
+ net_store_data(packet, (longlong) file->delete_length);
+ if (table->found_next_number_field)
+ {
+ table->next_number_field=table->found_next_number_field;
+ table->next_number_field->reset();
+ file->update_auto_increment();
+ net_store_data(packet, table->next_number_field->val_int());
+ table->next_number_field=0;
+ }
+ else
+ net_store_null(packet);
+ if (!file->create_time)
+ net_store_null(packet);
+ else
+ {
+ localtime_r(&file->create_time,&tm_tmp);
+ net_store_data(packet, &tm_tmp);
+ }
+ if (!file->update_time)
+ net_store_null(packet);
+ else
+ {
+ localtime_r(&file->update_time,&tm_tmp);
+ net_store_data(packet, &tm_tmp);
+ }
+ if (!file->check_time)
+ net_store_null(packet);
+ else
+ {
+ localtime_r(&file->check_time,&tm_tmp);
+ net_store_data(packet, &tm_tmp);
+ }
+ {
+ char option_buff[350],*ptr;
+ ptr=option_buff;
+ if (table->min_rows)
+ {
+ ptr=strmov(ptr," min_rows=");
+ ptr=longlong10_to_str(table->min_rows,ptr,10);
+ }
+ if (table->max_rows)
+ {
+ ptr=strmov(ptr," max_rows=");
+ ptr=longlong10_to_str(table->max_rows,ptr,10);
+ }
+ if (table->avg_row_length)
+ {
+ ptr=strmov(ptr," avg_row_length=");
+ ptr=longlong10_to_str(table->avg_row_length,ptr,10);
+ }
+ if (table->db_create_options & HA_OPTION_PACK_KEYS)
+ ptr=strmov(ptr," pack_keys=1");
+ if (table->db_create_options & HA_OPTION_NO_PACK_KEYS)
+ ptr=strmov(ptr," pack_keys=0");
+ if (table->db_create_options & HA_OPTION_CHECKSUM)
+ ptr=strmov(ptr," checksum=1");
+ if (table->db_create_options & HA_OPTION_DELAY_KEY_WRITE)
+ ptr=strmov(ptr," delay_key_write=1");
+ if (table->row_type != ROW_TYPE_DEFAULT)
+ ptr=strxmov(ptr, " format=", ha_row_type[(uint) table->row_type],
+ NullS);
+ if (file->raid_type)
+ {
+ char buff[100];
+ sprintf(buff," raid_type=%s raid_chunks=%d raid_chunksize=%ld",
+ 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,
+ (ptr == option_buff ? 0 : (uint) (ptr-option_buff)-1));
+ }
+ net_store_data(packet, table->comment);
+
+ close_thread_tables(thd,0);
+ }
+ if (my_net_write(&thd->net,(char*) packet->ptr(),
+ packet->length()))
+ DBUG_RETURN(-1);
+ }
+ send_eof(&thd->net);
+ DBUG_RETURN(0);
+}
+
+
+
+/***************************************************************************
+** List all columns in a table
+***************************************************************************/
+
+int
+mysqld_show_fields(THD *thd, TABLE_LIST *table_list,const char *wild)
+{
+ TABLE *table;
+ handler *file;
+ char tmp[MAX_FIELD_WIDTH];
+ DBUG_ENTER("mysqld_show_fields");
+ DBUG_PRINT("enter",("db: %s table: %s",table_list->db,
+ table_list->real_name));
+
+ if (!(table = open_ltable(thd, table_list, TL_UNLOCK)))
+ {
+ send_error(&thd->net);
+ DBUG_RETURN(1);
+ }
+ file=table->file;
+ file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
+ (void) get_table_grant(thd, table_list);
+
+ List<Item> field_list;
+ field_list.push_back(new Item_empty_string("Field",NAME_LEN));
+ field_list.push_back(new Item_empty_string("Type",40));
+ field_list.push_back(new Item_empty_string("Null",1));
+ field_list.push_back(new Item_empty_string("Key",3));
+ field_list.push_back(new Item_empty_string("Default",NAME_LEN));
+ field_list.push_back(new Item_empty_string("Extra",20));
+ field_list.push_back(new Item_empty_string("Privileges",80));
+
+ // Send first number of fields and records
+ {
+ char *pos;
+ pos=net_store_length(tmp, (uint) field_list.elements);
+ pos=net_store_length(pos,(ulonglong) file->records);
+ (void) my_net_write(&thd->net,tmp,(uint) (pos-tmp));
+ }
+
+ if (send_fields(thd,field_list,0))
+ DBUG_RETURN(1);
+ restore_record(table,2); // Get empty record
+
+ Field **ptr,*field;
+ for (ptr=table->field; (field= *ptr) ; ptr++)
+ {
+ if (!wild || !wild[0] || !wild_case_compare(field->field_name,wild))
+ {
+#ifdef NOT_USED
+ if (thd->col_access & TABLE_ACLS ||
+ ! check_grant_column(thd,table,field->field_name,
+ strlen(field->field_name),1))
+#endif
+ {
+ byte *pos;
+ uint flags=field->flags;
+ String *packet= &thd->packet,type(tmp,sizeof(tmp));
+ uint col_access;
+ bool null_default_value=0;
+
+ packet->length(0);
+ net_store_data(packet,field->field_name);
+ field->sql_type(type);
+ net_store_data(packet,type.ptr(),type.length());
+
+ pos=(byte*) ((flags & NOT_NULL_FLAG) &&
+ field->type() != FIELD_TYPE_TIMESTAMP ?
+ "" : "YES");
+ net_store_data(packet,(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);
+
+ if (field->type() == FIELD_TYPE_TIMESTAMP ||
+ field->unireg_check == Field::NEXT_NUMBER)
+ null_default_value=1;
+ if (!null_default_value && !field->is_null())
+ { // Not null by default
+ type.set(tmp,sizeof(tmp));
+ field->val_str(&type,&type);
+ net_store_data(packet,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);
+
+ char *end=tmp;
+ if (field->unireg_check == Field::NEXT_NUMBER)
+ end=strmov(tmp,"auto_increment");
+ net_store_data(packet,tmp,(uint) (end-tmp));
+
+ /* Add grant options */
+ col_access= get_column_grant(thd,table_list,field) & COL_ACLS;
+ end=tmp;
+ for (uint bitnr=0; col_access ; col_access>>=1,bitnr++)
+ {
+ if (col_access & 1)
+ {
+ *end++=',';
+ end=strmov(end,grant_types.type_names[bitnr]);
+ }
+ }
+ net_store_data(packet,tmp+1,end == tmp ? 0 : (uint) (end-tmp-1));
+ if (my_net_write(&thd->net,(char*) packet->ptr(),packet->length()))
+ DBUG_RETURN(1);
+ }
+ }
+ }
+ send_eof(&thd->net);
+ DBUG_RETURN(0);
+}
+
+int
+mysqld_show_create(THD *thd, TABLE_LIST *table_list)
+{
+ TABLE *table;
+ DBUG_ENTER("mysqld_show_create");
+ DBUG_PRINT("enter",("db: %s table: %s",table_list->db,
+ table_list->real_name));
+
+ if (!(table = open_ltable(thd, table_list, TL_UNLOCK)))
+ {
+ send_error(&thd->net);
+ DBUG_RETURN(1);
+ }
+
+ List<Item> field_list;
+ field_list.push_back(new Item_empty_string("Table",NAME_LEN));
+ field_list.push_back(new Item_empty_string("Create Table",1024));
+
+ if (send_fields(thd,field_list,1))
+ DBUG_RETURN(1);
+
+ String *packet = &thd->packet;
+ for(;table; table = table->next)
+ {
+ packet->length(0);
+ net_store_data(packet, 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
+ // statement will fit into 3 bytes ( 16 MB max :-) )
+ ulong store_len_offset = packet->length();
+ packet->length(store_len_offset + 4);
+ if(store_create_info(thd, table, packet))
+ DBUG_RETURN(-1);
+ ulong create_len = packet->length() - store_len_offset - 4;
+ if(create_len > 0x00ffffff) // better readable in HEX ...
+ DBUG_RETURN(1); // just in case somebody manages to create a table
+ // with *that* much stuff in the definition
+
+ // now we have to store the length in three bytes, even if it would fit
+ // into fewer, so we cannot use net_store_data() anymore,
+ // and do it ourselves
+ char* p = (char*)packet->ptr() + store_len_offset;
+ *p++ = (char) 253; // The client the length is stored using 3-bytes
+ int3store(p, create_len);
+
+ // now we are in business :-)
+ if(my_net_write(&thd->net, (char*)packet->ptr(), packet->length()))
+ DBUG_RETURN(1);
+ }
+
+ send_eof(&thd->net);
+ DBUG_RETURN(0);
+}
+
+
+int
+mysqld_show_keys(THD *thd, TABLE_LIST *table_list)
+{
+ TABLE *table;
+ char buff[256];
+ DBUG_ENTER("mysqld_show_keys");
+ DBUG_PRINT("enter",("db: %s table: %s",table_list->db,
+ table_list->real_name));
+
+ if (!(table = open_ltable(thd, table_list, TL_UNLOCK)))
+ {
+ send_error(&thd->net);
+ DBUG_RETURN(1);
+ }
+
+ List<Item> field_list;
+ Item *item;
+ field_list.push_back(new Item_empty_string("Table",NAME_LEN));
+ field_list.push_back(new Item_int("Non_unique",0,1));
+ field_list.push_back(new Item_empty_string("Key_name",NAME_LEN));
+ field_list.push_back(new Item_int("Seq_in_index",0,2));
+ 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));
+ 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("Comment",255));
+ item->maybe_null=1;
+
+ if (send_fields(thd,field_list,1))
+ DBUG_RETURN(1);
+
+ KEY *key_info=table->key_info;
+ table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK | HA_STATUS_TIME);
+ for (uint i=0 ; i < table->keys ; i++,key_info++)
+ {
+ KEY_PART_INFO *key_part= key_info->key_part;
+ char *end;
+ String *packet= &thd->packet;
+ 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);
+ 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 :
+ "?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);
+ 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));
+ }
+ else
+ net_store_null(packet);
+ 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 */
+ }
+ else
+ net_store_null(packet);
+ net_store_null(packet); // No pack_information yet
+ net_store_null(packet); // No comments yet
+ if (my_net_write(&thd->net,(char*) packet->ptr(),packet->length()))
+ DBUG_RETURN(1); /* purecov: inspected */
+ }
+ }
+ send_eof(&thd->net);
+ DBUG_RETURN(0);
+}
+
+
+/****************************************************************************
+** Return only fields for API mysql_list_fields
+** Use "show table wildcard" in mysql instead of this
+****************************************************************************/
+
+void
+mysqld_list_fields(THD *thd, TABLE_LIST *table_list, const char *wild)
+{
+ TABLE *table;
+ DBUG_ENTER("mysqld_list_fields");
+ DBUG_PRINT("enter",("table: %s",table_list->real_name));
+
+ if (!(table = open_ltable(thd, table_list, TL_UNLOCK)))
+ {
+ send_error(&thd->net);
+ DBUG_VOID_RETURN;
+ }
+ List<Item> field_list;
+
+ Field **ptr,*field;
+ for (ptr=table->field ; (field= *ptr); ptr++)
+ {
+ if (!wild || !wild[0] || !wild_case_compare(field->field_name,wild))
+ field_list.push_back(new Item_field(field));
+ }
+ restore_record(table,2); // Get empty record
+ if (send_fields(thd,field_list,2))
+ DBUG_VOID_RETURN;
+ VOID(net_flush(&thd->net));
+ DBUG_VOID_RETURN;
+}
+
+int
+mysqld_dump_create_info(THD *thd, TABLE *table, int fd)
+{
+ 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))
+ DBUG_RETURN(-1);
+
+ if(fd < 0)
+ {
+ 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)))
+ DBUG_RETURN(-1);
+ }
+
+ DBUG_RETURN(0);
+}
+
+static int
+store_create_info(THD *thd, TABLE *table, String* packet)
+{
+ DBUG_ENTER("store_create_info");
+ DBUG_PRINT("enter",("table: %s",table->real_name));
+
+ restore_record(table,2); // Get empty record
+
+ List<Item> field_list;
+ char tmp[MAX_FIELD_WIDTH];
+ String type(tmp, sizeof(tmp));
+ packet->append("create table ", 13);
+ packet->append(table->real_name);
+ packet->append('(');
+
+ Field **ptr,*field;
+ for (ptr=table->field ; (field= *ptr); ptr++)
+ {
+ if(ptr != table->field)
+ packet->append(',');
+
+ uint flags = field->flags;
+ packet->append(field->field_name);
+ packet->append(' ');
+ // check for surprises from the previous call to Field::sql_type()
+ if(type.ptr() != tmp)
+ type.set(tmp, sizeof(tmp));
+
+ field->sql_type(type);
+ packet->append(type.ptr(),type.length());
+
+ bool null_default_value = (field->type() == FIELD_TYPE_TIMESTAMP ||
+ field->unireg_check == Field::NEXT_NUMBER);
+ bool has_default = (field->type() != FIELD_TYPE_BLOB);
+
+ if((flags & NOT_NULL_FLAG) && !null_default_value)
+ packet->append(" not null", 9);
+
+
+ if(has_default)
+ {
+ packet->append(" default ", 9);
+ if (!null_default_value && !field->is_null())
+ { // Not null by default
+ type.set(tmp,sizeof(tmp));
+ field->val_str(&type,&type);
+ packet->append('\'');
+ packet->append(type.ptr(),type.length());
+ packet->append('\'');
+ }
+ else if (field->maybe_null() || null_default_value)
+ packet->append("NULL", 4); // Null as default
+ else
+ packet->append(tmp,0);
+ }
+
+ if (field->unireg_check == Field::NEXT_NUMBER)
+ packet->append(" auto_increment", 15 );
+
+
+ }
+
+ KEY *key_info=table->key_info;
+ table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK | HA_STATUS_TIME);
+ uint primary_key = table->primary_key;
+
+ for (uint i=0 ; i < table->keys ; i++,key_info++)
+ {
+ packet->append(',');
+
+ KEY_PART_INFO *key_part= key_info->key_part;
+ if(i == primary_key)
+ packet->append("primary", 7);
+ else if(key_info->flags & HA_NOSAME)
+ packet->append("unique", 6);
+ packet->append(" key ", 5);
+
+ if(i != primary_key)
+ packet->append(key_info->name);
+
+ packet->append('(');
+
+ for (uint j=0 ; j < key_info->key_parts ; j++,key_part++)
+ {
+ if(j)
+ packet->append(',');
+
+ if(key_part->field)
+ packet->append(key_part->field->field_name);
+ KEY *key=table->key_info+i;
+
+ if (!key_part->field ||
+ key_part->length !=
+ table->field[key_part->fieldnr-1]->key_length())
+ {
+ char buff[64];
+ buff[0] = '(';
+ char* end=int10_to_str((long) key_part->length, buff + 1,10);
+ *end++ = ')';
+ packet->append(buff,(uint) (end-buff));
+ }
+ }
+
+ packet->append(')');
+ }
+
+ packet->append(')');
+
+ handler *file = table->file;
+ packet->append(" type=", 6);
+ packet->append(file->table_type());
+ char buff[128];
+ char* p;
+
+ if(table->min_rows)
+ {
+ packet->append(" min_rows=");
+ p = longlong10_to_str(table->min_rows, buff, 10);
+ packet->append(buff, (uint) (p - buff));
+ }
+
+ if(table->max_rows)
+ {
+ packet->append(" max_rows=");
+ p = longlong10_to_str(table->max_rows, buff, 10);
+ packet->append(buff, (uint) (p - buff));
+ }
+
+ if (table->db_create_options & HA_OPTION_PACK_KEYS)
+ packet->append(" pack_keys=1", 12);
+ if (table->db_create_options & HA_OPTION_NO_PACK_KEYS)
+ packet->append(" pack_keys=0", 12);
+ if (table->db_create_options & HA_OPTION_CHECKSUM)
+ packet->append(" checksum=1", 11);
+ if (table->db_create_options & HA_OPTION_DELAY_KEY_WRITE)
+ packet->append(" delay_key_write=1",18);
+
+
+ DBUG_RETURN(0);
+}
+
+
+/****************************************************************************
+** Return info about all processes
+** returns for each thread: thread id, user, host, db, command, info
+****************************************************************************/
+
+class thread_info :public ilink {
+public:
+ static void *operator new(size_t size) {return (void*) sql_alloc(size); }
+ static void operator delete(void *ptr __attribute__((unused)),
+ size_t size __attribute__((unused))) {} /*lint -e715 */
+
+ ulong thread_id,start_time;
+ uint command;
+ const char *user,*host,*db,*proc_info,*state_info;
+ char *query;
+};
+
+#ifdef __GNUC__
+template class I_List<thread_info>;
+#endif
+
+
+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;
+ 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(field=new Item_empty_string("db",NAME_LEN));
+ field->maybe_null=1;
+ field_list.push_back(new Item_empty_string("Command",16));
+ field_list.push_back(new Item_empty_string("Time",7));
+ field_list.push_back(field=new Item_empty_string("State",30));
+ field->maybe_null=1;
+ field_list.push_back(field=new Item_empty_string("Info",max_query_length));
+ field->maybe_null=1;
+ if (send_fields(thd,field_list,1))
+ DBUG_VOID_RETURN;
+
+ VOID(pthread_mutex_lock(&LOCK_thread_count)); // For unlink from list
+ if (!thd->killed)
+ {
+ I_List_iterator<THD> it(threads);
+ THD *tmp;
+ while ((tmp=it++))
+ {
+ if ((tmp->net.vio || tmp->system_thread) &&
+ (!user || (tmp->user && !strcmp(tmp->user,user))))
+ {
+ thread_info *thd_info=new thread_info;
+
+ thd_info->thread_id=tmp->thread_id;
+ thd_info->user=sql_strdup(tmp->user ? tmp->user : (tmp->system_thread ?
+ "system user" : "unauthenticated user"));
+ thd_info->host=sql_strdup(tmp->host ? tmp->host : (tmp->ip ? tmp->ip :
+ (tmp->system_thread ? "none" : "connecting host")));
+ if ((thd_info->db=tmp->db)) // Safe test
+ thd_info->db=sql_strdup(thd_info->db);
+ thd_info->command=(int) tmp->command;
+ if (tmp->mysys_var)
+ pthread_mutex_lock(&tmp->mysys_var->mutex);
+ thd_info->proc_info= (char*) (tmp->killed ? "Killed" : 0);
+ thd_info->state_info= (char*) (tmp->locked ? "Locked" :
+ tmp->net.reading_or_writing ?
+ (tmp->net.reading_or_writing == 2 ?
+ "Writing to net" :
+ thd_info->command == COM_SLEEP ? "" :
+ "Reading from net") :
+ tmp->proc_info ? tmp->proc_info :
+ tmp->mysys_var &&
+ tmp->mysys_var->current_cond ?
+ "Waiting on cond" : NullS);
+ if (tmp->mysys_var)
+ pthread_mutex_unlock(&tmp->mysys_var->mutex);
+
+#if !defined(DONT_USE_THR_ALARM) && ! defined(SCO)
+ if (pthread_kill(tmp->real_id,0))
+ tmp->proc_info="*** DEAD ***"; // This shouldn't happen
+#endif
+ thd_info->start_time= tmp->start_time;
+ thd_info->query=0;
+ if (tmp->query)
+ {
+ uint length=strlen(tmp->query);
+ if (length > max_query_length)
+ length=max_query_length;
+ thd_info->query=(char*) sql_memdup(tmp->query,length+1);
+ thd_info->query[length]=0;
+ }
+ thread_infos.append(thd_info);
+ }
+ }
+ }
+ VOID(pthread_mutex_unlock(&LOCK_thread_count));
+
+ thread_info *thd_info;
+ String *packet= &thd->packet;
+ while ((thd_info=thread_infos.get()))
+ {
+ 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);
+ if (thd_info->db)
+ net_store_data(packet,thd_info->db);
+ else
+ net_store_null(packet);
+ if (thd_info->proc_info)
+ net_store_data(packet,thd_info->proc_info);
+ else
+ net_store_data(packet,command_name[thd_info->command]);
+ if (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);
+ else
+ net_store_null(packet);
+ if (thd_info->query)
+ net_store_data(packet,thd_info->query);
+ else
+ net_store_null(packet);
+ if (my_net_write(&thd->net,(char*) packet->ptr(),packet->length()))
+ break; /* purecov: inspected */
+ }
+ send_eof(&thd->net);
+ DBUG_VOID_RETURN;
+}
+
+
+/*****************************************************************************
+** Status functions
+*****************************************************************************/
+
+
+int mysqld_show(THD *thd, const char *wild, show_var_st *variables)
+{
+ uint i;
+ char buff[8192];
+ String packet2(buff,sizeof(buff));
+ List<Item> field_list;
+ 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));
+ if (send_fields(thd,field_list,1))
+ DBUG_RETURN(1); /* purecov: inspected */
+
+ pthread_mutex_lock(&THR_LOCK_keycache);
+ pthread_mutex_lock(&LOCK_status);
+ for (i=0; variables[i].name; i++)
+ {
+ if (!(wild && wild[0] && wild_compare(variables[i].name,wild)))
+ {
+ packet2.length(0);
+ net_store_data(&packet2,variables[i].name);
+ switch (variables[i].type){
+ case SHOW_LONG:
+ case SHOW_LONG_CONST:
+ net_store_data(&packet2,(uint32) *(ulong*) variables[i].value);
+ break;
+ case SHOW_BOOL:
+ net_store_data(&packet2,(ulong) *(bool*) variables[i].value ?
+ "ON" : "OFF");
+ break;
+ case SHOW_MY_BOOL:
+ net_store_data(&packet2,(ulong) *(my_bool*) variables[i].value ?
+ "ON" : "OFF");
+ break;
+ case SHOW_INT_CONST:
+ case SHOW_INT:
+ net_store_data(&packet2,(uint32) *(int*) variables[i].value);
+ break;
+ case SHOW_CHAR:
+ net_store_data(&packet2,variables[i].value);
+ break;
+ case SHOW_STARTTIME:
+ net_store_data(&packet2,(uint32) (thd->query_start() - start_time));
+ break;
+ case SHOW_QUESTION:
+ net_store_data(&packet2,(uint32) thd->query_id);
+ break;
+ 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;
+ }
+ }
+ if (my_net_write(&thd->net, (char*) packet2.ptr(),packet2.length()))
+ goto err; /* purecov: inspected */
+ }
+ }
+ pthread_mutex_unlock(&LOCK_status);
+ pthread_mutex_unlock(&THR_LOCK_keycache);
+ send_eof(&thd->net);
+ DBUG_RETURN(0);
+
+ err:
+ pthread_mutex_unlock(&LOCK_status);
+ pthread_mutex_unlock(&THR_LOCK_keycache);
+ DBUG_RETURN(1);
+}
+
+#ifdef __GNUC__
+template class List_iterator<char>;
+template class List<char>;
+#endif
diff --git a/sql/sql_string.cc b/sql/sql_string.cc
new file mode 100644
index 00000000000..c18f9e3807e
--- /dev/null
+++ b/sql/sql_string.cc
@@ -0,0 +1,741 @@
+/* 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 */
+
+/* This file is originally from the mysql distribution. Coded by monty */
+
+#ifdef __GNUC__
+#pragma implementation // gcc: Class implementation
+#endif
+
+#include <global.h>
+#include <my_sys.h>
+#include <m_string.h>
+#include <m_ctype.h>
+#ifdef HAVE_FCONVERT
+#include <floatingpoint.h>
+#endif
+
+extern gptr sql_alloc(unsigned size);
+extern void sql_element_free(void *ptr);
+
+#include "sql_string.h"
+
+/*****************************************************************************
+** String functions
+*****************************************************************************/
+
+bool String::real_alloc(uint32 arg_length)
+{
+ arg_length=ALIGN_SIZE(arg_length+1);
+ 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;
+}
+
+
+/*
+** Check that string is big enough. Set string[alloc_length] to 0
+** (for C functions)
+*/
+
+bool String::realloc(uint32 alloc_length)
+{
+ uint32 len=ALIGN_SIZE(alloc_length+1);
+ if (Alloced_length < len)
+ {
+ char *new_ptr;
+ if (alloced)
+ {
+ if ((new_ptr= (char*) my_realloc(Ptr,len,MYF(MY_WME))))
+ {
+ Ptr=new_ptr;
+ Alloced_length=len;
+ }
+ else
+ return TRUE; // Signal error
+ }
+ else if ((new_ptr= (char*) my_malloc(len,MYF(MY_WME))))
+ {
+ memcpy(new_ptr,Ptr,str_length);
+ new_ptr[str_length]=0;
+ Ptr=new_ptr;
+ Alloced_length=len;
+ alloced=1;
+ }
+ else
+ return TRUE; // Signal error
+ }
+ Ptr[alloc_length]=0; // This make other funcs shorter
+ return FALSE;
+}
+
+
+#ifdef NOT_NEEDED
+bool String::set(long num)
+{
+ if (alloc(14))
+ return TRUE;
+ str_length=(uint32) (int10_to_str(num,Ptr,-10)-Ptr);
+ return FALSE;
+}
+#endif
+
+bool String::set(longlong num)
+{
+ if (alloc(21))
+ return TRUE;
+ str_length=(uint32) (longlong10_to_str(num,Ptr,-10)-Ptr);
+ return FALSE;
+}
+
+bool String::set(ulonglong num)
+{
+ if (alloc(21))
+ return TRUE;
+ str_length=(uint32) (longlong10_to_str(num,Ptr,10)-Ptr);
+ return FALSE;
+}
+
+bool String::set(double num,uint decimals)
+{
+ char buff[331];
+ if (decimals >= NOT_FIXED_DEC)
+ {
+ sprintf(buff,"%.14g",num); // Enough for a DATETIME
+ return copy(buff,strlen(buff));
+ }
+#ifdef HAVE_FCONVERT
+ int decpt,sign;
+ char *pos,*to;
+
+ VOID(fconvert(num,(int) decimals,&decpt,&sign,buff+1));
+ if (!isdigit(buff[1]))
+ { // Nan or Inf
+ pos=buff+1;
+ if (sign)
+ {
+ buff[0]='-';
+ pos=buff;
+ }
+ return copy(pos,strlen(pos));
+ }
+ if (alloc((uint32) ((uint32) decpt+3+decimals)))
+ return TRUE;
+ to=Ptr;
+ if (sign)
+ *to++='-';
+
+ pos=buff+1;
+ if (decpt < 0)
+ { /* value is < 0 */
+ *to++='0';
+ if (!decimals)
+ goto end;
+ *to++='.';
+ if ((uint32) -decpt > decimals)
+ decpt= - (int) decimals;
+ decimals=(uint32) ((int) decimals+decpt);
+ while (decpt++ < 0)
+ *to++='0';
+ }
+ else if (decpt == 0)
+ {
+ *to++= '0';
+ if (!decimals)
+ goto end;
+ *to++='.';
+ }
+ else
+ {
+ while (decpt-- > 0)
+ *to++= *pos++;
+ if (!decimals)
+ goto end;
+ *to++='.';
+ }
+ while (decimals--)
+ *to++= *pos++;
+
+end:
+ *to=0;
+ str_length=(uint32) (to-Ptr);
+ return FALSE;
+#else
+#ifdef HAVE_SNPRINTF_
+ snprintf(buff,sizeof(buff), "%.*f",(int) decimals,num);
+#else
+ sprintf(buff,"%.*f",(int) decimals,num);
+#endif
+ return copy(buff,strlen(buff));
+#endif
+}
+
+
+bool String::copy()
+{
+ if (!alloced)
+ {
+ Alloced_length=0; // Force realloc
+ return realloc(str_length);
+ }
+ return FALSE;
+}
+
+bool String::copy(const String &str)
+{
+ if (alloc(str.str_length))
+ return TRUE;
+ str_length=str.str_length;
+ bmove(Ptr,str.Ptr,str_length); // May be overlapping
+ Ptr[str_length]=0;
+ return FALSE;
+}
+
+bool String::copy(const char *str,uint32 arg_length)
+{
+ if (alloc(arg_length))
+ return TRUE;
+ str_length=arg_length;
+ memcpy(Ptr,str,arg_length);
+ Ptr[arg_length]=0;
+ return FALSE;
+}
+
+/* This is used by mysql.cc */
+
+bool String::fill(uint32 max_length,char fill_char)
+{
+ if (str_length > max_length)
+ Ptr[str_length=max_length]=0;
+ else
+ {
+ if (realloc(max_length))
+ return TRUE;
+ bfill(Ptr+str_length,max_length-str_length,fill_char);
+ str_length=max_length;
+ }
+ return FALSE;
+}
+
+void String::strip_sp()
+{
+ while (str_length && isspace(Ptr[str_length-1]))
+ str_length--;
+}
+
+bool String::append(const String &s)
+{
+ if (realloc(str_length+s.length()))
+ return TRUE;
+ memcpy(Ptr+str_length,s.ptr(),s.length());
+ str_length+=s.length();
+ return FALSE;
+}
+
+bool String::append(const char *s,uint32 arg_length)
+{
+ if (!arg_length) // Default argument
+ arg_length=strlen(s);
+ if (realloc(str_length+arg_length))
+ return TRUE;
+ memcpy(Ptr+str_length,s,arg_length);
+ str_length+=arg_length;
+ return FALSE;
+}
+
+bool String::append(FILE* file, uint32 arg_length, myf my_flags)
+{
+ if (realloc(str_length+arg_length))
+ return TRUE;
+ if (my_fread(file, (byte*) Ptr + str_length, arg_length, my_flags))
+ {
+ shrink(str_length);
+ return TRUE;
+ }
+ str_length+=arg_length;
+ return FALSE;
+}
+
+uint32 String::numchars()
+{
+#ifdef USE_MB
+ register uint32 n=0,mblen;
+ register const char *mbstr=Ptr;
+ register const char *end=mbstr+str_length;
+ if (use_mb(default_charset_info))
+ {
+ while (mbstr < end) {
+ if ((mblen=my_ismbchar(default_charset_info, mbstr,end))) mbstr+=mblen;
+ else ++mbstr;
+ ++n;
+ }
+ return n;
+ }
+ else
+#endif
+ return str_length;
+}
+
+int String::charpos(int i,uint32 offset)
+{
+#ifdef USE_MB
+ register uint32 mblen;
+ register const char *mbstr=Ptr+offset;
+ register const char *end=Ptr+str_length;
+ if (use_mb(default_charset_info))
+ {
+ if (i<=0) return i;
+ while (i && mbstr < end) {
+ if ((mblen=my_ismbchar(default_charset_info, mbstr,end))) mbstr+=mblen;
+ else ++mbstr;
+ --i;
+ }
+ if ( INT_MAX32-i <= (int) (mbstr-Ptr-offset))
+ return INT_MAX32;
+ else
+ return (mbstr-Ptr-offset)+i;
+ }
+ else
+#endif
+ return i;
+}
+
+int String::strstr(const String &s,uint32 offset)
+{
+ if (s.length()+offset <= str_length)
+ {
+ if (!s.length())
+ return 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 (*str++ == *search)
+ {
+ register char *i,*j;
+ i=(char*) str; j=(char*) search+1;
+ while (j != search_end)
+ if (*i++ != *j++) goto skipp;
+ return (int) (str-Ptr) -1;
+ }
+ }
+ }
+ return -1;
+}
+
+
+/*
+** Search string from end. Offset is offset to the end of string
+*/
+
+int String::strrstr(const String &s,uint32 offset)
+{
+ if (s.length() <= offset && offset <= str_length)
+ {
+ if (!s.length())
+ return offset; // Empty string is always found
+ register const char *str = Ptr+offset-1;
+ register const char *search=s.ptr()+s.length()-1;
+
+ const char *end=Ptr+s.length()-2;
+ const char *search_end=s.ptr()-1;
+skipp:
+ while (str != end)
+ {
+ if (*str-- == *search)
+ {
+ register char *i,*j;
+ i=(char*) str; j=(char*) search-1;
+ while (j != search_end)
+ if (*i-- != *j--) goto skipp;
+ return (int) (i-Ptr) +1;
+ }
+ }
+ }
+ return -1;
+}
+
+/*
+** replace substring with string
+** If wrong parameter or not enough memory, do nothing
+*/
+
+
+bool String::replace(uint32 offset,uint32 arg_length,const String &to)
+{
+ long diff = (long) to.length()-(long) arg_length;
+ if (offset+arg_length <= str_length)
+ {
+ if (diff < 0)
+ {
+ memcpy(Ptr+offset,to.ptr(),to.length());
+ bmove(Ptr+offset+to.length(),Ptr+offset+arg_length,
+ str_length-offset-arg_length);
+ }
+ else
+ {
+ if (diff)
+ {
+ if (realloc(str_length+(uint32) diff))
+ return TRUE;
+ bmove_upp(Ptr+str_length+diff,Ptr+str_length,
+ str_length-offset-arg_length);
+ }
+ memcpy(Ptr+offset,to.ptr(),to.length());
+ }
+ str_length+=(uint32) diff;
+ }
+ return FALSE;
+}
+
+
+int sortcmp(const String *x,const String *y)
+{
+ const char *s= x->ptr();
+ const char *t= y->ptr();
+ uint32 x_len=x->length(),y_len=y->length(),len=min(x_len,y_len);
+
+#ifdef USE_STRCOLL
+ if (use_strcoll(default_charset_info))
+ {
+#ifndef CMP_ENDSPACE
+ while (x_len && isspace(s[x_len-1]))
+ x_len--;
+ while (y_len && isspace(t[y_len-1]))
+ y_len--;
+#endif
+ return my_strnncoll(default_charset_info,
+ (unsigned char *)s,x_len,(unsigned char *)t,y_len);
+ }
+ else
+ {
+#endif /* USE_STRCOLL */
+ x_len-=len; // For easy end space test
+ y_len-=len;
+ while (len--)
+ {
+ if (my_sort_order[(uchar) *s++] != my_sort_order[(uchar) *t++])
+ return ((int) my_sort_order[(uchar) s[-1]] -
+ (int) my_sort_order[(uchar) t[-1]]);
+ }
+#ifndef CMP_ENDSPACE
+ /* Don't compare end space in strings */
+ {
+ if (y_len)
+ {
+ const char *end=t+y_len;
+ for (; t != end ; t++)
+ if (!isspace(*t))
+ return -1;
+ }
+ else
+ {
+ const char *end=s+x_len;
+ for (; s != end ; s++)
+ if (!isspace(*s))
+ return 1;
+ }
+ return 0;
+ }
+#else
+ return (int) (x_len-y_len);
+#endif /* CMP_ENDSPACE */
+#ifdef USE_STRCOLL
+ }
+#endif
+}
+
+
+int stringcmp(const String *x,const String *y)
+{
+ const char *s= x->ptr();
+ const char *t= y->ptr();
+ uint32 x_len=x->length(),y_len=y->length(),len=min(x_len,y_len);
+
+ while (len--)
+ {
+ if (*s++ != *t++)
+ return ((int) (uchar) s[-1] - (int) (uchar) t[-1]);
+ }
+ return (int) (x_len-y_len);
+}
+
+
+String *copy_if_not_alloced(String *to,String *from,uint32 from_length)
+{
+ if (from->Alloced_length >= from_length)
+ return from;
+ if (from->alloced || !to || from == to)
+ {
+ (void) from->realloc(from_length);
+ return from;
+ }
+ if (to->realloc(from_length))
+ return from; // Actually an error
+ to->str_length=min(from->str_length,from_length);
+ memcpy(to->Ptr,from->Ptr,to->str_length);
+ return to;
+}
+
+/* Make it easier to handle different charactersets */
+
+#ifdef USE_MB
+#define INC_PTR(A,B) A+=((use_mb_flag && \
+ my_ismbchar(default_charset_info,A,B)) ? \
+ my_ismbchar(default_charset_info,A,B) : 1)
+#else
+#define INC_PTR(A,B) A++
+#endif
+
+/*
+** Compare string against string with wildcard
+** 0 if matched
+** -1 if not matched with wildcard
+** 1 if matched with wildcard
+*/
+
+#ifdef LIKE_CMP_TOUPPER
+#define likeconv(A) (uchar) toupper(A)
+#else
+#define likeconv(A) (uchar) my_sort_order[(uchar) (A)]
+#endif
+
+static int wild_case_compare(const char *str,const char *str_end,
+ const char *wildstr,const char *wildend,
+ char escape)
+{
+ int result= -1; // Not found, using wildcards
+#ifdef USE_MB
+ bool use_mb_flag=use_mb(default_charset_info);
+#endif
+ while (wildstr != wildend)
+ {
+ while (*wildstr != wild_many && *wildstr != wild_one)
+ {
+ if (*wildstr == escape && wildstr+1 != wildend)
+ wildstr++;
+#ifdef USE_MB
+ int l;
+ if (use_mb_flag &&
+ (l = my_ismbchar(default_charset_info, wildstr, wildend)))
+ {
+ if (str+l > str_end || memcmp(str, wildstr, l) != 0)
+ return 1;
+ str += l;
+ wildstr += l;
+ }
+ else
+#endif
+ if (str == str_end || likeconv(*wildstr++) != likeconv(*str++))
+ return(1); // No match
+ if (wildstr == wildend)
+ 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);
+ INC_PTR(str,str_end);
+ } while (++wildstr < wildend && *wildstr == wild_one);
+ if (wildstr == wildend)
+ break;
+ }
+ if (*wildstr == wild_many)
+ { // Found wild_many
+ wildstr++;
+ /* Remove any '%' and '_' from the wild search string */
+ for ( ; wildstr != wildend ; wildstr++)
+ {
+ if (*wildstr == wild_many)
+ continue;
+ if (*wildstr == wild_one)
+ {
+ if (str == str_end)
+ return (-1);
+ INC_PTR(str,str_end);
+ continue;
+ }
+ break; // Not a wild character
+ }
+ if (wildstr == wildend)
+ return(0); // Ok if wild_many is last
+ if (str == str_end)
+ return -1;
+
+ uchar cmp;
+ if ((cmp= *wildstr) == escape && wildstr+1 != wildend)
+ cmp= *++wildstr;
+#ifdef USE_MB
+ const char* mb = wildstr;
+ int mblen;
+ LINT_INIT(mblen);
+ if (use_mb_flag)
+ mblen = my_ismbchar(default_charset_info, wildstr, wildend);
+#endif
+ INC_PTR(wildstr,wildend); // This is compared trough cmp
+ cmp=likeconv(cmp);
+ do
+ {
+#ifdef USE_MB
+ if (use_mb_flag)
+ {
+ for (;;)
+ {
+ if (str >= str_end)
+ return -1;
+ if (mblen)
+ {
+ if (str+mblen <= str_end && memcmp(str, mb, mblen) == 0)
+ {
+ str += mblen;
+ break;
+ }
+ }
+ else if (!my_ismbchar(default_charset_info, str, str_end) &&
+ likeconv(*str) == cmp)
+ {
+ str++;
+ break;
+ }
+ INC_PTR(str, str_end);
+ }
+ }
+ else
+ {
+#endif /* USE_MB */
+ while (str != str_end && likeconv(*str) != cmp)
+ str++;
+ if (str++ == str_end) return (-1);
+#ifdef USE_MB
+ }
+#endif
+ {
+ int tmp=wild_case_compare(str,str_end,wildstr,wildend,escape);
+ if (tmp <= 0)
+ return (tmp);
+ }
+ } while (str != str_end && wildstr[0] != wild_many);
+ return(-1);
+ }
+ }
+ return (str != str_end ? 1 : 0);
+}
+
+
+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);
+}
+
+/*
+** The following is used when using LIKE on binary strings
+*/
+
+static int wild_compare(const char *str,const char *str_end,
+ const char *wildstr,const char *wildend,char escape)
+{
+ int result= -1; // Not found, using wildcards
+ while (wildstr != wildend)
+ {
+ while (*wildstr != wild_many && *wildstr != wild_one)
+ {
+ if (*wildstr == escape && wildstr+1 != wildend)
+ wildstr++;
+ if (str == str_end || *wildstr++ != *str++)
+ return(1);
+ if (wildstr == wildend)
+ 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);
+ str++;
+ } while (*++wildstr == wild_one && wildstr != wildend);
+ if (wildstr == wildend)
+ break;
+ }
+ if (*wildstr == wild_many)
+ { // Found wild_many
+ wildstr++;
+ /* Remove any '%' and '_' from the wild search string */
+ for ( ; wildstr != wildend ; wildstr++)
+ {
+ if (*wildstr == wild_many)
+ continue;
+ if (*wildstr == wild_one)
+ {
+ if (str == str_end)
+ return (-1);
+ str++;
+ continue;
+ }
+ break; // Not a wild character
+ }
+ if (wildstr == wildend)
+ return(0); // Ok if wild_many is last
+ if (str == str_end)
+ return -1;
+
+ char cmp;
+ if ((cmp= *wildstr) == escape && wildstr+1 != wildend)
+ cmp= *++wildstr;
+ wildstr++; // This is compared trough cmp
+ do
+ {
+ while (str != str_end && *str != cmp)
+ str++;
+ if (str++ == str_end) return (-1);
+ {
+ int tmp=wild_compare(str,str_end,wildstr,wildend,escape);
+ if (tmp <= 0)
+ return (tmp);
+ }
+ } while (str != str_end && wildstr[0] != wild_many);
+ return(-1);
+ }
+ }
+ 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);
+}
diff --git a/sql/sql_string.h b/sql/sql_string.h
new file mode 100644
index 00000000000..5d125422c0b
--- /dev/null
+++ b/sql/sql_string.h
@@ -0,0 +1,183 @@
+/* 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 */
+
+/* This file is originally from the mysql distribution. Coded by monty */
+
+#ifdef __GNUC__
+#pragma interface /* gcc class implementation */
+#endif
+
+#ifndef NOT_FIXED_DEC
+#define NOT_FIXED_DEC 31
+#endif
+
+class String
+{
+ char *Ptr;
+ uint32 str_length,Alloced_length;
+ bool alloced;
+public:
+ String()
+ { Ptr=0; str_length=Alloced_length=0; alloced=0; }
+ String(uint32 length_arg)
+ { alloced=0; Alloced_length=0; (void) real_alloc(length_arg); }
+ String(const char *str)
+ { Ptr=(char*) str; str_length=strlen(str); Alloced_length=0; alloced=0;}
+ String(const char *str,uint32 len)
+ { Ptr=(char*) str; str_length=len; Alloced_length=0; alloced=0;}
+ String(char *str,uint32 len)
+ { Ptr=(char*) str; Alloced_length=str_length=len; alloced=0;}
+ String(const String &str)
+ { Ptr=str.Ptr ; str_length=str.str_length ;
+ Alloced_length=str.Alloced_length; alloced=0; }
+
+ static void *operator new(size_t size) { return (void*) sql_alloc(size); }
+ static void operator delete(void *ptr_arg,size_t size) /*lint -e715 */
+ { sql_element_free(ptr_arg); }
+ ~String() { free(); }
+
+ inline uint32 length() const { return str_length;}
+ inline uint32 alloced_length() const { return Alloced_length;}
+ inline char& operator [] (uint32 i) const { return Ptr[i]; }
+ inline void length(uint32 len) { str_length=len ; }
+ inline bool is_empty() { return (str_length == 0); }
+ inline const char *ptr() const { return Ptr; }
+ inline char *c_ptr()
+ {
+ if (!Ptr || Ptr[str_length]) /* Should be safe */
+ (void) realloc(str_length);
+ return Ptr;
+ }
+ inline char *c_ptr_quick()
+ {
+ if (Ptr && str_length < Alloced_length)
+ Ptr[str_length]=0;
+ return Ptr;
+ }
+
+ void set(String &str,uint32 offset,uint32 arg_length)
+ {
+ free();
+ Ptr=(char*) str.ptr()+offset; str_length=arg_length; alloced=0;
+ if (str.Alloced_length)
+ Alloced_length=str.Alloced_length-offset;
+ else
+ Alloced_length=0;
+ }
+ inline void set(char *str,uint32 arg_length)
+ {
+ free();
+ Ptr=(char*) str; str_length=Alloced_length=arg_length ; alloced=0;
+ }
+ inline void set(const char *str,uint32 arg_length)
+ {
+ free();
+ Ptr=(char*) str; str_length=arg_length; Alloced_length=0 ; alloced=0;
+ }
+ inline void set_quick(char *str,uint32 arg_length)
+ {
+ if (!alloced)
+ {
+ Ptr=(char*) str; str_length=Alloced_length=arg_length;
+ }
+ }
+ bool set(longlong num);
+ /* bool set(long num); */
+ bool set(ulonglong num);
+ bool set(double num,uint decimals=2);
+ inline void free()
+ {
+ if (alloced)
+ {
+ alloced=0;
+ Alloced_length=0;
+ my_free(Ptr,MYF(0));
+ Ptr=0;
+ }
+ }
+
+ inline bool alloc(uint32 arg_length)
+ {
+ if (arg_length < Alloced_length)
+ return 0;
+ return real_alloc(arg_length);
+ }
+ bool real_alloc(uint32 arg_length); // Empties old string
+ bool realloc(uint32 arg_length);
+ inline void shrink(uint32 arg_length) // Shrink buffer
+ {
+ if (arg_length < Alloced_length)
+ {
+ char *new_ptr;
+ if (!(new_ptr=(char*) my_realloc(Ptr,arg_length,MYF(0))))
+ {
+ (void) my_free(Ptr,MYF(0));
+ real_alloc(arg_length);
+ }
+ else
+ {
+ Ptr=new_ptr;
+ Alloced_length=arg_length;
+ }
+ }
+ }
+ bool is_alloced() { return alloced; }
+ inline String& operator = (const String &s)
+ {
+ if (&s != this)
+ {
+ free();
+ Ptr=s.Ptr ; str_length=s.str_length ; Alloced_length=s.Alloced_length;
+ alloced=0;
+ }
+ return *this;
+ }
+
+ bool copy(); // Alloc string if not alloced
+ bool copy(const String &s); // Allocate new string
+ bool copy(const char *s,uint32 arg_length); // Allocate new string
+ bool append(const String &s);
+ bool append(const char *s,uint32 arg_length=0);
+ bool append(FILE* file, uint32 arg_length, myf my_flags);
+ int strstr(const String &search,uint32 offset=0); // Returns offset to substring or -1
+ 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)
+ {
+ if (str_length < Alloced_length)
+ {
+ Ptr[str_length++]=chr;
+ }
+ else
+ {
+ if (realloc(str_length+1))
+ return 1;
+ Ptr[str_length++]=chr;
+ }
+ return 0;
+ }
+ bool fill(uint32 max_length,char fill);
+ void strip_sp();
+ inline void caseup() { ::caseup(Ptr,str_length); }
+ inline void casedn() { ::casedn(Ptr,str_length); }
+ friend int sortcmp(const String *a,const String *b);
+ friend int stringcmp(const String *a,const String *b);
+ friend String *copy_if_not_alloced(String *a,String *b,uint32 arg_length);
+ friend int wild_case_compare(String &match,String &wild,char escape);
+ friend int wild_compare(String &match,String &wild,char escape);
+ uint32 numchars();
+ int charpos(int i,uint32 offset=0);
+};
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
new file mode 100644
index 00000000000..ed63b12ceb1
--- /dev/null
+++ b/sql/sql_table.cc
@@ -0,0 +1,1559 @@
+/* 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 */
+
+
+/* drop and alter of tables */
+
+#include "mysql_priv.h"
+#include <hash.h>
+#include <myisam.h>
+
+#ifdef __WIN__
+#include <io.h>
+#endif
+
+extern HASH open_cache;
+
+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);
+static int copy_data_between_tables(TABLE *from,TABLE *to,
+ List<create_field> &create,
+ enum enum_duplicates handle_duplicates,
+ ulong *copied,ulong *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
+*****************************************************************************/
+
+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;
+ db_type table_type;
+ DBUG_ENTER("mysql_rm_table");
+
+ /* mark for close and remove all cached entries */
+
+ pthread_mutex_lock(&thd->mysys_var->mutex);
+ thd->mysys_var->current_mutex= &LOCK_open;
+ thd->mysys_var->current_cond= &COND_refresh;
+ VOID(pthread_mutex_lock(&LOCK_open));
+ pthread_mutex_unlock(&thd->mysys_var->mutex);
+
+ for (TABLE_LIST *table=tables ; table ; table=table->next)
+ {
+ char *db=table->db ? table->db : thd->db;
+ if (!close_temporary_table(thd, db, table->real_name))
+ continue; // removed temporary table
+
+ abort_locked_tables(thd,db,table->real_name);
+ while (remove_table_from_cache(thd,db,table->real_name) && !thd->killed)
+ {
+ dropping_tables++;
+ (void) pthread_cond_wait(&COND_refresh,&LOCK_open);
+ dropping_tables--;
+ }
+ 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);
+ (void) unpack_filename(path,path);
+ error=0;
+
+ table_type=get_table_type(path);
+
+ if (my_delete(path,MYF(0))) /* Delete the table definition file */
+ {
+ if (errno != ENOENT || !if_exists)
+ {
+ error=1;
+ if (errno != ENOENT)
+ {
+ my_error(ER_CANT_DELETE_FILE,MYF(0),path,errno);
+ }
+ }
+ }
+ else
+ {
+ some_tables_deleted=1;
+ *fn_ext(path)=0; // Remove extension;
+ error=ha_delete_table(table_type, path);
+ if (error == ENOENT && if_exists)
+ error = 0;
+ }
+ if (error)
+ {
+ if (wrong_tables.length())
+ wrong_tables.append(',');
+ wrong_tables.append(String(table->real_name));
+ }
+ }
+ if (some_tables_deleted)
+ {
+ mysql_update_log.write(thd->query,thd->query_length);
+ Query_log_event qinfo(thd, thd->query);
+ mysql_bin_log.write(&qinfo);
+ }
+
+ VOID(pthread_cond_broadcast(&COND_refresh)); // Signal to refresh
+ 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 (wrong_tables.length())
+ {
+ my_error(ER_BAD_TABLE_ERROR,MYF(0),wrong_tables.c_ptr());
+ DBUG_RETURN(-1);
+ }
+ send_ok(&thd->net);
+ DBUG_RETURN(0);
+}
+
+
+int quick_rm_table(enum db_type base,const char *db,
+ const char *table_name)
+{
+ char path[FN_REFLEN];
+ int error=0;
+ (void) sprintf(path,"%s/%s/%s%s",mysql_data_home,db,table_name,reg_ext);
+ unpack_filename(path,path);
+ if (my_delete(path,MYF(0)))
+ error=1; /* purecov: inspected */
+ sprintf(path,"%s/%s/%s",mysql_data_home,db,table_name);
+ return ha_delete_table(base,path) || error;
+}
+
+/*****************************************************************************
+ * Create at table.
+ * If one creates a temporary table, this is automaticly opened
+ ****************************************************************************/
+
+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)
+{
+ char path[FN_REFLEN];
+ const char *key_name;
+ create_field *sql_field,*dup_field;
+ int error= -1;
+ uint db_options,field,null_fields,blob_columns;
+ ulong pos;
+ KEY *key_info,*key_info_buffer;
+ KEY_PART_INFO *key_part_info;
+ int auto_increment=0;
+ handler *file;
+ DBUG_ENTER("mysql_create_table");
+
+ /*
+ ** Check for dupplicate fields and check type of table to create
+ */
+
+ if (!fields.elements)
+ {
+ my_error(ER_TABLE_MUST_HAVE_COLUMNS,MYF(0));
+ DBUG_RETURN(-1);
+ }
+ List_iterator<create_field> it(fields),it2(fields);
+ null_fields=blob_columns=0;
+ db_options=create_info->table_options;
+ if (create_info->row_type == ROW_TYPE_DYNAMIC)
+ db_options|=HA_OPTION_PACK_RECORD;
+ file=get_new_handler((TABLE*) 0, create_info->db_type);
+
+ /* Don't pack keys in old tables if the user has requested this */
+
+ while ((sql_field=it++))
+ {
+ if ((sql_field->flags & BLOB_FLAG) ||
+ sql_field->sql_type == FIELD_TYPE_VAR_STRING &&
+ create_info->row_type != ROW_TYPE_FIXED)
+ {
+ db_options|=HA_OPTION_PACK_RECORD;
+ }
+ if (!(sql_field->flags & NOT_NULL_FLAG))
+ null_fields++;
+ while ((dup_field=it2++) != sql_field)
+ {
+ if (my_strcasecmp(sql_field->field_name, dup_field->field_name) == 0)
+ {
+ my_error(ER_DUP_FIELDNAME,MYF(0),sql_field->field_name);
+ DBUG_RETURN(-1);
+ }
+ }
+ it2.rewind();
+ }
+ /* If fixed row records, we need on bit to check for deleted rows */
+ if (!(db_options & HA_OPTION_PACK_RECORD))
+ null_fields++;
+ pos=(null_fields+7)/8;
+
+ it.rewind();
+ while ((sql_field=it++))
+ {
+ switch (sql_field->sql_type) {
+ case FIELD_TYPE_BLOB:
+ case FIELD_TYPE_MEDIUM_BLOB:
+ case FIELD_TYPE_TINY_BLOB:
+ case FIELD_TYPE_LONG_BLOB:
+ sql_field->pack_flag=FIELDFLAG_BLOB |
+ pack_length_to_packflag(sql_field->pack_length -
+ portable_sizeof_char_ptr);
+ if (sql_field->flags & BINARY_FLAG)
+ sql_field->pack_flag|=FIELDFLAG_BINARY;
+ sql_field->length=8; // Unireg field length
+ sql_field->unireg_check=Field::BLOB_FIELD;
+ blob_columns++;
+ break;
+ case FIELD_TYPE_VAR_STRING:
+ case FIELD_TYPE_STRING:
+ sql_field->pack_flag=0;
+ if (sql_field->flags & BINARY_FLAG)
+ sql_field->pack_flag|=FIELDFLAG_BINARY;
+ break;
+ case FIELD_TYPE_ENUM:
+ sql_field->pack_flag=pack_length_to_packflag(sql_field->pack_length) |
+ FIELDFLAG_INTERVAL;
+ sql_field->unireg_check=Field::INTERVAL_FIELD;
+ break;
+ case FIELD_TYPE_SET:
+ sql_field->pack_flag=pack_length_to_packflag(sql_field->pack_length) |
+ FIELDFLAG_BITFIELD;
+ sql_field->unireg_check=Field::BIT_FIELD;
+ break;
+ case FIELD_TYPE_DATE: // Rest of string types
+ case FIELD_TYPE_NEWDATE:
+ case FIELD_TYPE_TIME:
+ case FIELD_TYPE_DATETIME:
+ case FIELD_TYPE_NULL:
+ sql_field->pack_flag=f_settype((uint) sql_field->sql_type);
+ break;
+ case FIELD_TYPE_TIMESTAMP:
+ sql_field->unireg_check=Field::TIMESTAMP_FIELD;
+ /* fall through */
+ default:
+ sql_field->pack_flag=(FIELDFLAG_NUMBER |
+ (sql_field->flags & UNSIGNED_FLAG ? 0 :
+ FIELDFLAG_DECIMAL) |
+ (sql_field->flags & ZEROFILL_FLAG ?
+ FIELDFLAG_ZEROFILL : 0) |
+ f_settype((uint) sql_field->sql_type) |
+ (sql_field->decimals << FIELDFLAG_DEC_SHIFT));
+ break;
+ }
+ if (!(sql_field->flags & NOT_NULL_FLAG))
+ sql_field->pack_flag|=FIELDFLAG_MAYBE_NULL;
+ sql_field->offset= pos;
+ if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
+ auto_increment++;
+ pos+=sql_field->pack_length;
+ }
+ if (auto_increment > 1)
+ {
+ my_error(ER_WRONG_AUTO_KEY,MYF(0));
+ DBUG_RETURN(-1);
+ }
+ if (auto_increment &&
+ (file->option_flag() & HA_WRONG_ASCII_ORDER))
+ {
+ my_error(ER_TABLE_CANT_HANDLE_AUTO_INCREMENT,MYF(0));
+ DBUG_RETURN(-1);
+ }
+
+ if (blob_columns && (file->option_flag() & HA_NO_BLOBS))
+ {
+ my_error(ER_TABLE_CANT_HANDLE_BLOB,MYF(0));
+ DBUG_RETURN(-1);
+ }
+
+ /* Create keys */
+ List_iterator<Key> key_iterator(keys);
+ uint key_parts=0,key_count=keys.elements;
+ bool primary_key=0,unique_key=0;
+ Key *key;
+ uint tmp;
+ tmp=max(file->max_keys(), MAX_KEY);
+
+ if (key_count > tmp)
+ {
+ my_error(ER_TOO_MANY_KEYS,MYF(0),tmp);
+ DBUG_RETURN(-1);
+ }
+ while ((key=key_iterator++))
+ {
+ tmp=max(file->max_key_parts(),MAX_REF_PARTS);
+ if (key->columns.elements > tmp)
+ {
+ my_error(ER_TOO_MANY_KEY_PARTS,MYF(0),tmp);
+ DBUG_RETURN(-1);
+ }
+ if (key->name() && strlen(key->name()) > NAME_LEN)
+ {
+ my_error(ER_TOO_LONG_IDENT, MYF(0), key->name());
+ DBUG_RETURN(-1);
+ }
+ key_parts+=key->columns.elements;
+ }
+ key_info_buffer=key_info=(KEY*) sql_calloc(sizeof(KEY)*key_count);
+ key_part_info=(KEY_PART_INFO*) sql_calloc(sizeof(KEY_PART_INFO)*key_parts);
+ if (!key_info_buffer || ! key_part_info)
+ DBUG_RETURN(-1); // Out of memory
+
+ key_iterator.rewind();
+ for (; (key=key_iterator++) ; key_info++)
+ {
+ uint key_length=0;
+ key_part_spec *column;
+ if (key->type == Key::PRIMARY)
+ {
+ if (primary_key)
+ {
+ my_error(ER_MULTIPLE_PRI_KEY,MYF(0));
+ DBUG_RETURN(-1);
+ }
+ primary_key=1;
+ }
+ else if (key->type == Key::UNIQUE)
+ unique_key=1;
+ key_info->flags= (key->type == Key::MULTIPLE) ? 0 :
+ (key->type == Key::FULLTEXT) ? HA_FULLTEXT : HA_NOSAME;
+ key_info->key_parts=(uint8) key->columns.elements;
+ key_info->key_part=key_part_info;
+
+ List_iterator<key_part_spec> cols(key->columns);
+ for (uint column_nr=0 ; (column=cols++) ; column_nr++)
+ {
+ it.rewind();
+ field=0;
+ while ((sql_field=it++) &&
+ my_strcasecmp(column->field_name,sql_field->field_name))
+ field++;
+ if (!sql_field)
+ {
+ my_printf_error(ER_KEY_COLUMN_DOES_NOT_EXITS,
+ ER(ER_KEY_COLUMN_DOES_NOT_EXITS),MYF(0),
+ column->field_name);
+ DBUG_RETURN(-1);
+ }
+ if (f_is_blob(sql_field->pack_flag))
+ {
+ if (!(file->option_flag() & HA_BLOB_KEY))
+ {
+ my_printf_error(ER_BLOB_USED_AS_KEY,ER(ER_BLOB_USED_AS_KEY),MYF(0),
+ column->field_name);
+ DBUG_RETURN(-1);
+ }
+ if (!column->length)
+ {
+ if (key->type == Key::FULLTEXT)
+ column->length=1; /* ft-code ignores it anyway :-) */
+ else
+ {
+ my_printf_error(ER_BLOB_KEY_WITHOUT_LENGTH,
+ ER(ER_BLOB_KEY_WITHOUT_LENGTH),MYF(0),
+ column->field_name);
+ DBUG_RETURN(-1);
+ }
+ }
+ }
+ if (!(sql_field->flags & NOT_NULL_FLAG))
+ {
+ if (key->type == Key::PRIMARY)
+ {
+ my_error(ER_PRIMARY_CANT_HAVE_NULL, MYF(0));
+ DBUG_RETURN(-1);
+ }
+ if (!(file->option_flag() & 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);
+ }
+ }
+ if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER)
+ {
+ if (column_nr == 0 || (file->option_flag() & HA_AUTO_PART_KEY))
+ auto_increment--; // Field is used
+ }
+ key_part_info->fieldnr= field;
+ key_part_info->offset= (uint16) sql_field->offset;
+ key_part_info->key_type=sql_field->pack_flag;
+ uint length=sql_field->pack_length;
+ if (column->length)
+ {
+ if (f_is_blob(sql_field->pack_flag))
+ {
+ if ((length=column->length) > file->max_key_length())
+ {
+ my_error(ER_WRONG_SUB_KEY,MYF(0));
+ DBUG_RETURN(-1);
+ }
+ }
+ else if (column->length > length ||
+ (f_is_packed(sql_field->pack_flag) && column->length != length))
+ {
+ my_error(ER_WRONG_SUB_KEY,MYF(0));
+ DBUG_RETURN(-1);
+ }
+ length=column->length;
+ }
+ else if (length == 0)
+ {
+ my_printf_error(ER_WRONG_KEY_COLUMN, ER(ER_WRONG_KEY_COLUMN), MYF(0),
+ column->field_name);
+ DBUG_RETURN(-1);
+ }
+ key_part_info->length=(uint8) length;
+ /* Use packed keys for long strings on the first column */
+ if (!(db_options & HA_OPTION_NO_PACK_KEYS) &&
+ (length >= KEY_DEFAULT_PACK_LENGTH &&
+ (sql_field->sql_type == FIELD_TYPE_STRING ||
+ sql_field->sql_type == FIELD_TYPE_VAR_STRING ||
+ sql_field->pack_flag & FIELDFLAG_BLOB)))
+ {
+ if (column_nr == 0 && (sql_field->pack_flag & FIELDFLAG_BLOB))
+ key_info->flags|= HA_BINARY_PACK_KEY;
+ else
+ key_info->flags|= HA_PACK_KEY;
+ }
+ key_length+=length;
+ key_part_info++;
+
+ /* Create the key name based on the first column (if not given) */
+ if (column_nr == 0)
+ {
+ if (key->type == Key::PRIMARY)
+ key_name="PRIMARY";
+ else if (!(key_name = key->name()))
+ key_name=make_unique_key_name(sql_field->field_name,
+ key_info_buffer,key_info);
+ if (check_if_keyname_exists(key_name,key_info_buffer,key_info))
+ {
+ my_error(ER_DUP_KEYNAME,MYF(0),key_name);
+ DBUG_RETURN(-1);
+ }
+ key_info->name=(char*) key_name;
+ }
+ }
+ key_info->key_length=(uint16) key_length;
+ if (key_length > file->max_key_length())
+ {
+ my_error(ER_TOO_LONG_KEY,MYF(0),file->max_key_length());
+ DBUG_RETURN(-1);
+ }
+ }
+ if (auto_increment > 0)
+ {
+ my_error(ER_WRONG_AUTO_KEY,MYF(0));
+ DBUG_RETURN(-1);
+ }
+ if (!primary_key && !unique_key &&
+ (file->option_flag() & HA_REQUIRE_PRIMARY_KEY))
+ {
+ my_error(ER_REQUIRES_PRIMARY_KEY,MYF(0));
+ DBUG_RETURN(-1);
+ }
+
+ /* Check if table exists */
+ if (create_info->options & HA_LEX_CREATE_TMP_TABLE)
+ {
+ sprintf(path,"%s%s%lx_%lx_%x%s",mysql_tmpdir,tmp_file_prefix,
+ current_pid, thd->thread_id, thd->tmp_table++,reg_ext);
+ create_info->table_options|=HA_CREATE_DELAY_KEY_WRITE;
+ }
+ else
+ (void) sprintf(path,"%s/%s/%s%s",mysql_data_home,db,table_name,reg_ext);
+ unpack_filename(path,path);
+ /* Check if table already exists */
+ if ((create_info->options & HA_LEX_CREATE_TMP_TABLE)
+ && find_temporary_table(thd,db,table_name))
+ {
+ my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
+ DBUG_RETURN(-1);
+ }
+ VOID(pthread_mutex_lock(&LOCK_open));
+ if (!tmp_table && !(create_info->options & HA_LEX_CREATE_TMP_TABLE))
+ {
+ if (!access(path,F_OK))
+ {
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
+ DBUG_RETURN(0);
+ my_error(ER_TABLE_EXISTS_ERROR,MYF(0),table_name);
+ DBUG_RETURN(-1);
+ }
+ }
+
+ thd->proc_info="creating table";
+
+ create_info->table_options=db_options;
+ if (rea_create_table(path, create_info, fields, key_count,
+ key_info_buffer))
+ {
+ /* 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->query, thd->query_length);
+ 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 */
+ if (!(open_temporary_table(thd, path, db, table_name, 1)))
+ {
+ (void) rm_temporary_table(create_info->db_type, path);
+ goto end;
+ }
+ }
+ error=0;
+end:
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ thd->proc_info="After create";
+ DBUG_RETURN(error);
+}
+
+/*
+** Give the key name after the first field with an optional '_#' after
+**/
+
+static bool
+check_if_keyname_exists(const char *name, KEY *start, KEY *end)
+{
+ for (KEY *key=start ; key != end ; key++)
+ if (!my_strcasecmp(name,key->name))
+ return 1;
+ return 0;
+}
+
+
+static char *
+make_unique_key_name(const char *field_name,KEY *start,KEY *end)
+{
+ char buff[MAX_FIELD_NAME],*buff_end;
+
+ if (!check_if_keyname_exists(field_name,start,end))
+ return (char*) field_name; // Use fieldname
+ buff_end=strmake(buff,field_name,MAX_FIELD_NAME-4);
+ for (uint i=2 ; ; i++)
+ {
+ sprintf(buff_end,"_%d",i);
+ if (!check_if_keyname_exists(buff,start,end))
+ return sql_strdup(buff);
+ }
+}
+
+/****************************************************************************
+** Create table from a list of fields and items
+****************************************************************************/
+
+TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
+ const char *db, const char *name,
+ List<create_field> *extra_fields,
+ List<Key> *keys,
+ List<Item> *items,
+ MYSQL_LOCK **lock)
+{
+ TABLE tmp_table; // Used during 'create_field()'
+ TABLE *table;
+ tmp_table.table_name=0;
+ DBUG_ENTER("create_table_from_items");
+
+ /* Add selected items to field list */
+ List_iterator<Item> it(*items);
+ Item *item;
+ Field *tmp_field;
+ tmp_table.db_create_options=0;
+ tmp_table.null_row=tmp_table.maybe_null=0;
+ tmp_table.blob_ptr_size=portable_sizeof_char_ptr;
+ tmp_table.db_low_byte_first= test(create_info->db_type == DB_TYPE_MYISAM ||
+ create_info->db_type == DB_TYPE_HEAP);
+
+ while ((item=it++))
+ {
+ create_field *cr_field;
+ if (strlen(item->name) > NAME_LEN ||
+ check_column_name(item->name))
+ {
+ 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);
+ if (!field || !(cr_field=new create_field(field,1)))
+ DBUG_RETURN(0);
+ extra_fields->push_back(cr_field);
+ }
+ /* create and lock table */
+ /* QQ: This should be done atomic ! */
+ if (mysql_create_table(thd,db,name,create_info,*extra_fields,
+ *keys,0,1)) // no logging
+ DBUG_RETURN(0);
+ if (!(table=open_table(thd,db,name,name,(bool*) 0)))
+ {
+ quick_rm_table(create_info->db_type,db,name);
+ DBUG_RETURN(0);
+ }
+ table->reginfo.lock_type=TL_WRITE;
+ if (!((*lock)=mysql_lock_tables(thd,&table,1)))
+ {
+ hash_delete(&open_cache,(byte*) table);
+ quick_rm_table(create_info->db_type,db,name);
+ DBUG_RETURN(0);
+ }
+ table->file->extra(HA_EXTRA_WRITE_CACHE);
+ DBUG_RETURN(table);
+}
+
+
+/****************************************************************************
+** Alter a table definition
+****************************************************************************/
+
+static bool
+mysql_rename_table(enum db_type base,
+ const char *old_db,
+ const char * old_name,
+ const char *new_db,
+ const char * new_name)
+{
+ char from[FN_REFLEN],to[FN_REFLEN];
+ handler *file=get_new_handler((TABLE*) 0, base);
+ bool error=0;
+ DBUG_ENTER("mysql_rename_table");
+ (void) sprintf(from,"%s/%s/%s",mysql_data_home,old_db,old_name);
+ (void) sprintf(to,"%s/%s/%s",mysql_data_home,new_db,new_name);
+ fn_format(from,from,"","",4);
+ fn_format(to,to, "","",4);
+ if (file->rename_table((const char*) from,(const char *) to) ||
+ rename_file_ext(from,to,reg_ext))
+ error=1;
+ delete file;
+ DBUG_RETURN(error);
+}
+
+/*
+ close table in this thread and force close + reopen in other threads
+ This assumes that the calling thread has lock on LOCK_open
+ Win32 clients must also have a WRITE LOCK on the table !
+*/
+
+bool close_cached_table(THD *thd,TABLE *table)
+{
+ bool result=0;
+ DBUG_ENTER("close_cached_table");
+ if (table)
+ {
+ 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
+
+#ifdef REMOVE_LOCKS
+ /* Wait until all there are no other threads that has this table open */
+ while (remove_table_from_cache(thd,table->table_cache_key,
+ table->table_name))
+ {
+ dropping_tables++;
+ (void) pthread_cond_wait(&COND_refresh,&LOCK_open);
+ dropping_tables--;
+ }
+#else
+ (void) remove_table_from_cache(thd,table->table_cache_key,
+ table->table_name);
+#endif
+ /* When lock on LOCK_open is freed other threads can continue */
+ pthread_cond_broadcast(&COND_refresh);
+
+ /* Close lock if this is not got with LOCK TABLES */
+ if (thd->lock)
+ {
+ mysql_unlock_tables(thd, thd->lock); thd->lock=0; // Start locked threads
+ }
+ /* Close all copies of 'table'. This also frees all LOCK TABLES lock */
+ thd->open_tables=unlink_open_table(thd,thd->open_tables,table);
+ }
+ DBUG_RETURN(result);
+}
+
+int mysql_repair_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt)
+{
+ TABLE_LIST *table;
+ List<Item> field_list;
+ Item* item;
+ String* packet = &thd->packet;
+
+ DBUG_ENTER("mysql_repair_table");
+
+ field_list.push_back(item = new Item_empty_string("Table", NAME_LEN*2));
+ item->maybe_null = 1;
+ field_list.push_back(item = new Item_empty_string("Op", 10));
+ item->maybe_null = 1;
+ field_list.push_back(item = new Item_empty_string("Msg_type", 10));
+ item->maybe_null = 1;
+ field_list.push_back(item = new Item_empty_string("Msg_text", 255));
+ item->maybe_null = 1;
+ if (send_fields(thd, field_list, 1))
+ DBUG_RETURN(-1);
+
+
+ for (table = tables; table; table = table->next)
+ {
+ char table_name[NAME_LEN*2+2];
+ char* db = (table->db) ? table->db : thd->db;
+ strxmov(table_name,db ? db : "",".",table->name,NullS);
+
+ table->table = open_ltable(thd, table, TL_WRITE);
+ packet->length(0);
+
+ if (!table->table)
+ {
+ const char *err_msg;
+ net_store_data(packet, table_name);
+ net_store_data(packet, "repair");
+ net_store_data(packet, "error");
+ if (!(err_msg=thd->net.last_error))
+ err_msg=ER(ER_CHECK_NO_SUCH_TABLE);
+ net_store_data(packet, err_msg);
+ thd->net.last_error[0]=0;
+ if (my_net_write(&thd->net, (char*) thd->packet.ptr(),
+ packet->length()))
+ goto err;
+ continue;
+ }
+
+ int repair_code = table->table->file->repair(thd, check_opt);
+ packet->length(0);
+ net_store_data(packet, table_name);
+ net_store_data(packet, "repair");
+
+ switch(repair_code) {
+ case HA_REPAIR_NOT_IMPLEMENTED:
+ net_store_data(packet, "error");
+ net_store_data(packet, ER(ER_CHECK_NOT_IMPLEMENTED));
+ break;
+
+ case HA_REPAIR_OK:
+ net_store_data(packet, "status");
+ net_store_data(packet, "OK");
+ break;
+
+ case HA_REPAIR_FAILED:
+ net_store_data(packet, "status");
+ net_store_data(packet, "Not repaired");
+ break;
+
+ default:
+ net_store_data(packet, "Unknown - internal error during repair");
+ break;
+ }
+ close_thread_tables(thd);
+ if (my_net_write(&thd->net, (char*) packet->ptr(),
+ packet->length()))
+ goto err;
+ }
+
+ close_thread_tables(thd);
+ send_eof(&thd->net);
+ DBUG_RETURN(0);
+ err:
+ close_thread_tables(thd);
+ DBUG_RETURN(-1);
+}
+
+int mysql_optimize_table(THD* thd, TABLE_LIST* tables)
+{
+ net_printf(&thd->net, ER_PARSE_ERROR, "Sorry; This doesn't work yet", "",
+ 0);
+ return 1;
+}
+
+int mysql_analyze_table(THD* thd, TABLE_LIST* tables)
+{
+ TABLE_LIST *table;
+ List<Item> field_list;
+ Item* item;
+ String* packet = &thd->packet;
+ DBUG_ENTER("mysql_analyze_table");
+
+ field_list.push_back(item = new Item_empty_string("Table", NAME_LEN*2));
+ item->maybe_null = 1;
+ field_list.push_back(item = new Item_empty_string("Op", 10));
+ item->maybe_null = 1;
+ field_list.push_back(item = new Item_empty_string("Msg_type", 10));
+ item->maybe_null = 1;
+ field_list.push_back(item = new Item_empty_string("Msg_text", 255));
+ item->maybe_null = 1;
+ if (send_fields(thd, field_list, 1))
+ DBUG_RETURN(-1);
+
+
+ for (table = tables; table; table = table->next)
+ {
+ char table_name[NAME_LEN*2+2];
+ char* db = (table->db) ? table->db : thd->db;
+ strxmov(table_name,db ? db : "",".",table->name,NullS);
+
+ table->table = open_ltable(thd, table, TL_READ_NO_INSERT);
+ packet->length(0);
+
+ if (!table->table)
+ {
+ const char *err_msg;
+ net_store_data(packet, table_name);
+ net_store_data(packet, "analyze");
+ net_store_data(packet, "error");
+ if (!(err_msg=thd->net.last_error))
+ err_msg=ER(ER_CHECK_NO_SUCH_TABLE);
+ net_store_data(packet, err_msg);
+ thd->net.last_error[0]=0;
+ if (my_net_write(&thd->net, (char*) thd->packet.ptr(),
+ packet->length()))
+ goto err;
+ continue;
+ }
+ if (table->table->db_stat & HA_READ_ONLY)
+ {
+ net_store_data(packet, table_name);
+ net_store_data(packet, "analyze");
+ net_store_data(packet, "error");
+ net_store_data(packet, ER(ER_OPEN_AS_READONLY));
+ if (my_net_write(&thd->net, (char*) thd->packet.ptr(),
+ packet->length()))
+ goto err;
+ continue;
+ }
+
+ int analyze_code = table->table->file->analyze(thd);
+ packet->length(0);
+ net_store_data(packet, table_name);
+ net_store_data(packet, "analyze");
+
+ switch(analyze_code) {
+ case HA_ANALYZE_NOT_IMPLEMENTED:
+ net_store_data(packet, "error");
+ net_store_data(packet, ER(ER_CHECK_NOT_IMPLEMENTED));
+ break;
+
+ case HA_ANALYZE_OK:
+ net_store_data(packet, "status");
+ net_store_data(packet, "OK");
+ break;
+
+ default:
+ net_store_data(packet, "Unknown - internal error during analyze");
+ break;
+ }
+ close_thread_tables(thd);
+ if (my_net_write(&thd->net, (char*) packet->ptr(),
+ packet->length()))
+ goto err;
+ }
+
+ close_thread_tables(thd);
+ send_eof(&thd->net);
+ DBUG_RETURN(0);
+ err:
+ close_thread_tables(thd);
+ DBUG_RETURN(-1);
+}
+
+int mysql_check_table(THD* thd, TABLE_LIST* tables,HA_CHECK_OPT* check_opt)
+{
+ TABLE_LIST *table;
+ List<Item> field_list;
+ Item* item;
+ String* packet = &thd->packet;
+
+ DBUG_ENTER("mysql_check_table");
+
+ field_list.push_back(item = new Item_empty_string("Table", NAME_LEN*2));
+ item->maybe_null = 1;
+ field_list.push_back(item = new Item_empty_string("Op", 10));
+ item->maybe_null = 1;
+ field_list.push_back(item = new Item_empty_string("Msg_type", 10));
+ item->maybe_null = 1;
+ field_list.push_back(item = new Item_empty_string("Msg_text", 255));
+ item->maybe_null = 1;
+ if (send_fields(thd, field_list, 1))
+ DBUG_RETURN(-1);
+
+
+ for (table = tables; table; table = table->next)
+ {
+ char table_name[NAME_LEN*2+2];
+ char* db = (table->db) ? table->db : thd->db;
+ strxmov(table_name,db ? db : "",".",table->name,NullS);
+
+ table->table = open_ltable(thd, table, TL_READ_NO_INSERT);
+ packet->length(0);
+
+ if (!table->table)
+ {
+ const char *err_msg;
+ net_store_data(packet, table_name);
+ net_store_data(packet, "check");
+ net_store_data(packet, "error");
+ if (!(err_msg=thd->net.last_error))
+ err_msg=ER(ER_CHECK_NO_SUCH_TABLE);
+ net_store_data(packet, err_msg);
+ thd->net.last_error[0]=0;
+ if (my_net_write(&thd->net, (char*) thd->packet.ptr(),
+ packet->length()))
+ goto err;
+ continue;
+ }
+
+ int check_code = table->table->file->check(thd, check_opt);
+ packet->length(0);
+ net_store_data(packet, table_name);
+ net_store_data(packet, "check");
+
+ switch(check_code) {
+ case HA_CHECK_NOT_IMPLEMENTED:
+ net_store_data(packet, "error");
+ net_store_data(packet, ER(ER_CHECK_NOT_IMPLEMENTED));
+ break;
+
+ case HA_CHECK_OK:
+ net_store_data(packet, "status");
+ net_store_data(packet, "OK");
+ break;
+
+ case HA_CHECK_CORRUPT:
+ net_store_data(packet, "status");
+ net_store_data(packet, "Corrupt");
+ break;
+
+ default:
+ net_store_data(packet, "Unknown - internal error during check");
+ break;
+ }
+ close_thread_tables(thd);
+ if (my_net_write(&thd->net, (char*) packet->ptr(),
+ packet->length()))
+ goto err;
+ }
+
+ close_thread_tables(thd);
+ send_eof(&thd->net);
+ DBUG_RETURN(0);
+ err:
+ close_thread_tables(thd);
+ DBUG_RETURN(-1);
+}
+
+
+
+int mysql_alter_table(THD *thd,char *new_db, char *new_name,
+ HA_CREATE_INFO *create_info,
+ TABLE_LIST *table_list,
+ List<create_field> &fields,
+ List<Key> &keys,List<Alter_drop> &drop_list,
+ List<Alter_column> &alter_list,
+ bool drop_primary,
+ enum enum_duplicates handle_duplicates)
+{
+ TABLE *table,*new_table;
+ int error;
+ char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN],
+ *table_name,*db;
+ bool use_timestamp=0;
+ ulong copied,deleted;
+ uint save_time_stamp,db_create_options;
+ 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)
+ new_db=db;
+
+ if (!(table=open_ltable(thd,table_list,TL_WRITE_ALLOW_READ)))
+ DBUG_RETURN(-1);
+
+ /* Check that we are not trying to rename to an existing table */
+ if (new_name)
+ {
+ strmov(new_name_buff,new_name);
+ fn_same(new_name_buff,table_name,3);
+ if (!strcmp(new_name_buff,table_name)) // Check if name changed
+ new_name=table_name; // No. Make later check easier
+ else
+ {
+ if (table->tmp_table)
+ {
+ if (find_temporary_table(thd,new_db,new_name_buff))
+ {
+ my_error(ER_TABLE_EXISTS_ERROR,MYF(0),new_name);
+ DBUG_RETURN(-1);
+ }
+ }
+ else
+ {
+ if (!access(fn_format(new_name_buff,new_name_buff,new_db,reg_ext,0),
+ F_OK))
+ {
+ /* Table will be closed in do_command() */
+ my_error(ER_TABLE_EXISTS_ERROR,MYF(0),new_name);
+ DBUG_RETURN(-1);
+ }
+ }
+ }
+ }
+ else
+ new_name=table_name;
+
+ old_db_type=table->db_type;
+ if (create_info->db_type == DB_TYPE_DEFAULT)
+ create_info->db_type=old_db_type;
+ if (create_info->row_type == ROW_TYPE_DEFAULT)
+ create_info->row_type=table->row_type;
+ new_db_type=create_info->db_type;
+
+ /* Check if the user only wants to do a simple RENAME */
+
+ 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)
+ {
+ 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)
+ {
+ mysql_update_log.write(thd->query,thd->query_length);
+ Query_log_event qinfo(thd, thd->query);
+ mysql_bin_log.write(&qinfo);
+ send_ok(&thd->net);
+ }
+
+ DBUG_RETURN(error);
+ }
+
+ /* Full alter table */
+ restore_record(table,2); // Empty record for DEFAULT
+ List_iterator<Alter_drop> drop_it(drop_list);
+ List_iterator<create_field> def_it(fields);
+ List_iterator<Alter_column> alter_it(alter_list);
+ List<create_field> create_list; // Add new fields here
+ List<Key> key_list; // Add new keys here
+
+ /*
+ ** First collect all fields from table which isn't in drop_list
+ */
+
+ create_field *def;
+ Field **f_ptr,*field;
+ for (f_ptr=table->field ; (field= *f_ptr) ; f_ptr++)
+ {
+ /* Check if field should be droped */
+ Alter_drop *drop;
+ drop_it.rewind();
+ while ((drop=drop_it++))
+ {
+ if (drop->type == Alter_drop::COLUMN &&
+ !my_strcasecmp(field->field_name, drop->name))
+ break;
+ }
+ if (drop)
+ {
+ drop_it.remove();
+ continue;
+ }
+ /* Check if field is changed */
+ def_it.rewind();
+ while ((def=def_it++))
+ {
+ if (def->change && !my_strcasecmp(field->field_name, def->change))
+ break;
+ }
+ if (def)
+ { // Field is changed
+ def->field=field;
+ if (def->sql_type == FIELD_TYPE_TIMESTAMP)
+ use_timestamp=1;
+ create_list.push_back(def);
+ def_it.remove();
+ }
+ else
+ { // Use old field value
+ create_list.push_back(def=new create_field(field));
+ if (def->sql_type == FIELD_TYPE_TIMESTAMP)
+ use_timestamp=1;
+
+ alter_it.rewind(); // Change default if ALTER
+ Alter_column *alter;
+ while ((alter=alter_it++))
+ {
+ if (!my_strcasecmp(field->field_name, alter->name))
+ break;
+ }
+ if (alter)
+ {
+ def->def=alter->def; // Use new default
+ alter_it.remove();
+ }
+ }
+ }
+ def_it.rewind();
+ List_iterator<create_field> find_it(create_list);
+ while ((def=def_it++)) // Add new columns
+ {
+ if (def->change)
+ {
+ my_error(ER_BAD_FIELD_ERROR,MYF(0),def->change,table_name);
+ DBUG_RETURN(-1);
+ }
+ if (!def->after)
+ create_list.push_back(def);
+ else if (def->after == first_keyword)
+ create_list.push_front(def);
+ else
+ {
+ create_field *find;
+ find_it.rewind();
+ while ((find=find_it++)) // Add new columns
+ {
+ if (!my_strcasecmp(def->after, find->field_name))
+ break;
+ }
+ if (!find)
+ {
+ my_error(ER_BAD_FIELD_ERROR,MYF(0),def->after,table_name);
+ DBUG_RETURN(-1);
+ }
+ find_it.after(def); // Put element after this
+ }
+ }
+ if (alter_list.elements)
+ {
+ my_error(ER_BAD_FIELD_ERROR,MYF(0),alter_list.head()->name,table_name);
+ DBUG_RETURN(-1);
+ }
+ if (!create_list.elements)
+ {
+ my_error(ER_CANT_REMOVE_ALL_FIELDS,MYF(0));
+ DBUG_RETURN(-1);
+ }
+
+ /*
+ ** Collect all keys which isn't in drop list. Add only those
+ ** for which some fields exists.
+ */
+
+ List_iterator<Key> key_it(keys);
+ List_iterator<create_field> field_it(create_list);
+ List<key_part_spec> key_parts;
+
+ KEY *key_info=table->key_info;
+ for (uint i=0 ; i < table->keys ; i++,key_info++)
+ {
+ if (drop_primary && (key_info->flags & HA_NOSAME))
+ {
+ drop_primary=0;
+ continue;
+ }
+
+ char *key_name=key_info->name;
+ Alter_drop *drop;
+ drop_it.rewind();
+ while ((drop=drop_it++))
+ {
+ if (drop->type == Alter_drop::KEY &&
+ !my_strcasecmp(key_name, drop->name))
+ break;
+ }
+ if (drop)
+ {
+ drop_it.remove();
+ continue;
+ }
+
+ KEY_PART_INFO *key_part= key_info->key_part;
+ key_parts.empty();
+ for (uint j=0 ; j < key_info->key_parts ; j++,key_part++)
+ {
+ if (!key_part->field)
+ continue; // Wrong field (from UNIREG)
+ const char *key_part_name=key_part->field->field_name;
+ create_field *cfield;
+ field_it.rewind();
+ while ((cfield=field_it++))
+ {
+ if (cfield->change)
+ {
+ if (!my_strcasecmp(key_part_name, cfield->change))
+ break;
+ }
+ else if (!my_strcasecmp(key_part_name, cfield->field_name))
+ break;
+ }
+ if (!cfield)
+ continue; // Field is removed
+ uint key_part_length=key_part->length;
+ if (cfield->field) // Not new field
+ { // Check if sub key
+ if (cfield->field->type() != FIELD_TYPE_BLOB &&
+ (cfield->field->pack_length() == key_part_length ||
+ cfield->length != cfield->pack_length ||
+ cfield->pack_length <= key_part_length))
+ key_part_length=0; // Use whole field
+ }
+ key_parts.push_back(new key_part_spec(cfield->field_name,
+ key_part_length));
+ }
+ if (key_parts.elements)
+ key_list.push_back(new Key(key_info->flags & HA_NOSAME ?
+ (!my_strcasecmp(key_name, "PRIMARY") ?
+ Key::PRIMARY : Key::UNIQUE) :
+ (key_info->flags & HA_FULLTEXT ?
+ Key::FULLTEXT : Key::MULTIPLE),
+ key_name,key_parts));
+ }
+ key_it.rewind();
+ {
+ Key *key;
+ while ((key=key_it++)) // Add new keys
+ key_list.push_back(key);
+ }
+
+ if (drop_list.elements)
+ {
+ my_error(ER_CANT_DROP_FIELD_OR_KEY,MYF(0),drop_list.head()->name);
+ goto err;
+ }
+ if (alter_list.elements)
+ {
+ my_error(ER_CANT_DROP_FIELD_OR_KEY,MYF(0),alter_list.head()->name);
+ goto err;
+ }
+
+ (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))
+ db_create_options&= ~(HA_OPTION_PACK_KEYS | HA_OPTION_NO_PACK_KEYS);
+ if (create_info->table_options &
+ (HA_OPTION_CHECKSUM | HA_OPTION_NO_CHECKSUM))
+ db_create_options&= ~(HA_OPTION_CHECKSUM | HA_OPTION_NO_CHECKSUM);
+ if (create_info->table_options &
+ (HA_OPTION_DELAY_KEY_WRITE | HA_OPTION_NO_DELAY_KEY_WRITE))
+ db_create_options&= ~(HA_OPTION_DELAY_KEY_WRITE |
+ HA_OPTION_NO_DELAY_KEY_WRITE);
+ create_info->table_options|= db_create_options;
+
+ if (table->tmp_table)
+ create_info->options|=HA_LEX_CREATE_TMP_TABLE;
+
+ if ((error=mysql_create_table(thd, new_db, tmp_name,
+ create_info,
+ create_list,key_list,1,1))) // no logging
+ DBUG_RETURN(error);
+ {
+ if (table->tmp_table)
+ new_table=open_table(thd,new_db,tmp_name,tmp_name,0);
+ else
+ {
+ char path[FN_REFLEN];
+ (void) sprintf(path,"%s/%s/%s",mysql_data_home,new_db,tmp_name);
+ fn_format(path,path,"","",4+16+32);
+ new_table=open_temporary_table(thd, path, new_db, tmp_name,0);
+ }
+ if (!new_table)
+ {
+ VOID(quick_rm_table(new_db_type,new_db,tmp_name));
+ goto err;
+ }
+ }
+
+ save_time_stamp=new_table->time_stamp;
+ if (use_timestamp)
+ new_table->time_stamp=0;
+ new_table->next_number_field=new_table->found_next_number_field;
+ thd->count_cuted_fields=1; /* calc cuted fields */
+ thd->cuted_fields=0L;
+ thd->proc_info="copy to tmp table";
+ error=copy_data_between_tables(table,new_table,create_list,handle_duplicates,
+ &copied,&deleted);
+ thd->count_cuted_fields=0; /* Don`t calc cuted fields */
+ new_table->time_stamp=save_time_stamp;
+
+ if (table->tmp_table)
+ {
+ /* We changed a temporary table */
+ if (error)
+ {
+ close_temporary_table(thd,new_db,tmp_name);
+ my_free((gptr) new_table,MYF(0));
+ goto err;
+ }
+ /* Remove link to old table and rename the new one */
+ close_temporary_table(thd,table->table_cache_key,table_name);
+ if (rename_temporary_table(new_table, new_db, new_name))
+ { // Fatal error
+ close_temporary_table(thd,new_db,tmp_name);
+ my_free((gptr) new_table,MYF(0));
+ goto err;
+ }
+ mysql_update_log.write(thd->query,thd->query_length);
+ Query_log_event qinfo(thd, thd->query);
+ mysql_bin_log.write(&qinfo);
+
+ goto end_temporary;
+ DBUG_RETURN(0);
+ }
+
+ intern_close_table(new_table); /* close temporary table */
+ my_free((gptr) new_table,MYF(0));
+ VOID(pthread_mutex_lock(&LOCK_open));
+ if (error)
+ {
+ VOID(quick_rm_table(new_db_type,new_db,tmp_name));
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ goto err;
+ }
+ /*
+ ** Data is copied. Now we rename the old table to a temp name,
+ ** rename the new one to the old name, remove all entries from the old table
+ ** from the cash, free all locks, close the old table and remove it.
+ */
+
+ 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 (!access(new_name_buff,F_OK))
+ {
+ error=1;
+ my_error(ER_TABLE_EXISTS_ERROR,MYF(0),new_name_buff);
+ VOID(quick_rm_table(new_db_type,new_db,tmp_name));
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ goto err;
+ }
+ }
+
+#ifdef __WIN__
+ // Win32 can't rename an open table, so we must close the org table!
+ table_name=sql_strdup(table_name); // must be saved
+ if (close_cached_table(thd,table))
+ { // Aborted
+ VOID(quick_rm_table(new_db_type,new_db,tmp_name));
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ goto err;
+ }
+ table=0; // Marker for win32 version
+#endif
+
+ error=0;
+ if (mysql_rename_table(old_db_type,db,table_name,db,old_name))
+ {
+ error=1;
+ VOID(quick_rm_table(new_db_type,new_db,tmp_name));
+ }
+ else if (mysql_rename_table(new_db_type,new_db,tmp_name,new_db,
+ new_name))
+ { // Try to get everything back
+ error=1;
+ VOID(quick_rm_table(new_db_type,new_db,new_name));
+ VOID(quick_rm_table(new_db_type,new_db,tmp_name));
+ VOID(mysql_rename_table(old_db_type,db,old_name,db,table_name));
+ }
+ if (error)
+ {
+ // 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
+ 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
+ if (table)
+ {
+ VOID(table->file->extra(HA_EXTRA_FORCE_REOPEN)); // Use new file
+ remove_table_from_cache(thd,db,table_name); // Mark all in-use copies old
+ mysql_lock_abort(thd,table); // end threads waiting on lock
+ }
+ VOID(quick_rm_table(old_db_type,db,old_name));
+ if (close_data_tables(thd,db,table_name) ||
+ reopen_tables(thd,1,0))
+ { // This shouldn't happen
+ close_cached_table(thd,table); // Remove lock for table
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ goto err;
+ }
+ }
+ if ((error = ha_commit(thd)))
+ {
+ VOID(pthread_cond_broadcast(&COND_refresh));
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ goto err;
+ }
+
+ thd->proc_info="end";
+ mysql_update_log.write(thd->query,thd->query_length);
+ {
+ Query_log_event qinfo(thd, thd->query);
+ mysql_bin_log.write(&qinfo);
+ }
+ VOID(pthread_cond_broadcast(&COND_refresh));
+ VOID(pthread_mutex_unlock(&LOCK_open));
+
+end_temporary:
+ sprintf(tmp_name,ER(ER_INSERT_INFO),copied+deleted,deleted,
+ thd->cuted_fields);
+ send_ok(&thd->net,copied+deleted,0L,tmp_name);
+ thd->some_tables_deleted=0;
+ DBUG_RETURN(0);
+
+ err:
+ DBUG_RETURN(-1);
+}
+
+
+static int
+copy_data_between_tables(TABLE *from,TABLE *to,List<create_field> &create,
+ enum enum_duplicates handle_duplicates,
+ ulong *copied,ulong *deleted)
+{
+ int error;
+ Copy_field *copy,*copy_end;
+ ulong found_count,delete_count;
+ THD *thd= current_thd;
+ DBUG_ENTER("copy_data_between_tables");
+
+ if (!(copy= new Copy_field[to->fields]))
+ DBUG_RETURN(-1); /* purecov: inspected */
+
+ to->file->external_lock(thd,F_WRLCK);
+ to->file->extra(HA_EXTRA_WRITE_CACHE);
+
+ List_iterator<create_field> it(create);
+ create_field *def;
+ copy_end=copy;
+ for (Field **ptr=to->field ; *ptr ; ptr++)
+ {
+ def=it++;
+ if (def->field)
+ (copy_end++)->set(*ptr,def->field,0);
+ }
+
+ READ_RECORD info;
+ init_read_record(&info, thd, from, (SQL_SELECT *) 0, 1,1);
+
+ found_count=delete_count=0;
+ Field *next_field=to->next_number_field;
+ while (!(error=info.read_record(&info)))
+ {
+ if (thd->killed)
+ {
+ my_error(ER_SERVER_SHUTDOWN,MYF(0));
+ error= 1;
+ break;
+ }
+ if (next_field)
+ next_field->reset();
+ for (Copy_field *copy_ptr=copy ; copy_ptr != copy_end ; copy_ptr++)
+ copy_ptr->do_copy(copy_ptr);
+ if ((error=to->file->write_row((byte*) to->record[0])))
+ {
+ if (handle_duplicates != DUP_IGNORE ||
+ (error != HA_ERR_FOUND_DUPP_KEY &&
+ error != HA_ERR_FOUND_DUPP_UNIQUE))
+ {
+ to->file->print_error(error,MYF(0));
+ break;
+ }
+ delete_count++;
+ }
+ else
+ found_count++;
+ }
+ end_read_record(&info);
+ delete [] copy;
+ uint tmp_error;
+ if ((tmp_error=to->file->extra(HA_EXTRA_NO_CACHE)))
+ {
+ to->file->print_error(tmp_error,MYF(0));
+ error=1;
+ }
+ if (ha_commit(thd) || to->file->external_lock(thd,F_UNLCK))
+ error=1;
+ *copied= found_count;
+ *deleted=delete_count;
+ DBUG_RETURN(error > 0 ? -1 : 0);
+}
diff --git a/sql/sql_test.cc b/sql/sql_test.cc
new file mode 100644
index 00000000000..411a85a5c55
--- /dev/null
+++ b/sql/sql_test.cc
@@ -0,0 +1,246 @@
+/* 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 */
+
+
+/* Write some debug info */
+
+
+#include "mysql_priv.h"
+#include "sql_select.h"
+#include <hash.h>
+
+/* Intern key cache variables */
+extern "C" pthread_mutex_t THR_LOCK_keycache;
+
+#ifndef DBUG_OFF
+
+void
+print_where(COND *cond,const char *info)
+{
+ if (cond)
+ {
+ char buff[256];
+ String str(buff,(uint32) sizeof(buff));
+ str.length(0);
+ cond->print(&str);
+ str.append('\0');
+ DBUG_LOCK_FILE;
+ (void) fprintf(DBUG_FILE,"\nWHERE:(%s) ",info);
+ (void) fputs(str.ptr(),DBUG_FILE);
+ (void) fputc('\n',DBUG_FILE);
+ DBUG_UNLOCK_FILE;
+ }
+}
+
+ /* This is for debugging purposes */
+
+extern HASH open_cache;
+extern TABLE *unused_tables;
+
+void print_cached_tables(void)
+{
+ uint idx,count,unused;
+ TABLE *start_link,*lnk;
+
+ VOID(pthread_mutex_lock(&LOCK_open));
+ puts("DB Table Version Thread L.thread Open");
+
+ for (idx=unused=0 ; idx < open_cache.records ; idx++)
+ {
+ TABLE *entry=(TABLE*) hash_element(&open_cache,idx);
+ printf("%-14.14s %-32s%6ld%8ld%10ld%6d\n",
+ entry->table_cache_key,entry->real_name,entry->version,
+ entry->in_use ? entry->in_use->thread_id : 0L,
+ entry->in_use ? entry->in_use->dbug_thread_id : 0L,
+ entry->db_stat ? 1 : 0);
+ if (!entry->in_use)
+ unused++;
+ }
+ count=0;
+ if ((start_link=lnk=unused_tables))
+ {
+ do
+ {
+ if (lnk != lnk->next->prev || lnk != lnk->prev->next)
+ {
+ printf("unused_links isn't linked properly\n");
+ return;
+ }
+ } while (count++ < open_cache.records && (lnk=lnk->next) != start_link);
+ if (lnk != start_link)
+ {
+ printf("Unused_links aren't connected\n");
+ }
+ }
+ if (count != unused)
+ printf("Unused_links (%d) dosen't match open_cache: %d\n", count,unused);
+ printf("\nCurrent refresh version: %ld\n",refresh_version);
+ if (hash_check(&open_cache))
+ printf("Error: File hash table is corrupted\n");
+ fflush(stdout);
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ return;
+}
+
+
+void TEST_filesort(TABLE **table,SORT_FIELD *sortorder,uint s_length,
+ ha_rows special)
+{
+ char buff[256],buff2[256];
+ String str(buff,sizeof(buff)),out(buff2,sizeof(buff2));
+ const char *sep;
+ DBUG_ENTER("TEST_filesort");
+
+ out.length(0);
+ for (sep=""; s_length-- ; sortorder++, sep=" ")
+ {
+ out.append(sep);
+ if (sortorder->reverse)
+ out.append('-');
+ if (sortorder->field)
+ {
+ if (sortorder->field->table_name)
+ {
+ out.append(sortorder->field->table_name);
+ out.append('.');
+ }
+ out.append(sortorder->field->field_name ? sortorder->field->field_name:
+ "tmp_table_column");
+ }
+ else
+ {
+ str.length(0);
+ sortorder->item->print(&str);
+ out.append(str);
+ }
+ }
+ out.append('\0'); // Purify doesn't like c_ptr()
+ DBUG_LOCK_FILE;
+ VOID(fputs("\nInfo about FILESORT\n",DBUG_FILE));
+ if (special)
+ fprintf(DBUG_FILE,"Records to sort: %ld\n",special);
+ fprintf(DBUG_FILE,"Sortorder: %s\n",out.ptr());
+ DBUG_UNLOCK_FILE;
+ DBUG_VOID_RETURN;
+}
+
+
+void
+TEST_join(JOIN *join)
+{
+ uint i,ref;
+ DBUG_ENTER("TEST_join");
+
+ DBUG_LOCK_FILE;
+ VOID(fputs("\nInfo about JOIN\n",DBUG_FILE));
+ for (i=0 ; i < join->tables ; i++)
+ {
+ JOIN_TAB *tab=join->join_tab+i;
+ TABLE *form=tab->table;
+ fprintf(DBUG_FILE,"%-16.16s type: %-7s q_keys: %4d refs: %d key: %d len: %d\n",
+ form->table_name,
+ join_type_str[tab->type],
+ tab->keys,
+ tab->ref.key_parts,
+ tab->ref.key,
+ tab->ref.key_length);
+ if (tab->select)
+ {
+ if (tab->use_quick == 2)
+ fprintf(DBUG_FILE,
+ " quick select checked for each record (keys: %d)\n",
+ (int) tab->select->quick_keys);
+ else if (tab->select->quick)
+ fprintf(DBUG_FILE," quick select used on key %s, length: %d\n",
+ form->key_info[tab->select->quick->index].name,
+ tab->select->quick->max_used_key_length);
+ else
+ VOID(fputs(" select used\n",DBUG_FILE));
+ }
+ if (tab->ref.key_parts)
+ {
+ VOID(fputs(" refs: ",DBUG_FILE));
+ for (ref=0 ; ref < tab->ref.key_parts ; ref++)
+ {
+ Item *item=tab->ref.items[ref];
+ fprintf(DBUG_FILE,"%s ", item->full_name());
+ }
+ VOID(fputc('\n',DBUG_FILE));
+ }
+ }
+ DBUG_UNLOCK_FILE;
+ DBUG_VOID_RETURN;
+}
+
+#endif
+
+void mysql_print_status(THD *thd)
+{
+ printf("\nStatus information:\n\n");
+ if (thd)
+ thd->proc_info="locks";
+ thr_print_locks(); // Write some debug info
+#ifndef DBUG_OFF
+ if (thd)
+ thd->proc_info="table cache";
+ print_cached_tables();
+#endif
+ /* Print key cache status */
+ if (thd)
+ thd->proc_info="key cache";
+ pthread_mutex_lock(&THR_LOCK_keycache);
+ printf("key_cache status:\n\
+blocks used:%10lu\n\
+not flushed:%10lu\n\
+w_requests: %10lu\n\
+writes: %10lu\n\
+r_requests: %10lu\n\
+reads: %10lu\n",
+ _my_blocks_used,_my_blocks_changed,_my_cache_w_requests,
+ _my_cache_write,_my_cache_r_requests,_my_cache_read);
+ pthread_mutex_unlock(&THR_LOCK_keycache);
+
+ if (thd)
+ thd->proc_info="status";
+ pthread_mutex_lock(&LOCK_status);
+ printf("\nhandler status:\n\
+read_key: %10lu\n\
+read_next: %10lu\n\
+read_rnd %10lu\n\
+read_first: %10lu\n\
+write: %10lu\n\
+delete %10lu\n\
+update: %10lu\n",
+ ha_read_key_count, ha_read_next_count,
+ ha_read_rnd_count, ha_read_first_count,
+ ha_write_count, ha_delete_count, ha_update_count);
+ pthread_mutex_unlock(&LOCK_status);
+ printf("\nTable status:\n\
+Opened tables: %10lu\n\
+Open tables: %10lu\n\
+Open files: %10lu\n\
+Open streams: %10lu\n",
+ opened_tables,
+ (ulong) cached_tables(),
+ (ulong) my_file_opened,
+ (ulong) my_stream_opened);
+ fflush(stdout);
+ if (thd)
+ thd->proc_info="malloc";
+ TERMINATE(stdout); // Write malloc information
+ if (thd)
+ thd->proc_info=0;
+}
diff --git a/sql/sql_udf.cc b/sql/sql_udf.cc
new file mode 100644
index 00000000000..7d123141169
--- /dev/null
+++ b/sql/sql_udf.cc
@@ -0,0 +1,471 @@
+/* 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 */
+
+
+
+/* This implements 'user defined functions' */
+
+/*
+** Known bugs:
+**
+** Memory for functions are never freed!
+** Shared libraries are not closed before mysqld exists;
+** - This is because we can't be sure if some threads is using
+** a functions.
+**
+** The buggs only affects applications that creates and frees a lot of
+** dynamic functions, so this shouldn't be a real problem.
+*/
+
+#ifdef __GNUC__
+#pragma implementation // gcc: implement sql_udf.h
+#endif
+
+#include "mysql_priv.h"
+#ifdef HAVE_DLOPEN
+extern "C"
+{
+#include <dlfcn.h>
+#include <stdarg.h>
+#include <hash.h>
+}
+
+#ifndef RTLD_NOW
+#define RTLD_NOW 1 // For FreeBSD 2.2.2
+#endif
+
+#ifndef HAVE_DLERROR
+#define dlerror() ""
+#endif
+
+static bool initialized = 0;
+static MEM_ROOT mem;
+static HASH udf_hash;
+static pthread_mutex_t THR_LOCK_udf;
+
+
+static udf_func *add_udf(char *name, Item_result ret, char *dl,
+ Item_udftype typ);
+static void del_udf(udf_func *udf);
+static void *find_udf_dl(const char *dl);
+
+static void init_syms(udf_func *tmp)
+{
+ char nm[MAX_FIELD_NAME+16],*end;
+
+ tmp->func = dlsym(tmp->dlhandle, tmp->name);
+ end=strmov(nm,tmp->name);
+ (void) strmov(end,"_init");
+ tmp->func_init = dlsym(tmp->dlhandle, nm);
+ (void) strmov(end,"_deinit");
+ tmp->func_deinit = dlsym(tmp->dlhandle, nm);
+ if (tmp->type == UDFTYPE_AGGREGATE)
+ {
+ (void)strmov( end, "_reset" );
+ tmp->func_reset = dlsym( tmp->dlhandle, nm );
+ (void)strmov( end, "_add" );
+ tmp->func_add = dlsym( tmp->dlhandle, nm );
+ }
+}
+
+static 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;
+ return (byte*) udf->name;
+}
+
+/*
+** Read all predeclared functions from func@mysql and accept all that
+** can be used.
+*/
+
+void udf_init()
+{
+ udf_func *tmp;
+ TABLE_LIST tables;
+ READ_RECORD read_record_info;
+ int error;
+ DBUG_ENTER("ufd_init");
+
+ if (initialized)
+ DBUG_VOID_RETURN;
+
+ pthread_mutex_init(&THR_LOCK_udf,NULL);
+
+ init_sql_alloc(&mem, 1024);
+ THD *new_thd = new THD;
+ if (!new_thd ||
+ hash_init(&udf_hash,32,0,0,get_hash_key, NULL, HASH_CASE_INSENSITIVE))
+ {
+ sql_print_error("Can't allocate memory for udf structures");
+ hash_free(&udf_hash);
+ free_root(&mem);
+ 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));
+
+ bzero((gptr) &tables,sizeof(tables));
+ tables.name = tables.real_name = (char*) "func";
+ tables.lock_type = TL_READ;
+ tables.db=new_thd->db;
+
+ if (open_tables(new_thd, &tables))
+ {
+ DBUG_PRINT("error",("Can't open udf table"));
+ sql_print_error("Can't open mysql/func table");
+ close_thread_tables(new_thd);
+ delete new_thd;
+ DBUG_VOID_RETURN;
+ }
+
+ TABLE *table = tables.table;
+ init_read_record(&read_record_info, new_thd, table, NULL,1,0);
+ while (!(error = read_record_info.read_record(&read_record_info)))
+ {
+ DBUG_PRINT("info",("init udf record"));
+ char *name=get_field(&mem, table, 0);
+ char *dl_name= get_field(&mem, table, 2);
+ bool new_dl=0;
+ Item_udftype udftype=UDFTYPE_FUNCTION;
+ if (table->fields >= 4) // New func table
+ udftype=(Item_udftype) table->field[3]->val_int();
+
+ if (!(tmp = add_udf(name,(Item_result) table->field[1]->val_int(),
+ dl_name, udftype)))
+ {
+ sql_print_error("Can't alloc memory for udf function: name");
+ continue;
+ }
+
+ void *dl = find_udf_dl(tmp->dl);
+ if (dl == NULL)
+ {
+ if (!(dl = dlopen(tmp->dl, RTLD_NOW)))
+ {
+ sql_print_error(ER(ER_CANT_OPEN_LIBRARY),
+ tmp->dl,errno,dlerror());
+ del_udf(tmp);
+ continue;
+ }
+ new_dl=1;
+ }
+ tmp->dlhandle = dl;
+ init_syms(tmp);
+ if (!tmp->func)
+ {
+ sql_print_error(ER(ER_CANT_FIND_DL_ENTRY), name);
+ del_udf(tmp);
+ if (new_dl)
+ dlclose(dl);
+ }
+ }
+ if (error > 0)
+ sql_print_error(ER(ER_GET_ERRNO), my_errno);
+ end_read_record(&read_record_info);
+ new_thd->version--; // Force close to free memory
+ close_thread_tables(new_thd);
+ delete new_thd;
+ DBUG_VOID_RETURN;
+}
+
+
+void udf_free()
+{
+ /* close all shared libraries */
+ DBUG_ENTER("udf_free");
+ for (uint idx=0 ; idx < udf_hash.records ; idx++)
+ {
+ udf_func *udf=(udf_func*) hash_element(&udf_hash,idx);
+ if (udf->dl)
+ {
+ 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;
+ }
+ }
+ dlclose(udf->dlhandle);
+ }
+ hash_free(&udf_hash);
+ free_root(&mem);
+ DBUG_VOID_RETURN;
+}
+
+
+static void del_udf(udf_func *udf)
+{
+ DBUG_ENTER("del_udf");
+ if (!--udf->usage_count)
+ {
+ hash_delete(&udf_hash,(byte*) udf);
+ using_udf_functions=udf_hash.records != 0;
+ }
+ 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
+ */
+ char *name= udf->name;
+ uint name_length=udf->name_length;
+ udf->name=(char*) "*";
+ udf->name_length=1;
+ hash_update(&udf_hash,(byte*) udf,name,name_length);
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+void free_udf(udf_func *udf)
+{
+ DBUG_ENTER("free_udf");
+ pthread_mutex_lock(&THR_LOCK_udf);
+ if (!--udf->usage_count)
+ {
+ hash_delete(&udf_hash,(byte*) udf);
+ using_udf_functions=udf_hash.records != 0;
+ if (!find_udf_dl(udf->dl))
+ dlclose(udf->dlhandle);
+ }
+ pthread_mutex_unlock(&THR_LOCK_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)
+{
+ udf_func *udf=0;
+ DBUG_ENTER("find_udf");
+
+ /* TODO: This should be changed to reader locks someday! */
+ pthread_mutex_lock(&THR_LOCK_udf);
+ udf=(udf_func*) hash_search(&udf_hash,name,
+ length ? length : strlen(name));
+ 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.
+ */
+ for (uint idx=0 ; idx < udf_hash.records ; idx++)
+ {
+ udf_func *udf=(udf_func*) hash_element(&udf_hash,idx);
+ if (!strcmp(dl, udf->dl) && udf->dlhandle != NULL)
+ DBUG_RETURN(udf->dlhandle);
+ }
+ DBUG_RETURN(0);
+}
+
+
+/* Assume that name && dl is already allocated */
+
+static udf_func *add_udf(char *name, Item_result ret, char *dl,
+ Item_udftype type)
+{
+ if (!name || !dl)
+ return 0;
+ udf_func *tmp= (udf_func*) alloc_root(&mem, sizeof(udf_func));
+ if (!tmp)
+ return 0;
+ bzero((char*) tmp,sizeof(*tmp));
+ tmp->name = name;
+ tmp->name_length=strlen(tmp->name);
+ tmp->dl = dl;
+ tmp->returns = ret;
+ tmp->type = type;
+ tmp->usage_count=1;
+ if (hash_insert(&udf_hash,(char*) tmp))
+ return 0;
+ using_udf_functions=1;
+ return tmp;
+}
+
+
+int mysql_create_function(THD *thd,udf_func *udf)
+{
+ int error;
+ void *dl=0;
+ bool new_dl=0;
+ TABLE *table;
+ TABLE_LIST tables;
+ udf_func *u_d;
+ DBUG_ENTER("mysql_create_function");
+
+ if (!initialized)
+ {
+ send_error(&thd->net, ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES));
+ DBUG_RETURN(1);
+ }
+
+ /*
+ Ensure that the .dll doesn't have a path
+ This is done to ensure that only approved dll from the system
+ directories are used (to make this even remotely secure).
+ */
+ if (strchr(udf->dl, '/'))
+ {
+ send_error(&thd->net, ER_UDF_NO_PATHS,ER(ER_UDF_NO_PATHS));
+ DBUG_RETURN(1);
+ }
+ if (udf->name_length > NAME_LEN)
+ {
+ net_printf(&thd->net, ER_TOO_LONG_IDENT,udf->name);
+ DBUG_RETURN(1);
+ }
+
+ pthread_mutex_lock(&THR_LOCK_udf);
+ if (hash_search(&udf_hash,udf->name, udf->name_length))
+ {
+ net_printf(&thd->net, ER_UDF_EXISTS, udf->name);
+ goto err;
+ }
+ if (!(dl = find_udf_dl(udf->dl)))
+ {
+ if (!(dl = dlopen(udf->dl, RTLD_NOW)))
+ {
+ DBUG_PRINT("error",("dlopen of %s failed, error: %d (%s)",
+ udf->dl,errno,dlerror()));
+ net_printf(&thd->net, ER_CANT_OPEN_LIBRARY, udf->dl, errno, dlerror());
+ goto err;
+ }
+ new_dl=1;
+ }
+ udf->dlhandle=dl;
+ init_syms(udf);
+
+ if (udf->func == NULL)
+ {
+ net_printf(&thd->net, ER_CANT_FIND_DL_ENTRY, udf->name);
+ goto err;
+ }
+ 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)))
+ {
+ send_error(&thd->net,0); // End of memory
+ goto err;
+ }
+ u_d->dlhandle = dl;
+ u_d->func=udf->func;
+ u_d->func_init=udf->func_init;
+ u_d->func_deinit=udf->func_deinit;
+ u_d->func_reset=udf->func_reset;
+ u_d->func_add=udf->func_add;
+
+ /* create entry in mysql/func table */
+
+ bzero((char*) &tables,sizeof(tables));
+ tables.db= (char*) "mysql";
+ tables.real_name=tables.name= (char*) "func";
+ /* Allow creation of functions even if we can't open func table */
+ if (!(table = open_ltable(thd,&tables,TL_WRITE)))
+ goto err;
+
+ restore_record(table,2); // Get default values for fields
+ table->field[0]->store(u_d->name, u_d->name_length);
+ table->field[1]->store((longlong) u_d->returns);
+ table->field[2]->store(u_d->dl,strlen(u_d->dl));
+ if (table->fields >= 4) // If not old func format
+ table->field[3]->store((longlong) u_d->type);
+ error = table->file->write_row(table->record[0]);
+
+ close_thread_tables(thd);
+ if (error)
+ {
+ net_printf(&thd->net, ER_ERROR_ON_WRITE, "func@mysql",error);
+ del_udf(u_d);
+ goto err;
+ }
+ pthread_mutex_unlock(&THR_LOCK_udf);
+ DBUG_RETURN(0);
+
+ err:
+ if (new_dl)
+ dlclose(dl);
+ pthread_mutex_unlock(&THR_LOCK_udf);
+ DBUG_RETURN(1);
+}
+
+
+int mysql_drop_function(THD *thd,const char *udf_name)
+{
+ TABLE *table;
+ TABLE_LIST tables;
+ udf_func *udf;
+ DBUG_ENTER("mysql_drop_function");
+ if (!initialized)
+ {
+ send_error(&thd->net, ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES));
+ DBUG_RETURN(1);
+ }
+ pthread_mutex_lock(&THR_LOCK_udf);
+ if (!(udf=(udf_func*) hash_search(&udf_hash,udf_name, strlen(udf_name))))
+ {
+ net_printf(&thd->net, ER_FUNCTION_NOT_DEFINED, udf_name);
+ goto err;
+ }
+ del_udf(udf);
+ if (!find_udf_dl(udf->dl))
+ dlclose(udf->dlhandle);
+
+ bzero((char*) &tables,sizeof(tables));
+ tables.db=(char*) "mysql";
+ tables.real_name=tables.name=(char*) "func";
+ if (!(table = open_ltable(thd,&tables,TL_WRITE)))
+ goto err;
+ if (!table->file->index_read_idx(table->record[0],0,(byte*) udf_name,
+ strlen(udf_name),
+ HA_READ_KEY_EXACT))
+ {
+ int error;
+ if ((error = table->file->delete_row(table->record[0])))
+ table->file->print_error(error, MYF(0));
+ }
+ close_thread_tables(thd);
+
+ pthread_mutex_unlock(&THR_LOCK_udf);
+ DBUG_RETURN(0);
+ err:
+ pthread_mutex_unlock(&THR_LOCK_udf);
+ DBUG_RETURN(1);
+}
+
+#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
new file mode 100644
index 00000000000..d0b20f0a734
--- /dev/null
+++ b/sql/sql_udf.h
@@ -0,0 +1,144 @@
+/* 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 */
+
+
+/* This file defines structures needed by udf functions */
+
+#ifdef __GNUC__
+#pragma interface
+#endif
+
+enum Item_udftype {UDFTYPE_FUNCTION=1,UDFTYPE_AGGREGATE};
+
+typedef struct st_udf_func
+{
+ char *name;
+ int name_length;
+ Item_result returns;
+ Item_udftype type;
+ char *dl;
+ void *dlhandle;
+ void *func;
+ void *func_init;
+ void *func_deinit;
+ void *func_reset;
+ void *func_add;
+ ulong usage_count;
+} udf_func;
+
+class Item_result_field;
+struct st_table_list;
+
+class udf_handler :public Sql_alloc
+{
+ protected:
+ udf_func *u_d;
+ String *buffers;
+ UDF_ARGS f_args;
+ UDF_INIT initid;
+ char *num_buffer;
+ uchar error;
+ bool initialized;
+ Item **args;
+
+ public:
+ table_map used_tables_cache;
+ bool const_item_cache;
+ udf_handler(udf_func *udf_arg) :u_d(udf_arg), buffers(0), error(0),
+ initialized(0)
+ {}
+ ~udf_handler();
+ const char *name() const { return u_d ? u_d->name : "?"; }
+ Item_result result_type () const
+ { return u_d ? u_d->returns : STRING_RESULT;}
+ bool get_arguments();
+ bool fix_fields(THD *thd,struct st_table_list *tlist,Item_result_field *item,
+ uint arg_count,Item **args);
+ double val(my_bool *null_value)
+ {
+ if (get_arguments())
+ {
+ *null_value=1;
+ return 0.0;
+ }
+ uchar is_null=0;
+ double (*func)(UDF_INIT *, UDF_ARGS *, uchar *, uchar *)=
+ (double (*)(UDF_INIT *, UDF_ARGS *, uchar *, uchar *)) u_d->func;
+ double tmp=func(&initid, &f_args, &is_null, &error);
+ if (is_null || error)
+ {
+ *null_value=1;
+ return 0.0;
+ }
+ *null_value=0;
+ return tmp;
+ }
+ longlong val_int(my_bool *null_value)
+ {
+ if (get_arguments())
+ {
+ *null_value=1;
+ return LL(0);
+ }
+ uchar is_null=0;
+ longlong (*func)(UDF_INIT *, UDF_ARGS *, uchar *, uchar *)=
+ (longlong (*)(UDF_INIT *, UDF_ARGS *, uchar *, uchar *)) u_d->func;
+ longlong tmp=func(&initid, &f_args, &is_null, &error);
+ if (is_null || error)
+ {
+ *null_value=1;
+ return LL(0);
+ }
+ *null_value=0;
+ return tmp;
+ }
+ void reset(my_bool *null_value)
+ {
+ uchar is_null=0;
+ if (get_arguments())
+ {
+ *null_value=1;
+ return;
+ }
+ void (*func)(UDF_INIT *, UDF_ARGS *, uchar *, uchar *)=
+ (void (*)(UDF_INIT *, UDF_ARGS *, uchar *, uchar *)) u_d->func_reset;
+ func(&initid, &f_args, &is_null, &error);
+ *null_value= (my_bool) (is_null || error);
+ }
+ void add(my_bool *null_value)
+ {
+ uchar is_null=0;
+ if (get_arguments())
+ {
+ *null_value=1;
+ return;
+ }
+ void (*func)(UDF_INIT *, UDF_ARGS *, uchar *, uchar *)=
+ (void (*)(UDF_INIT *, UDF_ARGS *, uchar *, uchar *)) u_d->func_add;
+ func(&initid, &f_args, &is_null, &error);
+ *null_value= (my_bool) (is_null || error);
+ }
+ String *val_str(String *str,String *save_str);
+};
+
+
+#ifdef HAVE_DLOPEN
+void udf_init(void),udf_free(void);
+udf_func *find_udf(const char *name, uint len=0,bool mark_used=0);
+void free_udf(udf_func *udf);
+int mysql_create_function(THD *thd,udf_func *udf);
+int mysql_drop_function(THD *thd,const char *name);
+#endif
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
new file mode 100644
index 00000000000..41f0eb97456
--- /dev/null
+++ b/sql/sql_update.cc
@@ -0,0 +1,266 @@
+/* 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 */
+
+#include "mysql_priv.h"
+#include "sql_acl.h"
+
+/* Return 0 if row hasn't changed */
+
+static bool compare_record(TABLE *table)
+{
+ if (!table->blob_fields)
+ return cmp_record(table,1);
+ ulong current_query_id=current_thd->query_id;
+
+ if (memcmp(table->null_flags,
+ table->null_flags+table->rec_buff_length,
+ table->null_bytes))
+ return 1; // Diff in NULL value
+ for (Field **ptr=table->field ; *ptr ; ptr++)
+ {
+ if ((*ptr)->query_id == current_query_id &&
+ (*ptr)->cmp_binary_offset(table->rec_buff_length))
+ return 1;
+ }
+ return 0;
+}
+
+
+int mysql_update(THD *thd,TABLE_LIST *table_list,List<Item> &fields,
+ List<Item> &values, COND *conds,
+ ha_rows limit,
+ enum enum_duplicates handle_duplicates,
+ thr_lock_type lock_type)
+{
+ bool using_limit=limit != HA_POS_ERROR;
+ bool used_key_is_modified;
+ int error=0;
+ uint save_time_stamp, used_index;
+ key_map old_used_keys;
+ TABLE *table;
+ SQL_SELECT *select;
+ READ_RECORD info;
+ DBUG_ENTER("mysql_update");
+ LINT_INIT(used_index);
+
+ if (!(table = open_ltable(thd,table_list,lock_type)))
+ DBUG_RETURN(-1);
+ save_time_stamp=table->time_stamp;
+ table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
+ thd->proc_info="init";
+
+ /*
+ ** Find the offsets of the given fields and condition
+ */
+
+ if (setup_fields(thd,table_list,fields,1,0))
+ DBUG_RETURN(-1);
+ table->grant.want_privilege=(SELECT_ACL & ~table->grant.privilege);
+ if (table->timestamp_field && // Don't set timestamp if used
+ table->timestamp_field->query_id == thd->query_id)
+ table->time_stamp=0;
+ table->used_keys=table->keys_in_use;
+ table->quick_keys=0;
+ if (setup_fields(thd,table_list,values,0,0) ||
+ setup_conds(thd,table_list,&conds))
+ {
+ table->time_stamp=save_time_stamp; // Restore timestamp pointer
+ DBUG_RETURN(-1); /* purecov: inspected */
+ }
+ old_used_keys=table->used_keys;
+ table->used_keys=0; // Can't use 'only index'
+ select=make_select(table,0,0,conds,&error);
+ if (error ||
+ (select && select->check_quick(test(thd->options & SQL_SAFE_UPDATES),
+ limit)))
+ {
+ delete select;
+ table->time_stamp=save_time_stamp; // Restore timestamp pointer
+ if (error)
+ {
+ DBUG_RETURN(-1); // Error in where
+ }
+ send_ok(&thd->net); // No matching records
+ DBUG_RETURN(0);
+ }
+ /* If running in safe sql mode, don't allow updates without keys */
+ if ((thd->options & OPTION_SAFE_UPDATES) && !table->quick_keys &&
+ limit == HA_POS_ERROR)
+ {
+ delete select;
+ table->time_stamp=save_time_stamp;
+ send_error(&thd->net,ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE);
+ DBUG_RETURN(1);
+ }
+
+ /* Check if we are modifying a key that we are used to search with */
+ if (select && select->quick)
+ used_key_is_modified= (!select->quick->unique_key_range() &&
+ check_if_key_used(table,
+ (used_index=select->quick->index),
+ fields));
+ else if ((used_index=table->file->key_used_on_scan) < MAX_KEY)
+ used_key_is_modified=check_if_key_used(table, used_index, fields);
+ else
+ used_key_is_modified=0;
+ if (used_key_is_modified)
+ {
+ /*
+ ** We can't update table directly; We must first search after all
+ ** matching rows before updating the table!
+ */
+ IO_CACHE tempfile;
+ if (open_cached_file(&tempfile, mysql_tmpdir,TEMP_PREFIX,
+ DISK_BUFFER_SIZE, MYF(MY_WME)))
+ {
+ delete select;
+ table->time_stamp=save_time_stamp; // Restore timestamp pointer
+ DBUG_RETURN(-1);
+ }
+ if (old_used_keys & ((key_map) 1 << used_index))
+ {
+ table->key_read=1;
+ table->file->extra(HA_EXTRA_KEYREAD);
+ }
+ init_read_record(&info,thd,table,select,0,1);
+ thd->proc_info="searching";
+
+ while (!(error=info.read_record(&info)) && !thd->killed)
+ {
+ if (!(select && select->skipp_record()))
+ {
+ table->file->position(table->record[0]);
+ if (my_b_write(&tempfile,table->file->ref,
+ table->file->ref_length))
+ {
+ error=1;
+ break;
+ }
+ }
+ else
+ {
+ if (!(test_flags & 512)) /* For debugging */
+ {
+ DBUG_DUMP("record",(char*) table->record[0],table->reclength);
+ }
+ }
+ }
+ end_read_record(&info);
+ if (table->key_read)
+ {
+ table->key_read=0;
+ table->file->extra(HA_EXTRA_NO_KEYREAD);
+ }
+ /* Change select to use tempfile */
+ if (select)
+ {
+ delete select->quick;
+ if (select->free_cond)
+ delete select->cond;
+ select->quick=0;
+ select->cond=0;
+ }
+ else
+ {
+ select= new SQL_SELECT;
+ select->head=table;
+ }
+ if (reinit_io_cache(&tempfile,READ_CACHE,0L,0,0))
+ error=1;
+ select->file=tempfile; // Read row ptrs from this file
+ if (error >= 0)
+ {
+ delete select;
+ table->time_stamp=save_time_stamp; // Restore timestamp pointer
+ DBUG_RETURN(-1);
+ }
+ }
+
+ if (!(test_flags & TEST_READCHECK)) /* For debugging */
+ VOID(table->file->extra(HA_EXTRA_NO_READCHECK));
+ init_read_record(&info,thd,table,select,0,1);
+
+ ha_rows updated=0L,found=0L;
+ thd->count_cuted_fields=1; /* calc cuted fields */
+ thd->cuted_fields=0L;
+ thd->proc_info="updating";
+
+ while (!(error=info.read_record(&info)) && !thd->killed)
+ {
+ if (!(select && select->skipp_record()))
+ {
+ store_record(table,1);
+ if (fill_record(fields,values))
+ break;
+ found++;
+ if (compare_record(table))
+ {
+ if (!(error=table->file->update_row((byte*) table->record[1],
+ (byte*) table->record[0])))
+ {
+ updated++;
+ if (!--limit && using_limit)
+ {
+ error= -1;
+ break;
+ }
+ }
+ else if (handle_duplicates != DUP_IGNORE ||
+ error != HA_ERR_FOUND_DUPP_KEY)
+ {
+ table->file->print_error(error,MYF(0));
+ error= 1;
+ break;
+ }
+ }
+ }
+ }
+ end_read_record(&info);
+ thd->proc_info="end";
+ VOID(table->file->extra(HA_EXTRA_READCHECK));
+ table->time_stamp=save_time_stamp; // Restore auto timestamp pointer
+ if (updated)
+ {
+ mysql_update_log.write(thd->query,thd->query_length);
+ Query_log_event qinfo(thd, thd->query);
+ mysql_bin_log.write(&qinfo);
+ }
+ if (ha_autocommit_or_rollback(thd, error >= 0))
+ error=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 */
+ else
+ {
+ char buff[80];
+ sprintf(buff,ER(ER_UPDATE_INFO), (long) found, (long) updated,
+ (long) thd->cuted_fields);
+ send_ok(&thd->net,
+ (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated,
+ thd->insert_id_used ? thd->insert_id() : 0L,buff);
+ DBUG_PRINT("info",("%d records updated",updated));
+ }
+ thd->count_cuted_fields=0; /* calc cuted fields */
+ DBUG_RETURN(0);
+}
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
new file mode 100644
index 00000000000..21f7ec1c48a
--- /dev/null
+++ b/sql/sql_yacc.yy
@@ -0,0 +1,2767 @@
+/* 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 */
+
+/* sql_yacc.y */
+
+%{
+#define MYSQL_YACC
+#define YYINITDEPTH 100
+#define YYMAXDEPTH 3200 /* Because of 64K stack */
+#define Lex current_lex
+#include "mysql_priv.h"
+#include "sql_acl.h"
+#include "lex_symbol.h"
+#include <myisam.h>
+
+extern void yyerror(const char*);
+int yylex(void *yylval);
+
+#define yyoverflow(A,B,C,D,E,F) if (my_yyoverflow((B),(D),(F))) { yyerror((char*) (A)); return 2; }
+
+inline Item *or_or_concat(Item* A, Item* B)
+{
+ return (current_thd->options & OPTION_ANSI_MODE ?
+ (Item*) new Item_func_concat(A,B) : (Item*) new Item_cond_or(A,B));
+}
+
+%}
+%union {
+ int num;
+ ulong ulong_num;
+ ulonglong ulonglong_num;
+ LEX_STRING lex_str;
+ LEX_STRING *lex_str_ptr;
+ LEX_SYMBOL symbol;
+ Table_ident *table;
+ char *simple_string;
+ Item *item;
+ List<Item> *item_list;
+ List<String> *string_list;
+ Key::Keytype key_type;
+ enum db_type db_type;
+ enum row_type row_type;
+ String *string;
+ key_part_spec *key_part;
+ TABLE_LIST *table_list;
+ udf_func *udf;
+ interval_type interval;
+ LEX_USER *lex_user;
+ enum Item_udftype udf_type;
+}
+
+%{
+bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
+%}
+
+%pure_parser /* We have threads */
+
+%token END_OF_INPUT
+
+%token EQ
+%token EQUAL_SYM
+%token GE
+%token GT_SYM
+%token LE
+%token LT
+%token NE
+%token IS
+%token SHIFT_LEFT
+%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 ADD
+%token ALTER
+%token AFTER_SYM
+%token ANALYZE_SYM
+%token BEGIN_SYM
+%token CHANGE
+%token COMMENT_SYM
+%token COMMIT_SYM
+%token CREATE
+%token CROSS
+%token DELETE_SYM
+%token DROP
+%token INSERT
+%token FLUSH_SYM
+%token SELECT_SYM
+%token MASTER_SYM
+%token REPAIR
+%token SLAVE
+%token START_SYM
+%token STOP_SYM
+%token ROLLBACK_SYM
+%token OPTIMIZE
+%token SHOW
+%token UPDATE_SYM
+%token KILL_SYM
+%token LOAD
+%token LOCK_SYM
+%token UNLOCK_SYM
+
+%token ACTION
+%token AGGREGATE_SYM
+%token ALL
+%token AND
+%token AS
+%token ASC
+%token AUTO_INC
+%token AUTOCOMMIT
+%token AVG_ROW_LENGTH
+%token BERKELEY_DB_SYM
+%token BINARY
+%token BIT_SYM
+%token BOOL_SYM
+%token BOTH
+%token BY
+%token CASCADE
+%token CHANGED_FILES
+%token CHECKSUM_SYM
+%token CHECK_SYM
+%token COLLECTION
+%token COLUMNS
+%token COLUMN_SYM
+%token CONSTRAINT
+%token DATABASES
+%token DATA_SYM
+%token DEFAULT
+%token DELAYED_SYM
+%token DELAY_KEY_WRITE_SYM
+%token DESC
+%token DESCRIBE
+%token DISTINCT
+%token DYNAMIC_SYM
+%token ENCLOSED
+%token ESCAPED
+%token ESCAPE_SYM
+%token EXISTS
+%token EXTENDED_SYM
+%token FILE_SYM
+%token FIRST_SYM
+%token FIXED_SYM
+%token FLOAT_NUM
+%token FOREIGN
+%token FROM
+%token FULL
+%token GRANT
+%token GRANTS
+%token GREATEST_SYM
+%token GROUP
+%token HAVING
+%token HEAP_SYM
+%token HEX_NUM
+%token HIGH_PRIORITY
+%token HOSTS_SYM
+%token IDENT
+%token IGNORE_SYM
+%token INDEX
+%token INFILE
+%token INNER_SYM
+%token INTO
+%token IN_SYM
+%token ISAM_SYM
+%token JOIN_SYM
+%token KEYS
+%token KEY_SYM
+%token LEADING
+%token LEAST_SYM
+%token LEX_HOSTNAME
+%token LIKE
+%token LINES
+%token LOCAL_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 MATCH
+%token MAX_ROWS
+%token MERGE_SYM
+%token MIN_ROWS
+%token MYISAM_SYM
+%token NATIONAL_SYM
+%token NATURAL
+%token NCHAR_SYM
+%token NOT
+%token NO_SYM
+%token NULL_SYM
+%token NUM
+%token ON
+%token OPTION
+%token OPTIONALLY
+%token OR
+%token OR_OR_CONCAT
+%token ORDER_SYM
+%token OUTER
+%token OUTFILE
+%token DUMPFILE
+%token PACK_KEYS_SYM
+%token PARTIAL
+%token PRIMARY_SYM
+%token PRIVILEGES
+%token PROCESS
+%token PROCESSLIST_SYM
+%token RAID_0_SYM
+%token RAID_STRIPED_SYM
+%token RAID_TYPE
+%token RAID_CHUNKS
+%token RAID_CHUNKSIZE
+%token READ_SYM
+%token REAL_NUM
+%token REFERENCES
+%token REGEXP
+%token RELOAD
+%token RENAME
+%token RESTRICT
+%token REVOKE
+%token ROWS_SYM
+%token ROW_FORMAT_SYM
+%token ROW_SYM
+%token SET
+%token SHUTDOWN
+%token STARTING
+%token STATUS_SYM
+%token STRAIGHT_JOIN
+%token TABLES
+%token TABLE_SYM
+%token TEMPORARY
+%token TERMINATED
+%token TEXT_STRING
+%token TO_SYM
+%token TRAILING
+%token TYPE_SYM
+%token FUNC_ARG0
+%token FUNC_ARG1
+%token FUNC_ARG2
+%token FUNC_ARG3
+%token UDF_RETURNS_SYM
+%token UDF_SONAME_SYM
+%token UDF_SYM
+%token UNIQUE_SYM
+%token USAGE
+%token USE_SYM
+%token USING
+%token VALUES
+%token VARIABLES
+%token WHERE
+%token WITH
+%token WRITE_SYM
+%token COMPRESSED_SYM
+
+%token BIGINT
+%token BLOB_SYM
+%token CHAR_SYM
+%token COALESCE
+%token DATETIME
+%token DATE_SYM
+%token DECIMAL_SYM
+%token DOUBLE_SYM
+%token ENUM
+%token FLOAT_SYM
+%token INT_SYM
+%token LIMIT
+%token LONGBLOB
+%token LONGTEXT
+%token MEDIUMBLOB
+%token MEDIUMINT
+%token MEDIUMTEXT
+%token NUMERIC_SYM
+%token PRECISION
+%token QUICK
+%token REAL
+%token SMALLINT
+%token STRING_SYM
+%token TEXT_SYM
+%token TIMESTAMP
+%token TIME_SYM
+%token TINYBLOB
+%token TINYINT
+%token TINYTEXT
+%token UNSIGNED
+%token VARBINARY
+%token VARCHAR
+%token VARYING
+%token ZEROFILL
+
+%token AGAINST
+%token ATAN
+%token BETWEEN_SYM
+%token BIT_AND
+%token BIT_OR
+%token CASE_SYM
+%token CONCAT
+%token CONCAT_WS
+%token CURDATE
+%token CURTIME
+%token DATABASE
+%token DATE_ADD_INTERVAL
+%token DATE_SUB_INTERVAL
+%token DAY_HOUR_SYM
+%token DAY_MINUTE_SYM
+%token DAY_OF_WEEK
+%token DAY_OF_YEAR
+%token DAY_SECOND_SYM
+%token DAY_SYM
+%token DECODE_SYM
+%token ELSE
+%token ELT_FUNC
+%token ENCODE_SYM
+%token ENCRYPT
+%token EXPORT_SET
+%token EXTRACT_SYM
+%token FIELD_FUNC
+%token FORMAT_SYM
+%token FOR_SYM
+%token FROM_UNIXTIME
+%token GROUP_UNIQUE_USERS
+%token HOUR_MINUTE_SYM
+%token HOUR_SECOND_SYM
+%token HOUR_SYM
+%token IDENTIFIED_SYM
+%token IF
+%token INSERT_ID
+%token INTERVAL_SYM
+%token LAST_INSERT_ID
+%token LEFT
+%token LOCATE
+%token MAKE_SET_SYM
+%token MINUTE_SECOND_SYM
+%token MINUTE_SYM
+%token MODIFY_SYM
+%token MONTH_SYM
+%token NOW_SYM
+%token PASSWORD
+%token POSITION_SYM
+%token PROCEDURE
+%token RAND
+%token REPLACE
+%token RIGHT
+%token ROUND
+%token SECOND_SYM
+%token SEC_TO_TIME
+%token SUBSTRING
+%token SUBSTRING_INDEX
+%token TRIM
+%token UDA_CHAR_SUM
+%token UDA_FLOAT_SUM
+%token UDA_INT_SUM
+%token UDF_CHAR_FUNC
+%token UDF_FLOAT_FUNC
+%token UDF_INT_FUNC
+%token UNIQUE_USERS
+%token UNIX_TIMESTAMP
+%token USER
+%token VERSION_SYM
+%token WEEKDAY
+%token WEEK_SYM
+%token WHEN_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 SQL_BIG_RESULT
+%token SQL_BUFFER_RESULT
+%token SQL_WARNINGS
+%token SQL_AUTO_IS_NULL
+%token SQL_SAFE_UPDATES
+
+%left SET_VAR
+%left OR_OR_CONCAT OR
+%left AND
+%left BETWEEN_SYM CASE_SYM WHEN_SYM THEN_SYM ELSE
+%left EQ EQUAL_SYM GE GT_SYM LE LT NE IS LIKE REGEXP IN_SYM
+%left '|'
+%left '&'
+%left SHIFT_LEFT SHIFT_RIGHT
+%left '-' '+'
+%left '*' '/' '%'
+%left NEG '~'
+%right NOT
+%right BINARY
+
+%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
+
+%type <lex_str_ptr>
+ opt_table_alias
+
+%type <table>
+ table_ident
+
+%type <simple_string>
+ remember_name remember_end opt_len opt_ident opt_db text_or_password
+ opt_escape
+
+%type <string>
+ text_string
+
+%type <num>
+ type int_type real_type order_dir opt_field_spec set_option lock_option
+ udf_type if_exists opt_local opt_table_options table_options
+ table_option opt_if_not_exists
+
+%type <ulong_num>
+ ULONG_NUM raid_types
+
+%type <ulonglong_num>
+ ULONGLONG_NUM
+
+%type <item>
+ literal text_literal insert_ident group_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
+
+%type <item_list>
+ expr_list udf_expr_list when_list ident_list
+
+%type <key_type>
+ key_type opt_unique
+
+%type <string_list>
+ key_usage_list
+
+%type <key_part>
+ key_part
+
+%type <table_list>
+ join_table_list join_table
+
+%type <udf>
+ UDF_CHAR_FUNC UDF_FLOAT_FUNC UDF_INT_FUNC
+ UDA_CHAR_SUM UDA_FLOAT_SUM UDA_INT_SUM
+
+%type <interval> interval
+
+%type <db_type> table_types
+
+%type <row_type> row_types
+
+%type <udf_type> udf_func_type
+
+%type <symbol> FUNC_ARG0 FUNC_ARG1 FUNC_ARG2 FUNC_ARG3 keyword
+
+%type <lex_user> user grant_user
+
+%type <NONE>
+ query verb_clause create change select drop insert replace insert2
+ insert_values update delete show describe load alter optimize flush
+ begin commit rollback slave master_def master_defs
+ repair analyze check field_list field_list_item field_spec kill
+ 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
+ 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_outer table_list table opt_option opt_place opt_low_priority
+ 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
+ equal optional_braces opt_key_definition key_usage_list2
+ opt_mi_check_type opt_to mi_check_types normal_join
+ END_OF_INPUT
+
+%type <NONE>
+ '-' '+' '*' '/' '%' '(' ')'
+ ',' '!' '{' '}' '&' '|' AND OR OR_OR_CONCAT BETWEEN_SYM CASE_SYM THEN_SYM WHEN_SYM
+%%
+
+
+query:
+ END_OF_INPUT
+ {
+ if (!current_thd->bootstrap)
+ send_error(&current_thd->net,ER_EMPTY_QUERY);
+ YYABORT;
+ }
+ | verb_clause END_OF_INPUT {}
+
+verb_clause:
+ alter
+ | analyze
+ | begin
+ | change
+ | check
+ | commit
+ | create
+ | delete
+ | describe
+ | drop
+ | grant
+ | insert
+ | flush
+ | load
+ | lock
+ | kill
+ | optimize
+ | repair
+ | replace
+ | revoke
+ | rollback
+ | select
+ | set
+ | slave
+ | show
+ | unlock
+ | update
+ | use
+
+/* change master */
+
+change:
+ CHANGE MASTER_SYM TO_SYM
+ {
+ LEX *lex = Lex;
+ lex->sql_command = SQLCOM_CHANGE_MASTER;
+ memset(&lex->mi, 0, sizeof(lex->mi));
+ } master_defs
+
+master_defs:
+ master_def
+ |
+ master_defs ',' master_def
+
+master_def:
+ MASTER_HOST_SYM EQ TEXT_STRING
+ {
+ Lex->mi.host = $3.str;
+ }
+ |
+ MASTER_USER_SYM EQ TEXT_STRING
+ {
+ Lex->mi.user = $3.str;
+ }
+ |
+ MASTER_PASSWORD_SYM EQ TEXT_STRING
+ {
+ Lex->mi.password = $3.str;
+ }
+ |
+ MASTER_LOG_FILE_SYM EQ TEXT_STRING
+ {
+ Lex->mi.log_file_name = $3.str;
+ }
+ |
+ MASTER_PORT_SYM EQ ULONGLONG_NUM
+ {
+ Lex->mi.port = $3;
+ }
+ |
+ MASTER_LOG_POS_SYM EQ ULONGLONG_NUM
+ {
+ Lex->mi.pos = $3;
+ }
+ |
+ MASTER_CONNECT_RETRY_SYM EQ ULONGLONG_NUM
+ {
+ Lex->mi.connect_retry = $3;
+ }
+
+
+
+/* create a table */
+
+create:
+ CREATE opt_table_options TABLE_SYM opt_if_not_exists table_ident
+ {
+ LEX *lex=Lex;
+ lex->sql_command= SQLCOM_CREATE_TABLE;
+ if (!add_table_to_list($5,
+ ($2 & HA_LEX_CREATE_TMP_TABLE ?
+ &tmp_table_alias : (LEX_STRING*) 0)))
+ YYABORT;
+ lex->create_list.empty();
+ lex->key_list.empty();
+ lex->col_list.empty();
+ 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;
+ }
+ create2
+
+ | CREATE opt_unique INDEX ident ON table_ident
+ {
+ Lex->sql_command= SQLCOM_CREATE_INDEX;
+ if (!add_table_to_list($6,NULL))
+ YYABORT;
+ 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();
+ }
+ | CREATE COLLECTION ident ON table_ident
+ {
+ Lex->sql_command= SQLCOM_CREATE_INDEX;
+ if (!add_table_to_list($5,NULL))
+ YYABORT;
+ Lex->create_list.empty();
+ Lex->key_list.empty();
+ Lex->col_list.empty();
+ Lex->change=NullS;
+ }
+ '(' key_list ')'
+ {
+ Lex->key_list.push_back(new Key(Key::FULLTEXT,$3.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;
+ }
+ | 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;
+ }
+ UDF_RETURNS_SYM udf_type UDF_SONAME_SYM TEXT_STRING
+ {
+ Lex->udf.returns=(Item_result) $7;
+ Lex->udf.dl=$9.str;
+ }
+
+create2:
+ '(' field_list ')' opt_create_table_options create3 {}
+ | opt_create_table_options create3 {}
+
+create3:
+ /* empty*/ {}
+ | opt_duplicate SELECT_SYM
+ {
+ LEX *lex=Lex;
+ lex->where=lex->having=0;
+ lex->select_limit=current_thd->default_select_limit;
+ lex->offset_limit=0L;
+ lex->options=0;
+ lex->exchange = 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_options select_item_list opt_select_from {}
+
+opt_table_options:
+ /* empty */ { $$= 0; }
+ | table_options { $$= $1;}
+
+table_options:
+ table_option { $$=$1; }
+ | table_option table_options { $$= $1 | $2 }
+
+table_option:
+ TEMPORARY { $$=HA_LEX_CREATE_TMP_TABLE; }
+
+opt_if_not_exists:
+ /* empty */ { $$= 0; }
+ | IF NOT EXISTS { $$=HA_LEX_CREATE_IF_NOT_EXISTS; }
+
+opt_create_table_options:
+ /* empty */
+ | create_table_options
+
+create_table_options:
+ create_table_option
+ | 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; }
+ | 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; }
+ | 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; }
+ | RAID_TYPE EQ raid_types { Lex->create_info.raid_type= $3; Lex->create_info.used_fields|= HA_CREATE_USED_RAID;}
+ | RAID_CHUNKS EQ ULONG_NUM { Lex->create_info.raid_chunks= $3; Lex->create_info.used_fields|= HA_CREATE_USED_RAID;}
+ | RAID_CHUNKSIZE EQ ULONG_NUM { Lex->create_info.raid_chunksize= $3*RAID_BLOCK_SIZE; Lex->create_info.used_fields|= HA_CREATE_USED_RAID;}
+
+table_types:
+ ISAM_SYM { $$= DB_TYPE_ISAM; }
+ | MYISAM_SYM { $$= DB_TYPE_MYISAM; }
+ | MERGE_SYM { $$= DB_TYPE_MRG_MYISAM; }
+ | HEAP_SYM { $$= DB_TYPE_HEAP; }
+ | BERKELEY_DB_SYM { $$= DB_TYPE_BERKELEY_DB; }
+
+row_types:
+ DEFAULT { $$= ROW_TYPE_DEFAULT; }
+ | FIXED_SYM { $$= ROW_TYPE_FIXED; }
+ | DYNAMIC_SYM { $$= ROW_TYPE_DYNAMIC; }
+ | COMPRESSED_SYM { $$= ROW_TYPE_COMPRESSED; }
+
+raid_types:
+ RAID_STRIPED_SYM { $$= RAID_TYPE_0; }
+ | RAID_0_SYM { $$= RAID_TYPE_0; }
+ | ULONG_NUM { $$=$1;}
+
+opt_select_from:
+ /* empty */
+ | select_from
+
+udf_func_type:
+ /* empty */ { $$ = UDFTYPE_FUNCTION; }
+ | AGGREGATE_SYM { $$ = UDFTYPE_AGGREGATE; }
+
+udf_type:
+ STRING_SYM {$$ = (int) STRING_RESULT; }
+ | REAL {$$ = (int) REAL_RESULT; }
+ | INT_SYM {$$ = (int) INT_RESULT; }
+
+field_list:
+ field_list_item
+ | field_list ',' field_list_item
+
+
+field_list_item:
+ field_spec
+ | field_spec references
+ {
+ Lex->col_list.empty(); /* Alloced by sql_alloc */
+ }
+ | 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 */
+ }
+ | opt_constraint FOREIGN KEY_SYM '(' key_list ')' references
+ {
+ Lex->col_list.empty(); /* Alloced by sql_alloc */
+ }
+ | opt_constraint CHECK_SYM '(' expr ')'
+ {
+ Lex->col_list.empty(); /* Alloced by sql_alloc */
+ }
+
+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;
+ }
+ type opt_attribute
+ {
+ 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))
+ YYABORT;
+ }
+
+type:
+ int_type opt_len field_options { Lex->length=$2; $$=$1; }
+ | real_type opt_precision field_options { $$=$1; }
+ | FLOAT_SYM float_options field_options { $$=FIELD_TYPE_FLOAT; }
+ | BIT_SYM opt_len { Lex->length=(char*) "1";
+ $$=FIELD_TYPE_TINY; }
+ | BOOL_SYM { Lex->length=(char*) "1";
+ $$=FIELD_TYPE_TINY; }
+ | char '(' NUM ')' opt_binary { Lex->length=$3.str;
+ $$=FIELD_TYPE_STRING; }
+ | char opt_binary { Lex->length=(char*) "1";
+ $$=FIELD_TYPE_STRING; }
+ | BINARY '(' NUM ')' { Lex->length=$3.str;
+ Lex->type|=BINARY_FLAG;
+ $$=FIELD_TYPE_STRING; }
+ | varchar '(' NUM ')' opt_binary { Lex->length=$3.str;
+ $$=FIELD_TYPE_VAR_STRING; }
+ | VARBINARY '(' NUM ')' { Lex->length=$3.str;
+ Lex->type|=BINARY_FLAG;
+ $$=FIELD_TYPE_VAR_STRING; }
+ | YEAR_SYM opt_len field_options { $$=FIELD_TYPE_YEAR; Lex->length=$2; }
+ | DATE_SYM { $$=FIELD_TYPE_DATE; }
+ | TIME_SYM { $$=FIELD_TYPE_TIME; }
+ | TIMESTAMP { $$=FIELD_TYPE_TIMESTAMP; }
+ | TIMESTAMP '(' NUM ')' { Lex->length=$3.str;
+ $$=FIELD_TYPE_TIMESTAMP; }
+ | DATETIME { $$=FIELD_TYPE_DATETIME; }
+ | TINYBLOB { Lex->type|=BINARY_FLAG;
+ $$=FIELD_TYPE_TINY_BLOB; }
+ | BLOB_SYM { Lex->type|=BINARY_FLAG;
+ $$=FIELD_TYPE_BLOB; }
+ | MEDIUMBLOB { Lex->type|=BINARY_FLAG;
+ $$=FIELD_TYPE_MEDIUM_BLOB; }
+ | LONGBLOB { Lex->type|=BINARY_FLAG;
+ $$=FIELD_TYPE_LONG_BLOB; }
+ | LONG_SYM VARBINARY { Lex->type|=BINARY_FLAG;
+ $$=FIELD_TYPE_MEDIUM_BLOB; }
+ | LONG_SYM varchar { $$=FIELD_TYPE_MEDIUM_BLOB; }
+ | TINYTEXT { $$=FIELD_TYPE_TINY_BLOB; }
+ | TEXT_SYM { $$=FIELD_TYPE_BLOB; }
+ | MEDIUMTEXT { $$=FIELD_TYPE_MEDIUM_BLOB; }
+ | LONGTEXT { $$=FIELD_TYPE_LONG_BLOB; }
+ | DECIMAL_SYM float_options field_options
+ { $$=FIELD_TYPE_DECIMAL;}
+ | NUMERIC_SYM float_options field_options
+ { $$=FIELD_TYPE_DECIMAL;}
+ | ENUM {Lex->interval_list.empty();} '(' string_list ')'
+ {
+ Lex->interval=typelib(Lex->interval_list);
+ $$=FIELD_TYPE_ENUM;
+ }
+ | SET { Lex->interval_list.empty();} '(' string_list ')'
+ {
+ Lex->interval=typelib(Lex->interval_list);
+ $$=FIELD_TYPE_SET;
+ }
+
+char:
+ CHAR_SYM {}
+ | NCHAR_SYM {}
+ | NATIONAL_SYM CHAR_SYM {}
+
+varchar:
+ char VARYING {}
+ | VARCHAR {}
+ | NATIONAL_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; }
+
+real_type:
+ REAL { $$= current_thd->options & OPTION_ANSI_MODE ?
+ FIELD_TYPE_FLOAT : FIELD_TYPE_DOUBLE; }
+ | DOUBLE_SYM { $$=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; }
+
+field_options:
+ /* empty */ {}
+ | field_opt_list {}
+
+field_opt_list:
+ field_opt_list field_option {}
+ | field_option {}
+
+field_option:
+ UNSIGNED { Lex->type|= UNSIGNED_FLAG;}
+ | ZEROFILL { Lex->type|= UNSIGNED_FLAG | ZEROFILL_FLAG; }
+
+opt_len:
+ /* empty */ { $$=(char*) 0; } /* use default length */
+ | '(' NUM ')' { $$=$2.str; }
+
+opt_precision:
+ /* empty */ {}
+ | '(' NUM ',' NUM ')' { Lex->length=$2.str; Lex->dec=$4.str; }
+
+opt_attribute:
+ /* empty */ {}
+ | opt_attribute_list {}
+
+opt_attribute_list:
+ opt_attribute_list attribute {}
+ | attribute
+
+attribute:
+ NULL_SYM { Lex->type&= ~ NOT_NULL_FLAG; }
+ | NOT NULL_SYM { Lex->type|= NOT_NULL_FLAG; }
+ | DEFAULT literal { Lex->default_value=$2; }
+ | AUTO_INC { Lex->type|= AUTO_INCREMENT_FLAG | NOT_NULL_FLAG; }
+ | 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; }
+
+opt_binary:
+ /* empty */ {}
+ | BINARY { Lex->type|=BINARY_FLAG; }
+
+references:
+ REFERENCES table_ident opt_on_delete {}
+ | 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_item {}
+ | opt_on_delete_item {}
+
+
+opt_on_delete_item:
+ ON DELETE_SYM delete_option {}
+ | ON UPDATE_SYM delete_option {}
+ | MATCH FULL {}
+ | MATCH PARTIAL {}
+
+delete_option:
+ RESTRICT {}
+ | CASCADE {}
+ | SET NULL_SYM {}
+ | NO_SYM ACTION {}
+ | SET DEFAULT {}
+
+key_type:
+ opt_constraint PRIMARY_SYM KEY_SYM { $$= Key::PRIMARY; }
+ | key_or_index { $$= Key::MULTIPLE; }
+ | opt_constraint UNIQUE_SYM { $$= Key::UNIQUE; }
+ | opt_constraint UNIQUE_SYM key_or_index { $$= Key::UNIQUE; }
+
+key_or_index:
+ KEY_SYM {}
+ | INDEX {}
+
+keys_or_index:
+ KEYS {}
+ | INDEX {}
+
+opt_unique:
+ /* empty */ { $$= Key::MULTIPLE; }
+ | UNIQUE_SYM { $$= Key::UNIQUE; }
+
+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:
+ ident { $$=new key_part_spec($1.str); }
+ | ident '(' NUM ')' { $$=new key_part_spec($1.str,(uint) atoi($3.str)); }
+
+opt_ident:
+ /* empty */ { $$=(char*) 0; } /* Defaultlength */
+ | field_ident { $$=$1.str; }
+
+string_list:
+ text_string { Lex->interval_list.push_back($1); }
+ | string_list ',' text_string { Lex->interval_list.push_back($3); }
+
+/*
+** Alter table
+*/
+
+alter:
+ ALTER opt_ignore TABLE_SYM table_ident
+ {
+ LEX *lex=Lex;
+ lex->sql_command = SQLCOM_ALTER_TABLE;
+ lex->name=0;
+ if (!add_table_to_list($4, NULL))
+ YYABORT;
+ lex->drop_primary=0;
+ lex->create_list.empty();
+ lex->key_list.empty();
+ lex->col_list.empty();
+ lex->drop_list.empty();
+ lex->alter_list.empty();
+ lex->db=lex->name=0;
+ bzero((char*) &lex->create_info,sizeof(lex->create_info));
+ lex->create_info.db_type= DB_TYPE_DEFAULT;
+ }
+ alter_list opt_create_table_options
+
+alter_list:
+ alter_list_item
+ | alter_list ',' alter_list_item
+
+add_column:
+ 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
+ | MODIFY_SYM opt_column field_ident
+ {
+ Lex->length=Lex->dec=0; Lex->type=0; Lex->interval=0;
+ Lex->default_value=0;
+ }
+ type opt_attribute
+ {
+ 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;
+ }
+ | 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 {}
+ | DROP key_or_index field_ident
+ { Lex->drop_list.push_back(new Alter_drop(Alter_drop::KEY,
+ $3.str)); }
+ | ALTER opt_column field_ident SET DEFAULT literal
+ { Lex->alter_list.push_back(new Alter_column($3.str,$6)); }
+ | 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_option
+
+opt_column:
+ /* empty */ {}
+ | COLUMN_SYM {}
+
+opt_ignore:
+ /* empty */ { Lex->duplicates=DUP_ERROR; }
+ | IGNORE_SYM { Lex->duplicates=DUP_IGNORE; }
+
+opt_restrict:
+ /* empty */ {}
+ | RESTRICT {}
+ | CASCADE {}
+
+opt_place:
+ /* empty */ {}
+ | AFTER_SYM ident { store_position_for_column($2.str); }
+ | FIRST_SYM { store_position_for_column(first_keyword); }
+
+opt_to:
+ /* empty */ {}
+ | TO_SYM {}
+
+slave:
+ SLAVE START_SYM
+ {
+ Lex->sql_command = SQLCOM_SLAVE_START;
+ Lex->type = 0;
+ }
+ |
+ SLAVE STOP_SYM
+ {
+ Lex->sql_command = SQLCOM_SLAVE_STOP;
+ Lex->type = 0;
+ };
+
+repair:
+ REPAIR table_or_tables
+ {
+ Lex->sql_command = SQLCOM_REPAIR;
+ Lex->check_opt.init();
+ }
+ table_list opt_mi_check_type
+
+
+opt_mi_check_type:
+ /* empty */ { Lex->check_opt.flags = T_MEDIUM; }
+ | TYPE_SYM EQ mi_check_types {}
+
+mi_check_types:
+ QUICK { Lex->check_opt.quick = 1; }
+ | EXTENDED_SYM { Lex->check_opt.flags = T_EXTEND; }
+
+analyze:
+ ANALYZE_SYM table_or_tables table_list
+ {
+ Lex->sql_command = SQLCOM_ANALYZE;
+ Lex->check_opt.init();
+ }
+
+check:
+ CHECK_SYM table_or_tables
+ {
+ Lex->sql_command = SQLCOM_CHECK;
+ Lex->check_opt.init();
+ }
+ table_list opt_mi_check_type
+
+optimize:
+ OPTIMIZE table_or_tables table_ident
+ {
+ Lex->sql_command = SQLCOM_OPTIMIZE;
+ if (!add_table_to_list($3, NULL))
+ YYABORT;
+ }
+
+/*
+** Select : retrieve data from table
+*/
+
+
+select:
+ SELECT_SYM
+ {
+ LEX *lex=Lex;
+ lex->where=lex->having=0;
+ lex->select_limit=current_thd->default_select_limit;
+ lex->offset_limit=0L;
+ lex->options=0;
+ lex->sql_command= SQLCOM_SELECT;
+ lex->exchange = 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_options select_item_list select_into
+
+select_into:
+ /* empty */
+ | select_from
+ | opt_into select_from
+ | select_from opt_into
+
+select_from:
+ FROM join_table_list where_clause group_clause having_clause order_clause limit_clause procedure_clause
+
+
+select_options:
+ /* empty*/
+ | select_option_list
+
+select_option_list:
+ select_option_list select_option
+ | select_option
+
+select_option:
+ STRAIGHT_JOIN { Lex->options|= SELECT_STRAIGHT_JOIN; }
+ | HIGH_PRIORITY { Lex->options|= SELECT_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; }
+ | ALL {}
+
+select_item_list:
+ select_item_list ',' select_item
+ | select_item
+ | '*'
+ {
+ if (add_item_to_list(new Item_field(NULL,NULL,"*")))
+ YYABORT;
+ }
+
+
+select_item:
+ remember_name select_item2 remember_end select_alias
+ {
+ if (add_item_to_list($2))
+ YYABORT;
+ if ($4.str)
+ $2->set_name($4.str);
+ else if (!$2->name)
+ $2->set_name($1,(uint) ($3 - $1));
+ }
+
+remember_name:
+ { $$=(char*) Lex->tok_start; }
+
+remember_end:
+ { $$=(char*) Lex->tok_end; }
+
+select_item2:
+ table_wild { $$=$1; } /* table.* */
+ | expr { $$=$1; }
+
+select_alias:
+ { $$.str=0;}
+ | AS ident { $$=$2; }
+ | AS TEXT_STRING { $$=$2; }
+ | ident { $$=$1; }
+ | TEXT_STRING { $$=$1; }
+
+optional_braces:
+ /* empty */ {}
+ | '(' ')' {}
+
+/* all possible expressions */
+expr: expr_expr {$$ = $1; }
+ | simple_expr {$$ = $1; }
+
+/* expressions that begin with 'expr' */
+expr_expr:
+ expr IN_SYM '(' expr_list ')'
+ { $$= new Item_func_in($1,*$4); }
+ | expr NOT IN_SYM '(' expr_list ')'
+ { $$= new Item_func_not(new Item_func_in($1,*$5)); }
+ | expr BETWEEN_SYM no_and_expr AND expr
+ { $$= new Item_func_between($1,$3,$5); }
+ | expr NOT BETWEEN_SYM no_and_expr AND 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 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));}
+ | expr REGEXP expr { $$= new Item_func_regex($1,$3); }
+ | expr NOT REGEXP expr { $$= new Item_func_not(new Item_func_regex($1,$4)); }
+ | expr IS NULL_SYM { $$= new Item_func_isnull($1); }
+ | expr IS NOT NULL_SYM { $$= new Item_func_isnotnull($1); }
+ | expr EQ expr { $$= new Item_func_eq($1,$3); }
+ | expr EQUAL_SYM expr { $$= new Item_func_equal($1,$3); }
+ | expr GE expr { $$= new Item_func_ge($1,$3); }
+ | expr GT_SYM expr { $$= new Item_func_gt($1,$3); }
+ | expr LE expr { $$= new Item_func_le($1,$3); }
+ | expr LT expr { $$= new Item_func_lt($1,$3); }
+ | expr NE expr { $$= new Item_func_ne($1,$3); }
+ | expr SHIFT_LEFT expr { $$= new Item_func_shift_left($1,$3); }
+ | expr SHIFT_RIGHT expr { $$= new Item_func_shift_right($1,$3); }
+ | expr '+' expr { $$= new Item_func_plus($1,$3); }
+ | expr '-' expr { $$= new Item_func_minus($1,$3); }
+ | 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_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); }
+
+/* expressions that begin with 'expr' that do NOT follow IN_SYM */
+no_in_expr:
+ no_in_expr BETWEEN_SYM no_and_expr AND expr
+ { $$= new Item_func_between($1,$3,$5); }
+ | no_in_expr NOT BETWEEN_SYM no_and_expr AND 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 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)); }
+ | no_in_expr REGEXP expr { $$= new Item_func_regex($1,$3); }
+ | no_in_expr NOT REGEXP expr { $$= new Item_func_not(new Item_func_regex($1,$4)); }
+ | no_in_expr IS NULL_SYM { $$= new Item_func_isnull($1); }
+ | no_in_expr IS NOT NULL_SYM { $$= new Item_func_isnotnull($1); }
+ | no_in_expr EQ expr { $$= new Item_func_eq($1,$3); }
+ | no_in_expr EQUAL_SYM expr { $$= new Item_func_equal($1,$3); }
+ | no_in_expr GE expr { $$= new Item_func_ge($1,$3); }
+ | no_in_expr GT_SYM expr { $$= new Item_func_gt($1,$3); }
+ | no_in_expr LE expr { $$= new Item_func_le($1,$3); }
+ | no_in_expr LT expr { $$= new Item_func_lt($1,$3); }
+ | no_in_expr NE expr { $$= new Item_func_ne($1,$3); }
+ | no_in_expr SHIFT_LEFT expr { $$= new Item_func_shift_left($1,$3); }
+ | no_in_expr SHIFT_RIGHT expr { $$= new Item_func_shift_right($1,$3); }
+ | no_in_expr '+' expr { $$= new Item_func_plus($1,$3); }
+ | no_in_expr '-' expr { $$= new Item_func_minus($1,$3); }
+ | 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_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
+
+/* expressions that begin with 'expr' that does NOT follow AND */
+no_and_expr:
+ no_and_expr IN_SYM '(' expr_list ')'
+ { $$= new Item_func_in($1,*$4); }
+ | no_and_expr NOT IN_SYM '(' expr_list ')'
+ { $$= new Item_func_not(new Item_func_in($1,*$5)); }
+ | no_and_expr BETWEEN_SYM no_and_expr AND expr
+ { $$= new Item_func_between($1,$3,$5); }
+ | no_and_expr NOT BETWEEN_SYM no_and_expr 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 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); }
+ | no_and_expr NOT REGEXP expr { $$= new Item_func_not(new Item_func_regex($1,$4)); }
+ | no_and_expr IS NULL_SYM { $$= new Item_func_isnull($1); }
+ | no_and_expr IS NOT NULL_SYM { $$= new Item_func_isnotnull($1); }
+ | no_and_expr EQ expr { $$= new Item_func_eq($1,$3); }
+ | no_and_expr EQUAL_SYM expr { $$= new Item_func_equal($1,$3); }
+ | no_and_expr GE expr { $$= new Item_func_ge($1,$3); }
+ | no_and_expr GT_SYM expr { $$= new Item_func_gt($1,$3); }
+ | no_and_expr LE expr { $$= new Item_func_le($1,$3); }
+ | no_and_expr LT expr { $$= new Item_func_lt($1,$3); }
+ | no_and_expr NE expr { $$= new Item_func_ne($1,$3); }
+ | no_and_expr SHIFT_LEFT expr { $$= new Item_func_shift_left($1,$3); }
+ | no_and_expr SHIFT_RIGHT expr { $$= new Item_func_shift_right($1,$3); }
+ | no_and_expr '+' expr { $$= new Item_func_plus($1,$3); }
+ | no_and_expr '-' expr { $$= new Item_func_minus($1,$3); }
+ | 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_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_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); }
+ | sum_expr
+ | '-' expr %prec NEG { $$= new Item_func_neg($2); }
+ | '~' expr %prec NEG { $$= new Item_func_bit_neg($2); }
+ | NOT expr %prec NEG { $$= new Item_func_not($2); }
+ | '!' 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))); }
+ | BINARY expr %prec NEG { $$= new Item_func_binary($2); }
+ | CASE_SYM opt_expr WHEN_SYM when_list opt_else END
+ { $$= new Item_func_case(* $4, $2, $5 ) }
+ | FUNC_ARG0 '(' ')'
+ { $$= ((Item*(*)(void))($1.symbol->create_func))();}
+ | FUNC_ARG1 '(' expr ')'
+ { $$= ((Item*(*)(Item*))($1.symbol->create_func))($3);}
+ | FUNC_ARG2 '(' expr ',' expr ')'
+ { $$= ((Item*(*)(Item*,Item*))($1.symbol->create_func))($3,$5);}
+ | FUNC_ARG3 '(' expr ',' expr ',' expr ')'
+ { $$= ((Item*(*)(Item*,Item*,Item*))($1.symbol->create_func))($3,$5,$7);}
+ | ATAN '(' expr ')'
+ { $$= new Item_func_atan($3); }
+ | ATAN '(' expr ',' expr ')'
+ { $$= new Item_func_atan($3,$5); }
+ | CHAR_SYM '(' expr_list ')'
+ { $$= new Item_func_char(*$3); }
+ | COALESCE '(' expr_list ')'
+ { $$= new Item_func_coalesce(* $3); }
+ | CONCAT '(' expr_list ')'
+ { $$= new Item_func_concat(* $3); }
+ | CONCAT_WS '(' expr ',' expr_list ')'
+ { $$= new Item_func_concat_ws($3, *$5); }
+ | CURDATE optional_braces
+ { $$= new Item_func_curdate(); }
+ | CURTIME optional_braces
+ { $$= new Item_func_curtime(); }
+ | CURTIME '(' expr ')'
+ { $$= new Item_func_curtime($3); }
+ | 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(); }
+ | 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 ',' 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); }
+ | EXPORT_SET '(' expr ',' expr ',' expr ')'
+ { $$= new Item_func_export_set($3, $5, $7); }
+ | EXPORT_SET '(' expr ',' expr ',' expr ',' expr ')'
+ { $$= new Item_func_export_set($3, $5, $7, $9); }
+ | EXPORT_SET '(' expr ',' expr ',' expr ',' expr ',' expr ')'
+ { $$= new Item_func_export_set($3, $5, $7, $9, $11); }
+ | FORMAT_SYM '(' expr ',' NUM ')'
+ { $$= new Item_func_format($3,atoi($5.str)); }
+ | FROM_UNIXTIME '(' expr ')'
+ { $$= new Item_func_from_unixtime($3); }
+ | FROM_UNIXTIME '(' expr ',' expr ')'
+ {
+ $$= new Item_func_date_format(new Item_func_from_unixtime($3),$5,0);
+ }
+ | FIELD_FUNC '(' expr ',' expr_list ')'
+ { $$= new Item_func_field($3, *$5); }
+ | HOUR_SYM '(' expr ')'
+ { $$= new Item_func_hour($3); }
+ | IF '(' expr ',' expr ',' expr ')'
+ { $$= new Item_func_if($3,$5,$7); }
+ | INSERT '(' expr ',' expr ',' expr ',' expr ')'
+ { $$= new Item_func_insert($3,$5,$7,$9); }
+ | INTERVAL_SYM expr interval '+' expr
+ /* we cannot put interval before - */
+ { $$= new Item_date_add_interval($5,$2,$3,0); }
+ | INTERVAL_SYM '(' expr ',' expr_list ')'
+ { $$= new Item_func_interval($3,* $5); }
+ | LAST_INSERT_ID '(' ')'
+ {
+ $$= new Item_int((char*) "last_insert_id()",
+ current_thd->insert_id(),21);
+ }
+ | LAST_INSERT_ID '(' expr ')'
+ {
+ $$= new Item_func_set_last_insert_id($3);
+ }
+ | LEFT '(' expr ',' expr ')'
+ { $$= new Item_func_left($3,$5); }
+ | LOCATE '(' expr ',' expr ')'
+ { $$= new Item_func_locate($5,$3); }
+ | LOCATE '(' expr ',' expr ',' expr ')'
+ { $$= new Item_func_locate($5,$3,$7); }
+ | GREATEST_SYM '(' expr ',' expr_list ')'
+ { $5->push_front($3); $$= new Item_func_max(*$5); }
+ | LEAST_SYM '(' expr ',' expr_list ')'
+ { $5->push_front($3); $$= new Item_func_min(*$5); }
+ | MINUTE_SYM '(' expr ')'
+ { $$= new Item_func_minute($3); }
+ | MONTH_SYM '(' expr ')'
+ { $$= new Item_func_month($3); }
+ | NOW_SYM optional_braces
+ { $$= new Item_func_now(); }
+ | NOW_SYM '(' expr ')'
+ { $$= new Item_func_now($3); }
+ | 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(); }
+ | REPLACE '(' expr ',' expr ',' expr ')'
+ { $$= new Item_func_replace($3,$5,$7); }
+ | RIGHT '(' expr ',' expr ')'
+ { $$= new Item_func_right($3,$5); }
+ | ROUND '(' expr ')'
+ { $$= new Item_func_round($3, new Item_int((char*)"0",0,1),0); }
+ | ROUND '(' expr ',' expr ')' { $$= new Item_func_round($3,$5,0); }
+ | SECOND_SYM '(' expr ')'
+ { $$= new Item_func_second($3); }
+ | SUBSTRING '(' expr ',' expr ',' expr ')'
+ { $$= new Item_func_substr($3,$5,$7); }
+ | SUBSTRING '(' expr ',' expr ')'
+ { $$= new Item_func_substr($3,$5); }
+ | SUBSTRING '(' expr FROM expr FOR_SYM expr ')'
+ { $$= new Item_func_substr($3,$5,$7); }
+ | SUBSTRING '(' expr FROM expr ')'
+ { $$= new Item_func_substr($3,$5); }
+ | SUBSTRING_INDEX '(' expr ',' expr ',' expr ')'
+ { $$= new Item_func_substr_index($3,$5,$7); }
+ | TRIM '(' expr ')'
+ { $$= new Item_func_trim($3,new Item_string(" ",1)); }
+ | TRIM '(' LEADING opt_pad FROM expr ')'
+ { $$= new Item_func_ltrim($6,$4); }
+ | TRIM '(' TRAILING opt_pad FROM expr ')'
+ { $$= new Item_func_rtrim($6,$4); }
+ | TRIM '(' BOTH opt_pad FROM expr ')'
+ { $$= new Item_func_trim($6,$4); }
+ | TRIM '(' expr FROM expr ')'
+ { $$= new Item_func_trim($5,$3); }
+
+ | UDA_CHAR_SUM '(' udf_expr_list ')'
+ {
+ if ($3 != NULL)
+ $$ = new Item_sum_udf_str($1, *$3);
+ else
+ $$ = new Item_sum_udf_str($1);
+ }
+ | UDA_FLOAT_SUM '(' udf_expr_list ')'
+ {
+ if ($3 != NULL)
+ $$ = new Item_sum_udf_float($1, *$3);
+ else
+ $$ = new Item_sum_udf_float($1);
+ }
+ | UDA_INT_SUM '(' udf_expr_list ')'
+ {
+ if ($3 != NULL)
+ $$ = new Item_sum_udf_int($1, *$3);
+ else
+ $$ = new Item_sum_udf_int($1);
+ }
+ | UDF_CHAR_FUNC '(' udf_expr_list ')'
+ {
+ if ($3 != NULL)
+ $$ = new Item_func_udf_str($1, *$3);
+ else
+ $$ = new Item_func_udf_str($1);
+ }
+ | UDF_FLOAT_FUNC '(' udf_expr_list ')'
+ {
+ if ($3 != NULL)
+ $$ = new Item_func_udf_float($1, *$3);
+ else
+ $$ = new Item_func_udf_float($1);
+ }
+ | UDF_INT_FUNC '(' udf_expr_list ')'
+ {
+ if ($3 != NULL)
+ $$ = new Item_func_udf_int($1, *$3);
+ else
+ $$ = 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); }
+ | UNIX_TIMESTAMP '(' ')'
+ { $$= new Item_func_unix_timestamp(); }
+ | UNIX_TIMESTAMP '(' expr ')'
+ { $$= new Item_func_unix_timestamp($3); }
+ | USER '(' ')'
+ { $$= new Item_func_user(); }
+ | WEEK_SYM '(' expr ')'
+ { $$= new Item_func_week($3,new Item_int((char*) "0",0,1)); }
+ | WEEK_SYM '(' expr ',' expr ')'
+ { $$= new Item_func_week($3,$5); }
+ | YEAR_SYM '(' expr ')'
+ { $$= new Item_func_year($3); }
+ | YEARWEEK '(' expr ')'
+ { $$= new Item_func_yearweek($3,new Item_int((char*) "0",0,1)); }
+ | YEARWEEK '(' expr ',' expr ')'
+ { $$= new Item_func_yearweek($3, $5); }
+ | BENCHMARK_SYM '(' ULONG_NUM ',' expr ')'
+ { $$=new Item_func_benchmark($3,$5); }
+ | EXTRACT_SYM '(' interval FROM expr ')'
+ { $$=new Item_extract( $3, $5); }
+
+udf_expr_list:
+ /* empty */ { $$= NULL; }
+ | expr_list { $$= $1;}
+
+sum_expr:
+ AVG_SYM '(' in_sum_expr ')'
+ { $$=new Item_sum_avg($3); }
+ | BIT_AND '(' in_sum_expr ')'
+ { $$=new Item_sum_and($3); }
+ | BIT_OR '(' in_sum_expr ')'
+ { $$=new Item_sum_or($3); }
+ | COUNT_SYM '(' '*' ')'
+ { $$=new Item_sum_count(new Item_int((int32) 0L,1)); }
+ | COUNT_SYM '(' in_sum_expr ')'
+ { $$=new Item_sum_count($3); }
+ | COUNT_SYM '(' DISTINCT expr_list ')'
+ { $$=new Item_sum_count_distinct(* $4); }
+ | GROUP_UNIQUE_USERS '(' text_literal ',' NUM ',' NUM ',' in_sum_expr ')'
+ { $$= new Item_sum_unique_users($3,atoi($5.str),atoi($7.str),$9); }
+ | MIN_SYM '(' in_sum_expr ')'
+ { $$=new Item_sum_min($3); }
+ | MAX_SYM '(' in_sum_expr ')'
+ { $$=new Item_sum_max($3); }
+ | STD_SYM '(' in_sum_expr ')'
+ { $$=new Item_sum_std($3); }
+ | SUM_SYM '(' in_sum_expr ')'
+ { $$=new Item_sum_sum($3); }
+
+in_sum_expr:
+ { Lex->in_sum_expr++ }
+ expr
+ {
+ Lex->in_sum_expr--;
+ $$=$2;
+ }
+
+expr_list:
+ { Lex->expr_list.push_front(new List<Item>); }
+ expr_list2
+ { $$= Lex->expr_list.pop(); }
+
+expr_list2:
+ expr { Lex->expr_list.head()->push_back($1); }
+ | expr_list2 ',' expr { Lex->expr_list.head()->push_back($3); }
+
+ident_list:
+ { Lex->expr_list.push_front(new List<Item>); }
+ ident_list2
+ { $$= Lex->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); }
+
+opt_expr:
+ /* empty */ { $$= NULL; }
+ | expr { $$= $1; }
+
+opt_else:
+ /* empty */ { $$= NULL; }
+ | ELSE expr { $$= $2; }
+
+when_list:
+ { Lex->when_list.push_front(new List<Item>) }
+ when_list2
+ { $$= Lex->when_list.pop(); }
+
+when_list2:
+ expr THEN_SYM expr
+ {
+ Lex->when_list.head()->push_back($1);
+ Lex->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);
+ }
+
+opt_pad:
+ /* empty */ { $$=new Item_string(" ",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->name;
+ Lex->db2=$4->db; Lex->table2=$4->name; }
+ USING '(' using_list ')'
+ { add_join_on($4,$8); $$=$4; }
+ | join_table_list LEFT opt_outer JOIN_SYM join_table ON expr
+ { add_join_on($5,$7); $5->outer_join=1; $$=$5; }
+ | join_table_list LEFT opt_outer JOIN_SYM join_table
+ { Lex->db1=$1->db; Lex->table1=$1->name;
+ Lex->db2=$5->db; Lex->table2=$5->name; }
+ USING '(' using_list ')'
+ { add_join_on($5,$9); $5->outer_join=1; $$=$5; }
+ | join_table_list NATURAL LEFT opt_outer JOIN_SYM join_table
+ { add_join_natural($1,$6); $6->outer_join=1; $$=$6; }
+ | join_table_list NATURAL JOIN_SYM join_table
+ { add_join_natural($1,$4); $$=$4; }
+
+normal_join:
+ ',' {}
+ | JOIN_SYM {}
+ | CROSS JOIN_SYM {}
+
+join_table:
+ { Lex->use_index_ptr=Lex->ignore_index_ptr=0; }
+ table_ident opt_table_alias opt_key_definition
+ { if (!($$=add_table_to_list($2,$3,TL_UNLOCK, Lex->use_index_ptr,
+ Lex->ignore_index_ptr))) YYABORT; }
+ | '{' ident join_table LEFT OUTER JOIN_SYM join_table ON expr '}'
+ { add_join_on($7,$9); $7->outer_join=1; $$=$7; }
+
+opt_outer:
+ /* empty */ {}
+ | OUTER {}
+
+opt_key_definition:
+ /* empty */ {}
+ | USE_SYM key_usage_list
+ { Lex->use_index= *$2; Lex->use_index_ptr= &Lex->use_index; }
+ | IGNORE_SYM key_usage_list
+ { Lex->ignore_index= *$2; Lex->ignore_index_ptr= &Lex->ignore_index;}
+
+key_usage_list:
+ key_or_index { Lex->interval_list.empty() } '(' key_usage_list2 ')'
+ { $$= &Lex->interval_list; }
+
+key_usage_list2:
+ key_usage_list2 ',' ident
+ { Lex->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)); }
+ | PRIMARY_SYM
+ { Lex->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))))
+ 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)))
+ YYABORT;
+ }
+
+interval:
+ DAY_HOUR_SYM { $$=INTERVAL_DAY_HOUR; }
+ | DAY_MINUTE_SYM { $$=INTERVAL_DAY_MINUTE; }
+ | DAY_SECOND_SYM { $$=INTERVAL_DAY_SECOND; }
+ | DAY_SYM { $$=INTERVAL_DAY; }
+ | HOUR_MINUTE_SYM { $$=INTERVAL_HOUR_MINUTE; }
+ | HOUR_SECOND_SYM { $$=INTERVAL_HOUR_SECOND; }
+ | HOUR_SYM { $$=INTERVAL_HOUR; }
+ | MINUTE_SECOND_SYM { $$=INTERVAL_MINUTE_SECOND; }
+ | MINUTE_SYM { $$=INTERVAL_MINUTE; }
+ | MONTH_SYM { $$=INTERVAL_MONTH; }
+ | SECOND_SYM { $$=INTERVAL_SECOND; }
+ | YEAR_MONTH_SYM { $$=INTERVAL_YEAR_MONTH; }
+ | YEAR_SYM { $$=INTERVAL_YEAR; }
+
+table_alias:
+ /* empty */
+ | AS
+ | EQ
+
+opt_table_alias:
+ /* empty */ { $$=0; }
+ | table_alias ident
+ { $$= (LEX_STRING*) sql_memdup(&$2,sizeof(LEX_STRING)); }
+
+
+where_clause:
+ /* empty */ { Lex->where= 0; }
+ | WHERE expr { Lex->where= $2; }
+
+having_clause:
+ /* empty */
+ | HAVING { Lex->create_refs=1; } expr
+ { Lex->having= $3; Lex->create_refs=0; }
+
+opt_escape:
+ ESCAPE_SYM TEXT_STRING { $$= $2.str; }
+ | /* empty */ { $$= (char*) "\\"; }
+
+
+/*
+** group by statement in select
+*/
+
+group_clause:
+ /* empty */
+ | GROUP BY group_list
+
+group_list:
+ group_list ',' group_ident
+ { if (add_group_to_list($3,(bool) 1)) YYABORT; }
+ | group_ident order_dir
+ { if (add_group_to_list($1,(bool) 1)) YYABORT; }
+
+/*
+** Order by statement in select
+*/
+
+order_clause:
+ /* empty */
+ | ORDER_SYM BY { Lex->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; }
+
+order_dir:
+ /* empty */ { $$ = 1; }
+ | ASC { $$ = Lex->sort_default=1; }
+ | DESC { $$ = Lex->sort_default=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; }
+
+delete_limit_clause:
+ /* empty */
+ {
+ Lex->select_limit= HA_POS_ERROR;
+ }
+ | LIMIT ULONGLONG_NUM
+ { Lex->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); }
+
+procedure_clause:
+ /* empty */
+ | PROCEDURE ident /* Procedure name */
+ {
+ LEX *lex=Lex;
+ 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)))
+ YYABORT;
+ }
+ '(' procedure_list ')'
+
+
+procedure_list:
+ /* empty */ {}
+ | procedure_list2 {}
+
+procedure_list2:
+ procedure_list2 ',' procedure_item
+ | procedure_item
+
+procedure_item:
+ remember_name expr
+ {
+ if (add_proc_to_list($2))
+ YYABORT;
+ if (!$2->name)
+ $2->set_name($1,(uint) ((char*) Lex->tok_end - $1));
+ }
+
+opt_into:
+ INTO OUTFILE TEXT_STRING
+ {
+ if (!(Lex->exchange= new sql_exchange($3.str,0)))
+ YYABORT;
+ }
+ opt_field_term opt_line_term
+ | INTO DUMPFILE TEXT_STRING
+ {
+ if (!(Lex->exchange= new sql_exchange($3.str,1)))
+ YYABORT;
+ }
+
+
+/*
+** Drop : delete tables or index
+*/
+
+drop:
+ DROP TABLE_SYM if_exists table_list
+ {
+ Lex->sql_command = SQLCOM_DROP_TABLE;
+ Lex->drop_if_exists = $3;
+ }
+ | 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,
+ $3.str));
+ if (!add_table_to_list($5,NULL))
+ YYABORT;
+ }
+ | DROP DATABASE if_exists ident
+ {
+ 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;
+ }
+
+
+table_list:
+ table
+ | table_list ',' table
+
+table:
+ table_ident
+ { if (!add_table_to_list($1,NULL)) YYABORT; }
+
+if_exists:
+ /* empty */ { $$=0; }
+ | IF EXISTS { $$= 1; }
+
+/*
+** Insert : add new data to table
+*/
+
+insert:
+ INSERT { Lex->sql_command = SQLCOM_INSERT; } insert_lock_option opt_ignore insert2 insert_field_spec
+
+replace:
+ REPLACE { Lex->sql_command = SQLCOM_REPLACE; } replace_lock_option insert2 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; }
+
+replace_lock_option:
+ opt_low_priority {}
+ | DELAYED_SYM { Lex->lock_option= TL_WRITE_DELAYED; }
+
+insert2:
+ INTO insert_table {}
+ | insert_table {}
+
+insert_table:
+ table
+ {
+ 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))
+ YYABORT;
+ }
+ 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_values:
+ VALUES values_list {}
+ | SELECT_SYM
+ {
+ LEX *lex=Lex;
+ lex->where=lex->having=0;
+ lex->select_limit=current_thd->default_select_limit;
+ lex->offset_limit=0L;
+ lex->options=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;
+ lex->sql_command = (lex->sql_command == SQLCOM_INSERT ?
+ SQLCOM_INSERT_SELECT : SQLCOM_REPLACE_SELECT);
+ }
+ select_options select_item_list select_from {}
+
+values_list:
+ values_list ',' no_braces
+ | no_braces
+
+ident_eq_list:
+ ident_eq_list ',' ident_eq_value
+ |
+ ident_eq_value
+
+ident_eq_value:
+ simple_ident equal expr
+ {
+ if (Lex->field_list.push_back($1) ||
+ Lex->insert_list->push_back($3))
+ YYABORT;
+ }
+
+equal: EQ {}
+ | SET_VAR {}
+
+no_braces:
+ '('
+ {
+ if (!(Lex->insert_list = new List_item))
+ YYABORT;
+ }
+ opt_values ')'
+ {
+ if (Lex->many_values.push_back(Lex->insert_list))
+ YYABORT;
+ }
+
+opt_values:
+ /* empty */ {}
+ | values
+
+values:
+ values ',' expr
+ {
+ if (Lex->insert_list->push_back($3))
+ YYABORT;
+ }
+ | expr
+ {
+ if (Lex->insert_list->push_back($1))
+ YYABORT;
+ }
+
+/* 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_list:
+ update_list ',' simple_ident equal expr
+ {
+ if (add_item_to_list($3) || add_value_to_list($5))
+ YYABORT;
+ }
+ | simple_ident equal expr
+ {
+ 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; }
+
+/* Delete rows from a table */
+
+delete:
+ DELETE_SYM opt_low_priority FROM table where_clause delete_limit_clause
+ { Lex->sql_command= SQLCOM_DELETE; }
+
+
+/* Show things */
+
+show: SHOW { Lex->wild=0;} show_param
+
+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;}
+ | TABLE_SYM STATUS_SYM opt_db wild
+ { Lex->sql_command= SQLCOM_SHOW_TABLES;
+ Lex->options|= SELECT_DESCRIBE;
+ Lex->db= $3;
+ }
+ | COLUMNS FROM table_ident opt_db wild
+ {
+ Lex->sql_command= SQLCOM_SHOW_FIELDS;
+ if ($4)
+ $3->change_db($4);
+ if (!add_table_to_list($3,NULL))
+ YYABORT;
+ }
+ | 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))
+ YYABORT;
+ }
+ | STATUS_SYM wild
+ { Lex->sql_command= SQLCOM_SHOW_STATUS; }
+ | PROCESSLIST_SYM
+ { Lex->sql_command= SQLCOM_SHOW_PROCESSLIST; Lex->verbose=0; }
+ | FULL PROCESSLIST_SYM
+ { Lex->sql_command= SQLCOM_SHOW_PROCESSLIST; Lex->verbose=1; }
+ | VARIABLES wild
+ { Lex->sql_command= SQLCOM_SHOW_VARIABLES; }
+ | GRANTS FOR_SYM user
+ { 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))
+ YYABORT;
+ }
+ | MASTER_SYM STATUS_SYM
+ {
+ Lex->sql_command = SQLCOM_SHOW_MASTER_STAT;
+ }
+ | SLAVE STATUS_SYM
+ {
+ Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT;
+ }
+
+opt_db:
+ /* empty */ { $$= 0; }
+ | FROM ident { $$= $2.str; }
+
+wild:
+ /* empty */
+ | LIKE text_string { Lex->wild= $2; }
+
+/* A Oracle compatible synonym for show */
+describe:
+ describe_command table_ident
+ {
+ Lex->wild=0;
+ Lex->sql_command=SQLCOM_SHOW_FIELDS;
+ if (!add_table_to_list($2, NULL))
+ YYABORT;
+ }
+ opt_describe_column
+ | describe_command select { Lex->options|= SELECT_DESCRIBE };
+
+
+describe_command:
+ DESC
+ | DESCRIBE
+
+opt_describe_column:
+ /* empty */ {}
+ | text_string { Lex->wild= $1; }
+ | 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_options:
+ flush_options ',' flush_option
+ | flush_option
+
+flush_option:
+ TABLES { Lex->type|= REFRESH_TABLES; }
+ | TABLES WITH READ_SYM LOCK_SYM { Lex->type|= REFRESH_TABLES | REFRESH_READ_LOCK; }
+ | 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; }
+
+/* kill threads */
+
+kill:
+ KILL_SYM NUM
+ {
+ Lex->sql_command=SQLCOM_KILL;
+ Lex->thread_id= (ulong) strtoul($2.str,NULL,10);
+ }
+
+/* change database */
+
+use: USE_SYM ident
+ { Lex->sql_command=SQLCOM_CHANGE_DB; Lex->db= $2.str; }
+
+/* import, export of files */
+
+load: LOAD DATA_SYM opt_low_priority opt_local INFILE TEXT_STRING
+ {
+ Lex->sql_command= SQLCOM_LOAD;
+ Lex->local_file= $4;
+ if (!(Lex->exchange= new sql_exchange($6.str,0)))
+ YYABORT;
+ 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))
+ YYABORT;
+ }
+ |
+ LOAD TABLE_SYM table_ident FROM MASTER_SYM
+ {
+ Lex->sql_command = SQLCOM_LOAD_MASTER_TABLE;
+ if (!add_table_to_list($3,NULL))
+ YYABORT;
+
+ }
+
+opt_local:
+ /* empty */ { $$=0;}
+ | LOCAL_SYM { $$=1;}
+
+opt_duplicate:
+ /* empty */ { Lex->duplicates=DUP_ERROR; }
+ | REPLACE { Lex->duplicates=DUP_REPLACE; }
+ | IGNORE_SYM { Lex->duplicates=DUP_IGNORE; }
+
+opt_field_term:
+ /* empty */
+ | COLUMNS field_term_list
+
+field_term_list:
+ field_term_list 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;}
+ | ENCLOSED BY text_string { Lex->exchange->enclosed= $3;}
+ | ESCAPED BY text_string { Lex->exchange->escaped= $3;}
+
+opt_line_term:
+ /* empty */
+ | LINES line_term_list
+
+line_term_list:
+ line_term_list line_term
+ | line_term
+
+line_term:
+ TERMINATED BY text_string { Lex->exchange->line_term= $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); }
+
+/* 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); }
+
+text_string:
+ TEXT_STRING { $$= new String($1.str,$1.length); }
+ | HEX_NUM
+ {
+ 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); }
+ | 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)};
+ | DATE_SYM text_literal { $$ = $2; }
+ | TIME_SYM text_literal { $$ = $2; }
+ | TIMESTAMP text_literal { $$ = $2; }
+
+/**********************************************************************
+** Createing different items.
+**********************************************************************/
+
+insert_ident:
+ simple_ident { $$=$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,"*"); }
+
+group_ident:
+ order_ident
+
+order_ident:
+ 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); }
+ | 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); }
+ | '.' 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); }
+ | 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); }
+
+
+field_ident:
+ ident { $$=$1;}
+ | ident '.' ident { $$=$3;} /* Skipp schema name in create*/
+ | '.' 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:
+ IDENT { $$=$1; }
+ | keyword
+ {
+ $$.str=sql_strmake($1.str,$1.length);
+ $$.length=$1.length;
+ if (Lex->next_state != STATE_END)
+ Lex->next_state=STATE_OPERATOR_OR_IDENT;
+ }
+
+ident_or_text:
+ ident { $$=$1;}
+ | TEXT_STRING { $$=$1;}
+ | LEX_HOSTNAME { $$=$1;}
+
+user:
+ ident_or_text
+ {
+ if (!($$=(LEX_USER*) sql_alloc(sizeof(st_lex_user))))
+ YYABORT;
+ $$->user = $1; $$->host.str=NullS;
+ }
+ | ident_or_text '@' ident_or_text
+ {
+ if (!($$=(LEX_USER*) sql_alloc(sizeof(st_lex_user))))
+ YYABORT;
+ $$->user = $1; $$->host=$3;
+ }
+
+/* Keyword that we allow for identifiers */
+
+keyword:
+ ACTION {}
+ | AFTER_SYM {}
+ | AGGREGATE_SYM {}
+ | AUTOCOMMIT {}
+ | AVG_ROW_LENGTH {}
+ | AVG_SYM {}
+ | BEGIN_SYM {}
+ | BIT_SYM {}
+ | BOOL_SYM {}
+ | CHECKSUM_SYM {}
+ | CHECK_SYM {}
+ | COMMENT_SYM {}
+ | COMMIT_SYM {}
+ | COMPRESSED_SYM {}
+ | DATA_SYM {}
+ | DATETIME {}
+ | DATE_SYM {}
+ | DAY_SYM {}
+ | DELAY_KEY_WRITE_SYM {}
+ | DUMPFILE {}
+ | DYNAMIC_SYM {}
+ | END {}
+ | ENUM {}
+ | ESCAPE_SYM {}
+ | EXTENDED_SYM {}
+ | FILE_SYM {}
+ | FIRST_SYM {}
+ | FIXED_SYM {}
+ | FLUSH_SYM {}
+ | GRANTS {}
+ | HEAP_SYM {}
+ | HOSTS_SYM {}
+ | HOUR_SYM {}
+ | IDENTIFIED_SYM {}
+ | ISAM_SYM {}
+ | LOCAL_SYM {}
+ | LOGS_SYM {}
+ | MAX_ROWS {}
+ | MAX_SYM {}
+ | MASTER_SYM {}
+ | MASTER_HOST_SYM {}
+ | MASTER_PORT_SYM {}
+ | MASTER_LOG_FILE_SYM {}
+ | MASTER_LOG_POS_SYM {}
+ | MASTER_USER_SYM {}
+ | MASTER_PASSWORD_SYM {}
+ | MASTER_CONNECT_RETRY_SYM {}
+ | MERGE_SYM {}
+ | MINUTE_SYM {}
+ | MIN_ROWS {}
+ | MODIFY_SYM {}
+ | MONTH_SYM {}
+ | MYISAM_SYM {}
+ | NATIONAL_SYM {}
+ | NCHAR_SYM {}
+ | NO_SYM {}
+ | PACK_KEYS_SYM {}
+ | PASSWORD {}
+ | PROCESS {}
+ | PROCESSLIST_SYM {}
+ | RAID_0_SYM {}
+ | RAID_CHUNKS {}
+ | RAID_CHUNKSIZE {}
+ | RAID_STRIPED_SYM {}
+ | RAID_TYPE {}
+ | RELOAD {}
+ | REPAIR {}
+ | ROLLBACK_SYM {}
+ | ROWS_SYM {}
+ | ROW_FORMAT_SYM {}
+ | ROW_SYM {}
+ | SECOND_SYM {}
+ | SHUTDOWN {}
+ | START_SYM {}
+ | STATUS_SYM {}
+ | STOP_SYM {}
+ | STRING_SYM {}
+ | TEMPORARY {}
+ | TEXT_SYM {}
+ | TIMESTAMP {}
+ | TIME_SYM {}
+ | TYPE_SYM {}
+ | UDF_SYM {}
+ | VARIABLES {}
+ | WORK_SYM {}
+ | YEAR_SYM {}
+ | SLAVE {}
+ | COLLECTION {}
+
+/* Option functions */
+
+set:
+ SET opt_option
+ {
+ Lex->sql_command= SQLCOM_SET_OPTION;
+ Lex->options=current_thd->options;
+ Lex->select_limit=current_thd->default_select_limit;
+ }
+ option_value_list
+
+opt_option:
+ /* empty */ {}
+ | OPTION {}
+
+option_value_list:
+ option_value
+ | option_value_list ',' option_value
+
+option_value:
+ set_option equal NUM
+ {
+ if (atoi($3.str) == 0)
+ Lex->options&= ~$1;
+ else
+ Lex->options|= $1;
+ }
+ | 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
+ {
+ current_thd->next_insert_id=$3;
+ }
+ | CHAR_SYM SET IDENT
+ {
+ CONVERT *tmp;
+ if (!(tmp=get_convert_set($3.str)))
+ {
+ net_printf(&current_thd->net,ER_UNKNOWN_CHARACTER_SET,$3);
+ YYABORT;
+ }
+ current_thd->convert_set=tmp;
+ }
+ | CHAR_SYM SET DEFAULT
+ {
+ current_thd->convert_set=0;
+ }
+ | 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())
+ YYABORT;
+ }
+
+text_or_password:
+ TEXT_STRING { $$=$1.str;}
+ | PASSWORD '(' TEXT_STRING ')'
+ {
+ if (!$3.length)
+ $$=$3.str;
+ else
+ {
+ char *buff=(char*) sql_alloc(HASH_PASSWORD_LENGTH+1);
+ make_scrambled_password(buff,$3.str);
+ $$=buff;
+ }
+ }
+
+set_option:
+ SQL_BIG_TABLES { $$= OPTION_BIG_TABLES; }
+ | AUTOCOMMIT { $$= OPTION_AUTO_COMMIT; }
+ | 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; }
+
+/* Lock function */
+
+lock:
+ LOCK_SYM table_or_tables
+ {
+ Lex->sql_command=SQLCOM_LOCK_TABLES;
+ }
+ table_lock_list
+
+table_or_tables:
+ TABLE_SYM
+ | TABLES
+
+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,(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; }
+
+unlock:
+ UNLOCK_SYM table_or_tables { Lex->sql_command=SQLCOM_UNLOCK_TABLES; }
+
+
+/* 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;
+ }
+ grant_privileges ON opt_table FROM user_list
+
+grant:
+ GRANT
+ {
+ Lex->sql_command = SQLCOM_GRANT;
+ Lex->users_list.empty();
+ Lex->columns.empty();
+ Lex->grant= Lex->grant_tot_col=0;
+ Lex->db=0;
+ }
+ grant_privileges ON opt_table TO_SYM user_list
+ grant_option
+
+grant_privileges:
+ grant_privilege_list {}
+ | ALL PRIVILEGES { Lex->grant = UINT_MAX;}
+ | ALL { Lex->grant = UINT_MAX;}
+
+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 {}
+ | INDEX { Lex->grant |= INDEX_ACL;}
+ | ALTER { Lex->grant |= ALTER_ACL;}
+ | CREATE { Lex->grant |= CREATE_ACL;}
+ | DROP { Lex->grant |= DROP_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;}
+
+opt_table:
+ '*'
+ {
+ Lex->db=current_thd->db;
+ if (Lex->grant == UINT_MAX)
+ Lex->grant = DB_ACLS & ~GRANT_ACL;
+ else if (Lex->columns.elements)
+ {
+ net_printf(&current_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)
+ {
+ net_printf(&current_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)
+ {
+ net_printf(&current_thd->net,ER_ILLEGAL_GRANT_FOR_TABLE);
+ YYABORT;
+ }
+ }
+ | table_ident
+ {
+ if (!add_table_to_list($1,NULL))
+ YYABORT;
+ if (Lex->grant == UINT_MAX)
+ 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:
+ user IDENTIFIED_SYM BY TEXT_STRING
+ {
+ $$=$1; $1->password=$4;
+ if ($4.length)
+ {
+ char *buff=(char*) sql_alloc(HASH_PASSWORD_LENGTH+1);
+ if (buff)
+ {
+ make_scrambled_password(buff,$4.str);
+ $1->password.str=buff;
+ $1->password.length=HASH_PASSWORD_LENGTH;
+ }
+ }
+ }
+ | user IDENTIFIED_SYM BY PASSWORD TEXT_STRING
+ { $$=$1; $1->password=$5 ; }
+ | user
+ { $$=$1; $1->password.str=NullS; }
+
+
+opt_column_list:
+ /* empty */ { Lex->grant |= Lex->which_columns; }
+ | '(' column_list ')'
+
+column_list:
+ column_list ',' column_list_id
+ | column_list_id
+
+column_list_id:
+ ident
+ {
+ String *new_str = new String((const char*) $1.str,$1.length);
+ List_iterator <LEX_COLUMN> iter(Lex->columns);
+ class LEX_COLUMN *point;
+ while ((point=iter++))
+ {
+ if (!my_strcasecmp(point->column.ptr(),new_str->ptr()))
+ break;
+ }
+ Lex->grant_tot_col|= Lex->which_columns;
+ if (point)
+ point->rights |= Lex->which_columns;
+ else
+ Lex->columns.push_back(new LEX_COLUMN (*new_str,Lex->which_columns));
+ }
+
+grant_option:
+ /* empty */ {}
+ | WITH GRANT OPTION { Lex->grant |= GRANT_ACL;}
+
+begin:
+ BEGIN_SYM { Lex->sql_command = SQLCOM_COMMIT;} opt_work
+
+opt_work:
+ /* empty */ {}
+ | WORK_SYM {}
+
+commit:
+ COMMIT_SYM { Lex->sql_command = SQLCOM_COMMIT;}
+
+rollback:
+ ROLLBACK_SYM { Lex->sql_command = SQLCOM_ROLLBACK;}
diff --git a/sql/structs.h b/sql/structs.h
new file mode 100644
index 00000000000..b32f957da25
--- /dev/null
+++ b/sql/structs.h
@@ -0,0 +1,160 @@
+/* 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 */
+
+
+/* The old structures from unireg */
+
+struct st_table;
+class Field;
+
+typedef struct st_date_format { /* How to print date */
+ uint pos[6]; /* Positions to YY.MM.DD HH:MM:SS */
+} DATE_FORMAT;
+
+
+typedef struct st_keyfile_info { /* used with ha_info() */
+ byte ref[MAX_REFLENGTH]; /* Pointer to current row */
+ byte dupp_ref[MAX_REFLENGTH]; /* Pointer to dupp row */
+ uint ref_length; /* Length of ref (1-8) */
+ uint block_size; /* index block size */
+ File filenr; /* (uniq) filenr for table */
+ ha_rows records; /* Records i datafilen */
+ ha_rows deleted; /* Deleted records */
+ ulonglong data_file_length; /* Length off data file */
+ ulonglong max_data_file_length; /* Length off data file */
+ ulonglong index_file_length;
+ ulonglong max_index_file_length;
+ ulonglong delete_length; /* Free bytes */
+ ulonglong auto_increment_value;
+ int errkey,sortkey; /* Last errorkey and sorted by */
+ time_t create_time; /* When table was created */
+ time_t check_time;
+ time_t update_time;
+ ulong mean_rec_length; /* physical reclength */
+} KEYFILE_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
+ 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
+} KEY_PART_INFO ;
+
+
+typedef struct st_key {
+ uint key_length; /* Tot length of key */
+ uint flags; /* dupp key and pack flags */
+ uint key_parts; /* How many key_parts */
+ uint extra_length;
+ uint usable_key_parts; /* Should normally be = key_parts */
+ KEY_PART_INFO *key_part;
+ char *name; /* Name of key */
+ ulong *rec_per_key; /* Key part distribution */
+} KEY;
+
+
+struct st_join_table;
+
+typedef struct st_reginfo { /* Extra info about reg */
+ struct st_join_table *join_tab; /* Used by SELECT() */
+ enum thr_lock_type lock_type; /* How database is used */
+ bool not_exists_optimize;
+ bool impossible_range;
+} REGINFO;
+
+
+struct st_read_record; /* For referense later */
+class SQL_SELECT;
+class THD;
+class handler;
+
+typedef struct st_read_record { /* Parameter to read_record */
+ struct st_table *table; /* Head-form */
+ handler *file;
+ struct st_table **forms; /* head and ref forms */
+ int (*read_record)(struct st_read_record *);
+ THD *thd;
+ SQL_SELECT *select;
+ uint cache_records;
+ uint ref_length,struct_length,reclength,rec_cache_size,error_offset;
+ uint index;
+ byte *ref_pos; /* pointer to form->refpos */
+ byte *record;
+ byte *cache,*cache_pos,*cache_end,*read_positions;
+ IO_CACHE *io_cache;
+ bool print_error;
+} READ_RECORD;
+
+enum timestamp_type { TIMESTAMP_NONE, TIMESTAMP_DATE, TIMESTAMP_FULL,
+ TIMESTAMP_TIME };
+
+typedef struct st_time {
+ uint year,month,day,hour,minute,second,second_part;
+ bool neg;
+ timestamp_type time_type;
+} TIME;
+
+typedef struct {
+ long year,month,day,hour,minute,second,second_part;
+ bool neg;
+} 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};
+
+struct show_var_st {
+ const char *name;
+ char *value;
+ SHOW_TYPE type;
+};
+
+typedef struct lex_string {
+ char *str;
+ uint length;
+} LEX_STRING;
+
+typedef struct st_lex_user {
+ LEX_STRING user, host, password;
+} LEX_USER;
+
+ /* 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 */
+#define REG_UPDATE 4 /* Uppdate record */
+#define REG_DELETE 8 /* Delete found record */
+#define REG_PROG 16 /* User is updateing database */
+#define REG_CLEAR_AFTER_WRITE 32
+#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
+
+ /* 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_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 */
diff --git a/sql/table.cc b/sql/table.cc
new file mode 100644
index 00000000000..98f7b089b19
--- /dev/null
+++ b/sql/table.cc
@@ -0,0 +1,1121 @@
+/* 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 */
+
+
+/* Some general useful functions */
+
+#include "mysql_priv.h"
+#include <errno.h>
+#include <m_ctype.h>
+
+
+ /* Functions defined in this file */
+
+static void frm_error(int error,TABLE *form,const char *name,int errortype);
+static void fix_type_pointers(const char ***array, TYPELIB *point_to_type,
+ uint types, char **names);
+static uint find_field(TABLE *form,uint start,uint length);
+
+
+static byte* get_field_name(Field *buff,uint *length,
+ my_bool not_used __attribute__((unused)))
+{
+ *length= strlen(buff->field_name);
+ return (byte*) buff->field_name;
+}
+
+ /* Open a .frm file */
+
+int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag,
+ TABLE *outparam)
+{
+ reg1 uint i;
+ reg2 uchar *strpos;
+ int j,flag,error;
+ uint rec_buff_length,n_length,int_length,records,key_parts,keys,
+ interval_count,interval_parts,read_length,db_create_options;
+ ulong pos;
+ 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;
+ File file;
+ Field **field_ptr,*reg_field;
+ KEY *keyinfo;
+ KEY_PART_INFO *key_part;
+ uchar *null_pos;
+ uint null_bit;
+ SQL_CRYPT *crypted=0;
+ DBUG_ENTER("openfrm");
+ DBUG_PRINT("enter",("name: '%s' form: %lx",name,outparam));
+
+ bzero((char*) outparam,sizeof(*outparam));
+ outparam->blob_ptr_size=sizeof(char*);
+ disk_buff=NULL; record= NULL; keynames=NullS;
+ outparam->db_stat = db_stat;
+ error=1;
+
+ init_sql_alloc(&outparam->mem_root,1024);
+ MEM_ROOT *old_root=my_pthread_getspecific_ptr(MEM_ROOT*,THR_MALLOC);
+ my_pthread_setspecific_ptr(THR_MALLOC,&outparam->mem_root);
+
+ outparam->real_name=strdup_root(&outparam->mem_root,
+ name+dirname_length(name));
+ *fn_ext(outparam->real_name)='\0'; // Remove extension
+ outparam->table_name=my_strdup(alias,MYF(MY_WME));
+ if (!outparam->real_name || !outparam->table_name)
+ goto err_end;
+
+ flag= (prgflag & CHANGE_FRM) ? O_RDWR : O_RDONLY | O_SHARE;
+ if ((file=my_open(fn_format(index_file,name,"",reg_ext,4),flag,
+ MYF(0)))
+ < 0)
+ {
+ goto err_end; /* purecov: inspected */
+ }
+ error=4;
+ if (!(outparam->path= strdup_root(&outparam->mem_root,name)))
+ goto err_not_open;
+ *fn_ext(outparam->path)='\0'; // Remove extension
+
+ 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 */
+ new_field_pack_flag=head[27];
+ new_frm_ver= (head[2] == FRM_VER+1);
+
+ error=3;
+ if (!(pos=get_form_pos(file,head,(TYPELIB*) 0)))
+ 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));
+ outparam->db_create_options=db_create_options=uint2korr(head+30);
+ outparam->db_options_in_use=outparam->db_create_options;
+ null_field_first=0;
+ if (!head[32]) // New frm file in 3.23
+ {
+ outparam->avg_row_length=uint4korr(head+34);
+ outparam->row_type=(row_type) head[40];
+ outparam->raid_type= head[41];
+ outparam->raid_chunks= head[42];
+ outparam->raid_chunksize= uint4korr(head+43);
+ null_field_first=1;
+ }
+ outparam->db_record_offset=1;
+ if (db_create_options & HA_OPTION_LONG_BLOB_PTR)
+ outparam->blob_ptr_size=portable_sizeof_char_ptr;
+ outparam->db_low_byte_first=test(outparam->db_type == DB_TYPE_MYISAM ||
+ outparam->db_type == DB_TYPE_BERKELEY_DB ||
+ outparam->db_type == DB_TYPE_HEAP);
+
+ error=4;
+ outparam->max_rows=uint4korr(head+18);
+ outparam->min_rows=uint4korr(head+22);
+
+ /* Read keyinformation */
+ VOID(my_seek(file,(ulong) uint2korr(head+6),MY_SEEK_SET,MYF(0)));
+ if (read_string(file,(gptr*) &disk_buff,(uint) uint2korr(head+28)))
+ goto err_not_open; /* purecov: inspected */
+ outparam->keys=keys=disk_buff[0];
+ outparam->keys_in_use= (((key_map) 1) << keys)- (key_map) 1;
+
+ outparam->key_parts=key_parts=disk_buff[1];
+ n_length=keys*sizeof(KEY)+key_parts*sizeof(KEY_PART_INFO);
+ if (!(keyinfo = (KEY*) alloc_root(&outparam->mem_root,
+ n_length+uint2korr(disk_buff+4))))
+ 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);
+ strpos=disk_buff+6;
+
+ ulong *rec_per_key;
+ if (!(rec_per_key= (ulong*) alloc_root(&outparam->mem_root,
+ sizeof(ulong*)*key_parts)))
+ goto err_not_open;
+
+ for (i=0 ; i < keys ; i++, keyinfo++)
+ {
+ uint null_parts=0;
+ keyinfo->flags= ((uint) strpos[0]) ^ HA_NOSAME;
+ keyinfo->key_length= (uint) uint2korr(strpos+1);
+ keyinfo->key_parts= (uint) strpos[3]; strpos+=4;
+ keyinfo->key_part= key_part;
+ keyinfo->rec_per_key= rec_per_key;
+ for (j=keyinfo->key_parts ; j-- ; key_part++)
+ {
+ *rec_per_key++=0;
+ key_part->fieldnr= (uint16) (uint2korr(strpos) & FIELD_NR_MASK);
+ 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)
+ {
+ key_part->key_part_flag= *(strpos+4);
+ key_part->length= (uint) uint2korr(strpos+7);
+ strpos+=9;
+ }
+ else
+ {
+ key_part->length= *(strpos+4);
+ key_part->key_part_flag=0;
+ if (key_part->length > 128)
+ {
+ key_part->length&=127; /* purecov: inspected */
+ key_part->key_part_flag=HA_REVERSE_SORT; /* purecov: inspected */
+ }
+ strpos+=7;
+ }
+ key_part->store_length=key_part->length;
+ }
+ keyinfo->key_length+=null_parts;
+ set_if_bigger(outparam->max_key_length,keyinfo->key_length+
+ keyinfo->key_parts);
+ if (keyinfo->flags & HA_NOSAME)
+ set_if_bigger(outparam->max_unique_length,keyinfo->key_length);
+ }
+
+ (void) strmov(keynames= (char *) key_part,(char *) strpos);
+ outparam->reclength = uint2korr((head+16));
+ if (*(head+26) == 1)
+ outparam->system=1; /* one-record-database */
+#ifdef HAVE_CRYPTED_FRM
+ else if (*(head+26) == 2)
+ {
+ extern SQL_CRYPT *get_crypt_for_frm(void);
+ my_pthread_setspecific_ptr(THR_MALLOC,old_root);
+ crypted=get_crypt_for_frm();
+ my_pthread_setspecific_ptr(THR_MALLOC,&outparam->mem_root);
+ outparam->crypted=1;
+ }
+#endif
+
+ if (!(outparam->file= get_new_handler(outparam,outparam->db_type)))
+ goto err_not_open;
+ error=2;
+ if (db_stat)
+ {
+ if ((outparam->file->
+ ha_open(index_file,
+ (db_stat & HA_READ_ONLY ? O_RDONLY : O_RDWR),
+ (db_stat & HA_OPEN_TEMPORARY ? HA_OPEN_TMP_TABLE :
+ db_stat & HA_WAIT_IF_LOCKED ||
+ specialflag & SPECIAL_WAIT_IF_LOCKED ?
+ HA_OPEN_WAIT_IF_LOCKED :
+ (db_stat & (HA_ABORT_IF_LOCKED | HA_GET_INFO)) ?
+ HA_OPEN_ABORT_IF_LOCKED :
+ HA_OPEN_IGNORE_IF_LOCKED) | ha_open_options)))
+ goto err_not_open; /* purecov: inspected */
+ }
+ outparam->db_low_byte_first=outparam->file->low_byte_first();
+
+ error=4;
+ outparam->reginfo.lock_type= TL_UNLOCK;
+ outparam->current_lock=F_UNLCK;
+ if (db_stat & HA_OPEN_KEYFILE || (prgflag & DELAYED_OPEN)) records=2;
+ else records=1;
+ if (prgflag & (READ_ALL+EXTRA_RECORD)) records++;
+ rec_buff_length=ALIGN_SIZE(outparam->reclength+1);
+ if (!(outparam->record[0]= (byte*)
+ (record = (char *) alloc_root(&outparam->mem_root,
+ rec_buff_length * records))))
+ goto err; /* purecov: inspected */
+ record[outparam->reclength]=0; // For purify and ->c_ptr()
+ outparam->rec_buff_length=rec_buff_length;
+ if (my_pread(file,(byte*) record,(uint) outparam->reclength,
+ (ulong) (uint2korr(head+6)+uint2korr(head+14)),
+ MYF(MY_NABP)))
+ goto err; /* purecov: inspected */
+ for (i=0 ; i < records ; i++, record+=rec_buff_length)
+ {
+ outparam->record[i]=(byte*) record;
+ if (i)
+ memcpy(record,record-rec_buff_length,(uint) outparam->reclength);
+ }
+
+ if (records == 2)
+ { /* fix for select */
+ outparam->record[2]=outparam->record[1];
+ if (db_stat & HA_READ_ONLY)
+ outparam->record[1]=outparam->record[0]; /* purecov: inspected */
+ }
+
+ VOID(my_seek(file,pos,MY_SEEK_SET,MYF(0)));
+ if (my_read(file,(byte*) head,288,MYF(MY_NABP))) goto err;
+ if (crypted)
+ {
+ crypted->decode((char*) head+256,288-256);
+ if (sint2korr(head+284) != 0) // Should be 0
+ goto err; // Wrong password
+ }
+
+ outparam->fields= uint2korr(head+258);
+ pos=uint2korr(head+260); /* Length of all screens */
+ n_length=uint2korr(head+268);
+ interval_count=uint2korr(head+270);
+ interval_parts=uint2korr(head+272);
+ int_length=uint2korr(head+274);
+ outparam->null_fields=uint2korr(head+282);
+ 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));
+
+ if (!(field_ptr = (Field **)
+ alloc_root(&outparam->mem_root,
+ (uint) ((outparam->fields+1)*sizeof(Field*)+
+ interval_count*sizeof(TYPELIB)+
+ (outparam->fields+interval_parts+
+ keys+3)*sizeof(my_string)+
+ (n_length+int_length)))))
+ goto err; /* purecov: inspected */
+
+ outparam->field=field_ptr;
+ read_length=((uint) (outparam->fields*11)+pos+
+ (uint) (n_length+int_length));
+ if (read_string(file,(gptr*) &disk_buff,read_length))
+ goto err; /* purecov: inspected */
+ if (crypted)
+ {
+ crypted->decode((char*) disk_buff,read_length);
+ delete crypted;
+ crypted=0;
+ }
+ strpos= disk_buff+pos;
+
+ outparam->intervals= (TYPELIB*) (field_ptr+outparam->fields+1);
+ int_array= (const char **) (outparam->intervals+interval_count);
+ 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),
+ (uint) (n_length+int_length));
+
+ fix_type_pointers(&int_array,&outparam->fieldnames,1,&names);
+ fix_type_pointers(&int_array,outparam->intervals,interval_count,
+ &names);
+ if (keynames)
+ fix_type_pointers(&int_array,&outparam->keynames,1,&keynames);
+ VOID(my_close(file,MYF(MY_WME)));
+
+ record=(char*) outparam->record[0]-1; /* Fieldstart = 1 */
+ if (null_field_first)
+ {
+ outparam->null_flags=null_pos=(uchar*) record+1;
+ null_bit= (db_create_options & HA_OPTION_PACK_RECORD) ? 1 : 2;
+ outparam->null_bytes=(outparam->null_fields+null_bit+6)/8;
+ }
+ else
+ {
+ outparam->null_bytes=(outparam->null_fields+7)/8;
+ outparam->null_flags=null_pos=
+ (uchar*) (record+1+outparam->reclength-outparam->null_bytes);
+ null_bit=1;
+ }
+
+ use_hash= outparam->fields >= MAX_FIELDS_BEFORE_HASH;
+ if (use_hash)
+ use_hash= !hash_init(&outparam->name_hash,
+ outparam->fields,0,0,
+ (hash_get_key) get_field_name,0,
+ HASH_CASE_INSENSITIVE);
+
+ for (i=0 ; i < outparam->fields; i++, strpos+= 11, field_ptr++)
+ {
+ uint pack_flag= uint2korr(strpos+6);
+ uint interval_nr= (uint) strpos[10];
+
+ *field_ptr=reg_field=
+ make_field(record+uint2korr(strpos+4),
+ (uint32) strpos[3], // field_length
+ null_pos,null_bit,
+ pack_flag,
+ (Field::utype) MTYP_TYPENR((uint) strpos[8]),
+ (interval_nr ?
+ outparam->intervals+interval_nr-1 :
+ (TYPELIB*) 0),
+ outparam->fieldnames.type_names[i],
+ outparam);
+ if (!(reg_field->flags & NOT_NULL_FLAG))
+ {
+ if ((null_bit<<=1) == 256)
+ {
+ null_pos++;
+ null_bit=1;
+ }
+ }
+ if (reg_field->unireg_check == Field::NEXT_NUMBER)
+ {
+ if ((int) (outparam->next_number_index= (uint)
+ find_ref_key(outparam,reg_field,
+ &outparam->next_number_key_offset)) < 0)
+ reg_field->unireg_check=Field::NONE; /* purecov: inspected */
+ else
+ {
+ outparam->found_next_number_field=reg_field;
+ reg_field->flags|=AUTO_INCREMENT_FLAG;
+ }
+ }
+ if (outparam->timestamp_field == reg_field)
+ outparam->timestamp_field_offset=i;
+ if (use_hash)
+ (void) hash_insert(&outparam->name_hash,(byte*) *field_ptr); // Will never fail
+ }
+ *field_ptr=0; // End marker
+
+ /* Fix key->name and key_part->field */
+ if (key_parts)
+ {
+ uint primary_key=(uint) (find_type((char*) "PRIMARY",&outparam->keynames,
+ 3)-1);
+ uint ha_option=outparam->file->option_flag();
+ keyinfo=outparam->key_info;
+ key_part=keyinfo->key_part;
+
+ for (uint key=0 ; key < outparam->keys ; key++,keyinfo++)
+ {
+ uint usable_parts=0;
+ keyinfo->name=(char*) outparam->keynames.type_names[key];
+ if (primary_key >= MAX_KEY && (keyinfo->flags & HA_NOSAME))
+ {
+ /*
+ If the UNIQUE key don't have NULL columns, declare this as
+ a primary key.
+ */
+ primary_key=key;
+ for (i=0 ; i < keyinfo->key_parts ;i++)
+ {
+ if (!key_part[i].fieldnr ||
+ outparam->field[key_part[i].fieldnr-1]->null_ptr)
+ {
+ primary_key=MAX_KEY; // Can't be used
+ break;
+ }
+ }
+ }
+
+ for (i=0 ; i < keyinfo->key_parts ; key_part++,i++)
+ {
+ if (new_field_pack_flag <= 1)
+ key_part->fieldnr=(uint16) find_field(outparam,
+ (uint) key_part->offset,
+ (uint) key_part->length);
+#ifdef EXTRA_DEBUG
+ if (key_part->fieldnr > outparam->fields)
+ goto err; // sanity check
+#endif
+ if (key_part->fieldnr)
+ { // Should always be true !
+ Field *field=key_part->field=outparam->field[key_part->fieldnr-1];
+ if (field->null_ptr)
+ {
+ key_part->null_offset=(uint) ((byte*) field->null_ptr -
+ outparam->record[0]);
+ key_part->null_bit= field->null_bit;
+ key_part->store_length+=HA_KEY_NULL_LENGTH;
+ keyinfo->flags|=HA_NULL_PART_KEY;
+ keyinfo->extra_length+= HA_KEY_NULL_LENGTH;
+ }
+ if (field->type() == FIELD_TYPE_BLOB ||
+ field->real_type() == FIELD_TYPE_VAR_STRING)
+ {
+ if (field->type() == FIELD_TYPE_BLOB)
+ key_part->key_part_flag|= HA_BLOB_PART;
+ keyinfo->extra_length+=HA_KEY_BLOB_LENGTH;
+ key_part->store_length+=HA_KEY_BLOB_LENGTH;
+ }
+ if (i == 0 && key != primary_key)
+ field->flags |=
+ ((keyinfo->flags & HA_NOSAME) &&
+ field->key_length() ==
+ 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 &&
+ field->key_length() == key_part->length)
+ {
+ if (field->key_type() != HA_KEYTYPE_TEXT ||
+ !(ha_option & HA_KEY_READ_WRONG_STR))
+ field->part_of_key|= ((key_map) 1 << key);
+ }
+ if (!(key_part->key_part_flag & HA_REVERSE_SORT) &&
+ usable_parts == i)
+ usable_parts++; // For FILESORT
+ field->flags|= PART_KEY_FLAG;
+ if (key == primary_key)
+ {
+ field->flags|= PRI_KEY_FLAG;
+ if (ha_option & HA_PRIMARY_KEY_IN_READ_INDEX)
+ field->part_of_key|= ((key_map) 1 << primary_key);
+ }
+ 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->field_length=key_part->length;
+ }
+ }
+ }
+ else
+ { // Error: shorten key
+ keyinfo->key_parts=usable_parts;
+ keyinfo->flags=0;
+ }
+ }
+ keyinfo->usable_key_parts=usable_parts; // Filesort
+ }
+ if (primary_key < MAX_KEY &&
+ (outparam->keys_in_use & ((key_map) 1 << primary_key)))
+ {
+ outparam->primary_key=primary_key;
+ if (outparam->key_info[primary_key].key_parts == 1)
+ {
+ Field *field= outparam->key_info[primary_key].key_part[0].field;
+ if (field && field->result_type() == INT_RESULT)
+ outparam->rowid_field=field;
+ }
+ }
+ else
+ outparam->primary_key = MAX_KEY; // we do not have a primary key
+ }
+ x_free((gptr) disk_buff);
+ if (new_field_pack_flag <= 1)
+ { /* Old file format with default null */
+ uint null_length=(outparam->null_fields+7)/8;
+ bfill(outparam->null_flags,null_length,255);
+ bfill(outparam->null_flags+outparam->rec_buff_length,null_length,255);
+ if (records > 2)
+ bfill(outparam->null_flags+outparam->rec_buff_length*2,null_length,255);
+ }
+
+ if (outparam->blob_fields)
+ {
+ Field **ptr;
+ Field_blob **save;
+
+ if (!(outparam->blob_field=save=
+ (Field_blob**) alloc_root(&outparam->mem_root,
+ (uint) (outparam->blob_fields+1)*
+ sizeof(Field_blob*))))
+ goto err;
+ for (ptr=outparam->field ; *ptr ; ptr++)
+ {
+ if ((*ptr)->flags & BLOB_FLAG)
+ (*save++)= (Field_blob*) *ptr;
+ }
+ *save=0; // End marker
+ }
+ else
+ outparam->blob_field=
+ (Field_blob**) (outparam->field+outparam->fields); // Point at null ptr
+
+ my_pthread_setspecific_ptr(THR_MALLOC,old_root);
+ opened_tables++;
+#ifndef DBUG_OFF
+ if (use_hash)
+ (void) hash_check(&outparam->name_hash);
+#endif
+ if (db_stat)
+ outparam->file->initialize();
+ DBUG_RETURN (0);
+
+ err:
+ if (outparam->file && db_stat)
+ (void) outparam->file->close();
+ err_not_open:
+ x_free((gptr) disk_buff);
+ delete crypted;
+ VOID(my_close(file,MYF(MY_WME)));
+
+ err_end: /* Here when no file */
+ delete crypted;
+ my_pthread_setspecific_ptr(THR_MALLOC,old_root);
+ frm_error(error,outparam,name,ME_ERROR+ME_WAITTANG);
+ outparam->file=0; // For easyer errorchecking
+ free_root(&outparam->mem_root);
+ my_free(outparam->table_name,MYF(MY_ALLOW_ZERO_PTR));
+ DBUG_RETURN (error);
+} /* openfrm */
+
+
+ /* close a .frm file and it's tables */
+
+int closefrm(register TABLE *table)
+{
+ int error=0;
+ DBUG_ENTER("closefrm");
+ if (table->db_stat)
+ error=table->file->close();
+ if (table->table_name)
+ {
+ my_free(table->table_name,MYF(0));
+ table->table_name=0;
+ }
+ if (table->fields)
+ {
+ for (Field **ptr=table->field ; *ptr ; ptr++)
+ delete *ptr;
+ table->fields=0;
+ }
+ delete table->file;
+ table->file=0; /* For easyer errorchecking */
+ hash_free(&table->name_hash);
+ free_root(&table->mem_root);
+ DBUG_RETURN(error);
+}
+
+
+/* Deallocate temporary blob storage */
+
+void free_blobs(register TABLE *table)
+{
+ for (Field_blob **ptr=table->blob_field ; *ptr ; ptr++)
+ (*ptr)->free();
+}
+
+
+ /* Find where a form starts */
+ /* if formname is NullS then only formnames is read */
+
+ulong get_form_pos(File file, uchar *head, TYPELIB *save_names)
+{
+ uint a_length,names,length;
+ uchar *pos,*buf;
+ ulong ret_value=0;
+ DBUG_ENTER("get_form_pos");
+
+ names=uint2korr(head+8);
+ a_length=(names+2)*sizeof(my_string); /* Room for two extra */
+
+ if (!save_names)
+ a_length=0;
+ else
+ save_names->type_names=0; /* Clear if error */
+
+ if (names)
+ {
+ length=uint2korr(head+4);
+ VOID(my_seek(file,64L,MY_SEEK_SET,MYF(0)));
+ if (!(buf= (uchar*) my_malloc((uint) length+a_length+names*4,
+ MYF(MY_WME))) ||
+ my_read(file,(byte*) buf+a_length,(uint) (length+names*4),
+ MYF(MY_NABP)))
+ { /* purecov: inspected */
+ x_free((gptr) buf); /* purecov: inspected */
+ DBUG_RETURN(0L); /* purecov: inspected */
+ }
+ pos= buf+a_length+length;
+ ret_value=uint4korr(pos);
+ }
+ if (! save_names)
+ my_free((gptr) buf,MYF(0));
+ else if (!names)
+ bzero((char*) save_names,sizeof(save_names));
+ else
+ {
+ char *str;
+ str=(char *) (buf+a_length);
+ fix_type_pointers((const char ***) &buf,save_names,1,&str);
+ }
+ DBUG_RETURN(ret_value);
+}
+
+
+ /* Read string from a file with malloc */
+
+int read_string(File file, gptr *to, uint length)
+{
+ DBUG_ENTER("read_string");
+
+ x_free((gptr) *to);
+ if (!(*to= (gptr) my_malloc(length+1,MYF(MY_WME))) ||
+ my_read(file,(byte*) *to,length,MYF(MY_NABP)))
+ {
+ x_free((gptr) *to); /* purecov: inspected */
+ *to= 0; /* purecov: inspected */
+ DBUG_RETURN(1); /* purecov: inspected */
+ }
+ *((char*) *to+length)= '\0';
+ DBUG_RETURN (0);
+} /* read_string */
+
+
+ /* Add a new form to a form file */
+
+ulong make_new_entry(File file, uchar *fileinfo, TYPELIB *formnames,
+ const char *newname)
+{
+ uint i,bufflength,maxlength,n_length,length,names;
+ ulong endpos,newpos;
+ char buff[IO_SIZE];
+ uchar *pos;
+ DBUG_ENTER("make_new_entry");
+
+ length=strlen(newname)+1;
+ n_length=uint2korr(fileinfo+4);
+ maxlength=uint2korr(fileinfo+6);
+ names=uint2korr(fileinfo+8);
+ newpos=uint4korr(fileinfo+10);
+
+ if (64+length+n_length+(names+1)*4 > maxlength)
+ { /* Expand file */
+ newpos+=IO_SIZE;
+ int4store(fileinfo+10,newpos);
+ endpos=(ulong) my_seek(file,0L,MY_SEEK_END,MYF(0)); /* Copy from file-end */
+ bufflength= (uint) (endpos & (IO_SIZE-1)); /* IO_SIZE is a power of 2 */
+
+ while (endpos > maxlength)
+ {
+ VOID(my_seek(file,(ulong) (endpos-bufflength),MY_SEEK_SET,MYF(0)));
+ if (my_read(file,(byte*) buff,bufflength,MYF(MY_NABP+MY_WME)))
+ DBUG_RETURN(0L);
+ VOID(my_seek(file,(ulong) (endpos-bufflength+IO_SIZE),MY_SEEK_SET,
+ MYF(0)));
+ if ((my_write(file,(byte*) buff,bufflength,MYF(MY_NABP+MY_WME))))
+ DBUG_RETURN(0);
+ endpos-=bufflength; bufflength=IO_SIZE;
+ }
+ bzero(buff,IO_SIZE); /* Null new block */
+ VOID(my_seek(file,(ulong) maxlength,MY_SEEK_SET,MYF(0)));
+ if (my_write(file,(byte*) buff,bufflength,MYF(MY_NABP+MY_WME)))
+ DBUG_RETURN(0L);
+ maxlength+=IO_SIZE; /* Fix old ref */
+ int2store(fileinfo+6,maxlength);
+ for (i=names, pos= (uchar*) *formnames->type_names+n_length-1; i-- ;
+ pos+=4)
+ {
+ endpos=uint4korr(pos)+IO_SIZE;
+ int4store(pos,endpos);
+ }
+ }
+
+ if (n_length == 1 )
+ { /* First name */
+ length++;
+ VOID(strxmov(buff,"/",newname,"/",NullS));
+ }
+ else
+ VOID(strxmov(buff,newname,"/",NullS)); /* purecov: inspected */
+ VOID(my_seek(file,63L+(ulong) n_length,MY_SEEK_SET,MYF(0)));
+ if (my_write(file,(byte*) buff,(uint) length+1,MYF(MY_NABP+MY_WME)) ||
+ (names && my_write(file,(byte*) (*formnames->type_names+n_length-1),
+ names*4, MYF(MY_NABP+MY_WME))) ||
+ my_write(file,(byte*) fileinfo+10,(uint) 4,MYF(MY_NABP+MY_WME)))
+ DBUG_RETURN(0L); /* purecov: inspected */
+
+ int2store(fileinfo+8,names+1);
+ int2store(fileinfo+4,n_length+length);
+ VOID(my_chsize(file,newpos,MYF(MY_WME))); /* Append file with '\0' */
+ DBUG_RETURN(newpos);
+} /* make_new_entry */
+
+
+ /* error message when opening a form file */
+
+static void frm_error(int error, TABLE *form, const char *name, myf errortype)
+{
+ int err_no;
+ char buff[FN_REFLEN];
+ const char *form_dev="",*datext;
+ DBUG_ENTER("frm_error");
+
+ switch (error) {
+ case 1:
+ if (my_errno == ENOENT)
+ {
+ char *db;
+ uint length=dirname_part(buff,name);
+ buff[length-1]=0;
+ db=buff+dirname_length(buff);
+ my_error(ER_NO_SUCH_TABLE,MYF(0),db,form->real_name);
+ }
+ else
+ my_error(ER_FILE_NOT_FOUND,errortype,
+ fn_format(buff,name,form_dev,reg_ext,0),my_errno);
+ break;
+ case 2:
+ {
+ datext=form->file ? *form->file->bas_ext() : "";
+ err_no= (my_errno == ENOENT) ? ER_FILE_NOT_FOUND : (my_errno == EAGAIN) ?
+ ER_FILE_USED : ER_CANT_OPEN_FILE;
+ my_error(err_no,errortype,
+ fn_format(buff,form->real_name,form_dev,datext,2),my_errno);
+ break;
+ }
+ default: /* Better wrong error than none */
+ case 4:
+ my_error(ER_NOT_FORM_FILE,errortype,
+ fn_format(buff,name,form_dev,reg_ext,0));
+ break;
+ }
+ DBUG_VOID_RETURN;
+} /* frm_error */
+
+
+ /*
+ ** fix a str_type to a array type
+ ** typeparts sepearated with some char. differents types are separated
+ ** with a '\0'
+ */
+
+static void
+fix_type_pointers(const char ***array, TYPELIB *point_to_type, uint types,
+ char **names)
+{
+ char *type_name, *ptr;
+ char chr;
+
+ ptr= *names;
+ while (types--)
+ {
+ point_to_type->name=0;
+ point_to_type->type_names= *array;
+
+ if ((chr= *ptr)) /* Test if empty type */
+ {
+ while ((type_name=strchr(ptr+1,chr)) != NullS)
+ {
+ *((*array)++) = ptr+1;
+ *type_name= '\0'; /* End string */
+ ptr=type_name;
+ }
+ ptr+=2; /* Skipp end mark and last 0 */
+ }
+ else
+ ptr++;
+ point_to_type->count= (uint) (*array - point_to_type->type_names);
+ point_to_type++;
+ *((*array)++)= NullS; /* End of type */
+ }
+ *names=ptr; /* Update end */
+ return;
+} /* fix_type_pointers */
+
+
+TYPELIB *typelib(List<String> &strings)
+{
+ TYPELIB *result=(TYPELIB*) sql_alloc(sizeof(TYPELIB));
+ if (!result)
+ return 0;
+ result->count=strings.elements;
+ result->name="";
+ if (!(result->type_names=(const char **) sql_alloc(sizeof(char *)*
+ (result->count+1))))
+ return 0;
+ List_iterator<String> it(strings);
+ String *tmp;
+ for (uint i=0; (tmp=it++) ; i++)
+ result->type_names[i]=tmp->ptr();
+ result->type_names[result->count]=0; // End marker
+ return result;
+}
+
+
+ /*
+ ** Search after a field with given start & length
+ ** If an exact field isn't found, return longest field with starts
+ ** at right position.
+ ** Return 0 on error, else field number+1
+ ** This is needed because in some .frm fields 'fieldnr' was saved wrong
+ */
+
+static uint find_field(TABLE *form,uint start,uint length)
+{
+ Field **field;
+ uint i,pos;
+
+ pos=0;
+
+ for (field=form->field, i=1 ; i<= form->fields ; i++,field++)
+ {
+ if ((*field)->offset() == start)
+ {
+ if ((*field)->key_length() == length)
+ return (i);
+ if (!pos || form->field[pos-1]->pack_length() <
+ (*field)->pack_length())
+ pos=i;
+ }
+ }
+ return (pos);
+}
+
+
+ /* Check that the integer is in the internvall */
+
+int set_zone(register int nr, int min_zone, int max_zone)
+{
+ if (nr<=min_zone)
+ return (min_zone);
+ if (nr>=max_zone)
+ return (max_zone);
+ return (nr);
+} /* set_zone */
+
+ /* Adjust number to next larger disk buffer */
+
+ulong next_io_size(register ulong pos)
+{
+ reg2 ulong offset;
+ if ((offset= pos & (IO_SIZE-1)))
+ return pos-offset+IO_SIZE;
+ return pos;
+} /* next_io_size */
+
+
+void append_unescaped(String *res,const char *pos)
+{
+ for ( ; *pos ; pos++)
+ {
+ switch (*pos) {
+ case 0: /* Must be escaped for 'mysql' */
+ res->append('\\');
+ res->append('0');
+ break;
+ case '\n': /* Must be escaped for logs */
+ res->append('\\');
+ res->append('n');
+ break;
+ case '\r':
+ res->append('\\'); /* This gives better readbility */
+ res->append('r');
+ break;
+ case '\\':
+ res->append('\\'); /* Because of the sql syntax */
+ res->append('\\');
+ break;
+ case '\'':
+ res->append('\''); /* Because of the sql syntax */
+ res->append('\'');
+ break;
+ default:
+ res->append(*pos);
+ break;
+ }
+ }
+}
+
+ /* Create a .frm file */
+
+File create_frm(register my_string name, uint reclength, uchar *fileinfo,
+ HA_CREATE_INFO *create_info, uint keys)
+{
+ register File file;
+ uint key_length;
+ ulong length;
+ char fill[IO_SIZE];
+
+#if SIZEOF_OFF_T > 4
+ /* Fix this in MySQL 4.0; The current limit is 4G rows (QQ) */
+ if (create_info->max_rows > ~(ulong) 0)
+ create_info->max_rows= ~(ulong) 0;
+ if (create_info->min_rows > ~(ulong) 0)
+ create_info->min_rows= ~(ulong) 0;
+#endif
+
+ if ((file=my_create(name,CREATE_MODE,O_RDWR | O_TRUNC,MYF(MY_WME))) >= 0)
+ {
+ bzero((char*) fileinfo,64);
+ fileinfo[0]=(uchar) 254; fileinfo[1]= 1; fileinfo[2]= FRM_VER+1; // Header
+ fileinfo[3]= (uchar) ha_checktype(create_info->db_type);
+ fileinfo[4]=1;
+ int2store(fileinfo+6,IO_SIZE); /* Next block starts here */
+ key_length=keys*(7+NAME_LEN+MAX_REF_PARTS*9)+16;
+ length=(ulong) next_io_size((ulong) (IO_SIZE+key_length+reclength));
+ int4store(fileinfo+10,length);
+ int2store(fileinfo+14,key_length);
+ int2store(fileinfo+16,reclength);
+ int4store(fileinfo+18,create_info->max_rows);
+ int4store(fileinfo+22,create_info->min_rows);
+ fileinfo[27]=2; // Use long pack-fields
+ create_info->table_options|=HA_OPTION_LONG_BLOB_PTR; // Use portable blob pointers
+ int2store(fileinfo+30,create_info->table_options);
+ fileinfo[32]=0; // No filename anymore
+ int4store(fileinfo+34,create_info->avg_row_length);
+ fileinfo[40]= (uchar) create_info->row_type;
+ fileinfo[41]= (uchar) create_info->raid_type;
+ fileinfo[42]= (uchar) create_info->raid_chunks;
+ int4store(fileinfo+43,create_info->raid_chunksize);
+ bzero(fill,IO_SIZE);
+ for (; length > IO_SIZE ; length-= IO_SIZE)
+ {
+ if (my_write(file,(byte*) fill,IO_SIZE,MYF(MY_WME | MY_NABP)))
+ {
+ VOID(my_close(file,MYF(0)));
+ VOID(my_delete(name,MYF(0)));
+ return(-1);
+ }
+ }
+ }
+ return (file);
+} /* create_frm */
+
+
+void update_create_info_from_table(HA_CREATE_INFO *create_info, TABLE *table)
+{
+ create_info->max_rows=table->max_rows;
+ create_info->min_rows=table->min_rows;
+ create_info->table_options=table->db_create_options;
+ create_info->avg_row_length=table->avg_row_length;
+ create_info->row_type=table->row_type;
+ create_info->raid_type=table->raid_type;
+ create_info->raid_chunks=table->raid_chunks;
+ create_info->raid_chunksize=table->raid_chunksize;
+}
+
+int
+rename_file_ext(const char * from,const char * to,const char * ext)
+{
+ char from_b[FN_REFLEN],to_b[FN_REFLEN];
+ VOID(strxmov(from_b,from,ext,NullS));
+ VOID(strxmov(to_b,to,ext,NullS));
+ return (my_rename(from_b,to_b,MYF(MY_WME)));
+}
+
+
+/*
+ Alloc a value as a string and return it
+ If field is empty, return NULL
+*/
+
+char *get_field(MEM_ROOT *mem, TABLE *table, uint fieldnr)
+{
+ Field *field=table->field[fieldnr];
+ char buff[MAX_FIELD_WIDTH];
+ String str(buff,sizeof(buff));
+ field->val_str(&str,&str);
+ uint length=str.length();
+ if (!length)
+ return NullS;
+ char *to= (char*) alloc_root(mem,length+1);
+ memcpy(to,str.ptr(),(uint) length);
+ to[length]=0;
+ return to;
+}
+
+bool check_db_name(const char *name)
+{
+ while (*name)
+ {
+#if defined(USE_MB) && defined(USE_MB_IDENT)
+ if (use_mb(default_charset_info))
+ {
+ int len=my_ismbchar(default_charset_info, name, name+MBMAXLEN);
+ if (len)
+ {
+ name += len;
+ continue;
+ }
+ }
+#endif
+ if (*name == '/' || *name == FN_LIBCHAR)
+ return 1;
+ name++;
+ }
+ return 0;
+}
+
+
+/*
+ Allow anything as a table name, as long as it doesn't contain an
+ a '/', or a '.' character
+ returns 1 on error
+*/
+
+
+bool check_table_name(const char *name, uint length)
+{
+ const char *end= name+length;
+
+ while (name != end)
+ {
+#if defined(USE_MB) && defined(USE_MB_IDENT)
+ if (use_mb(default_charset_info))
+ {
+ int len=my_ismbchar(default_charset_info, name, end);
+ if (len)
+ {
+ name += len;
+ continue;
+ }
+ }
+#endif
+ if (*name == '/' || *name == FN_LIBCHAR || *name == FN_EXTCHAR)
+ return 1;
+ name++;
+ }
+ return 0;
+}
+
+bool check_column_name(const char *name)
+{
+ while (*name)
+ {
+#if defined(USE_MB) && defined(USE_MB_IDENT)
+ if (use_mb(default_charset_info))
+ {
+ int len=my_ismbchar(default_charset_info, name, name+MBMAXLEN);
+ if (len)
+ {
+ name += len;
+ continue;
+ }
+ }
+#endif
+ if (*name == NAMES_SEP_CHAR)
+ return 1;
+ name++;
+ }
+ return 0;
+}
+
+/*
+** Get type of table from .frm file
+*/
+
+db_type get_table_type(const char *name)
+{
+ File file;
+ uchar head[4];
+ int error;
+ DBUG_ENTER("get_table_type");
+ DBUG_PRINT("enter",("name: '%s'",name));
+
+ if ((file=my_open(name,O_RDONLY, MYF(0))) < 0)
+ DBUG_RETURN(DB_TYPE_UNKNOWN);
+ 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))
+ DBUG_RETURN(DB_TYPE_UNKNOWN);
+ DBUG_RETURN(ha_checktype((enum db_type) (uint) *(head+3)));
+}
+
+
+/*****************************************************************************
+** Instansiate templates
+*****************************************************************************/
+
+#ifdef __GNUC__
+template class List<String>;
+template class List_iterator<String>;
+#endif
diff --git a/sql/table.h b/sql/table.h
new file mode 100644
index 00000000000..7fe3e5de601
--- /dev/null
+++ b/sql/table.h
@@ -0,0 +1,136 @@
+/* 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 */
+
+
+/* Structs that defines the TABLE */
+
+class Item; /* Needed by ORDER */
+class GRANT_TABLE;
+
+/* Order clause list element */
+
+typedef struct st_order {
+ struct st_order *next;
+ Item **item; /* Point at item in select fields */
+ bool asc; /* true if ascending */
+ bool free_me; /* true if item isn't shared */
+ bool in_field_list; /* true if in select field list */
+ Field *field; /* If tmp-table group */
+ char *buff; /* If tmp-table group */
+ table_map used,depend_map;
+} ORDER;
+
+typedef struct st_grant_info
+{
+ GRANT_TABLE *grant_table;
+ uint version;
+ uint privilege;
+ uint want_privilege;
+} GRANT_INFO;
+
+/* Table cache entry struct */
+
+class Field_timestamp;
+class Field_blob;
+
+struct st_table {
+ handler *file;
+ Field **field; /* Pointer to fields */
+ Field_blob **blob_field; /* Pointer to blob fields */
+ HASH name_hash; /* hash of field names */
+ byte *record[3]; /* Pointer to records */
+ uint fields; /* field count */
+ uint reclength; /* Recordlength */
+ uint rec_buff_length;
+ uint keys,key_parts,primary_key,max_key_length,max_unique_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 *key_info; /* data of keys in database */
+ TYPELIB keynames; /* Pointers to keynames */
+ ha_rows max_rows; /* create information */
+ ha_rows min_rows; /* create information */
+ ulong avg_row_length; /* create information */
+ ulong raid_chunksize;
+ TYPELIB fieldnames; /* Pointer to fieldnames */
+ TYPELIB *intervals; /* pointer to interval info */
+ enum db_type db_type; /* table_type for handler */
+ enum row_type row_type; /* How rows are stored */
+ uint db_create_options; /* Create options from database */
+ uint db_options_in_use; /* Options in use */
+ uint db_record_offset; /* if HA_REC_IN_SEQ */
+ uint db_stat; /* mode of file as in handler.h */
+ uint raid_type,raid_chunks;
+ uint status; /* Used by postfix.. */
+ uint system; /* Set if system record */
+ ulong time_stamp; /* Set to offset+1 of record */
+ uint timestamp_field_offset;
+ uint next_number_index;
+ uint blob_ptr_size; /* 4 or 8 */
+ uint next_number_key_offset;
+ int current_lock; /* Type of lock on 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,tmp_table,const_table;
+ my_bool key_read;
+ my_bool crypted;
+ my_bool db_low_byte_first; /* Portable row format */
+ my_bool locked_by_flush;
+ Field *next_number_field, /* Set if next_number is activated */
+ *found_next_number_field, /* Set on open */
+ *rowid_field;
+ Field_timestamp *timestamp_field;
+ my_string comment; /* Comment about table */
+ REGINFO reginfo; /* field connections */
+ MEM_ROOT mem_root;
+ GRANT_INFO grant;
+
+ char *table_cache_key;
+ char *table_name,*real_name,*path;
+ uint key_length; /* Length of key */
+ uint tablenr,used_fields,null_bytes;
+ table_map map;
+ 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 */
+ ORDER *group;
+ key_map quick_keys,used_keys;
+ ha_rows quick_rows[MAX_KEY];
+ uint quick_key_parts[MAX_KEY];
+ key_part_map const_key_parts[MAX_KEY];
+ ulong query_id;
+
+ THD *in_use; /* Which thread uses this */
+ struct st_table *next,*prev;
+};
+
+
+typedef struct st_table_list {
+ struct st_table_list *next;
+ char *db,*name,*real_name;
+ thr_lock_type lock_type;
+ bool straight; /* optimize with prev table */
+ bool outer_join;
+ TABLE *table;
+ Item *on_expr; /* Used with outer join */
+ struct st_table_list *natural_join; /* natural join on this table*/
+ List<String> *use_index,*ignore_index;
+ GRANT_INFO grant;
+} TABLE_LIST;
diff --git a/sql/thr_malloc.cc b/sql/thr_malloc.cc
new file mode 100644
index 00000000000..8cf4f937546
--- /dev/null
+++ b/sql/thr_malloc.cc
@@ -0,0 +1,88 @@
+/* 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 */
+
+
+/* Mallocs for used in threads */
+
+#include "mysql_priv.h"
+
+extern "C" {
+ void sql_alloc_error_handler(void)
+ {
+ current_thd->fatal_error=1; /* purecov: inspected */
+ }
+}
+
+void init_sql_alloc(MEM_ROOT *mem_root,uint block_size)
+{
+ init_alloc_root(mem_root,block_size);
+ mem_root->error_handler=sql_alloc_error_handler;
+}
+
+
+gptr sql_alloc(uint Size)
+{
+ MEM_ROOT *root=my_pthread_getspecific_ptr(MEM_ROOT*,THR_MALLOC);
+ char *ptr= (char*) alloc_root(root,Size);
+ if (!ptr)
+ {
+ sql_print_error(ER(ER_OUT_OF_RESOURCES));
+ }
+ return ptr;
+}
+
+
+gptr sql_calloc(uint size)
+{
+ gptr ptr;
+ if ((ptr=sql_alloc(size)))
+ bzero((char*) ptr,size);
+ return ptr;
+}
+
+
+char *sql_strdup(const char *str)
+{
+ uint len=strlen(str)+1;
+ char *pos;
+ if ((pos= (char*) sql_alloc(len)))
+ memcpy(pos,str,len);
+ return pos;
+}
+
+
+char *sql_strmake(const char *str,uint len)
+{
+ char *pos;
+ if ((pos= (char*) sql_alloc(len+1)))
+ {
+ memcpy(pos,str,len);
+ pos[len]=0;
+ }
+ return pos;
+}
+
+
+gptr sql_memdup(const void *ptr,uint len)
+{
+ char *pos;
+ if ((pos= (char*) sql_alloc(len)))
+ memcpy(pos,ptr,len);
+ return pos;
+}
+
+void sql_element_free(void *ptr __attribute__((unused)))
+{} /* purecov: deadcode */
diff --git a/sql/time.cc b/sql/time.cc
new file mode 100644
index 00000000000..066979d1c9a
--- /dev/null
+++ b/sql/time.cc
@@ -0,0 +1,690 @@
+/* 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 */
+
+
+/* Functions to handle date and time */
+
+#include "mysql_priv.h"
+#include <m_ctype.h>
+
+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 */
+ /* Currently only my_time_zone is inited */
+
+static long my_time_zone=0;
+pthread_mutex_t LOCK_timezone;
+
+void init_time(void)
+{
+ time_t seconds;
+ struct tm *l_time,tm_tmp;;
+ TIME my_time;
+
+ seconds= (time_t) time((time_t*) 0);
+ localtime_r(&seconds,&tm_tmp);
+ l_time= &tm_tmp;
+ my_time_zone=0;
+ my_time.year= (uint) l_time->tm_year+1900;
+ my_time.month= (uint) l_time->tm_mon+1;
+ my_time.day= (uint) l_time->tm_mday;
+ my_time.hour= (uint) l_time->tm_hour;
+ my_time.minute= (uint) l_time->tm_min;
+ my_time.second= (uint) l_time->tm_sec;
+ VOID(my_gmt_sec(&my_time)); /* Init my_time_zone */
+}
+
+/*
+ Convert current time to sec. since 1970.01.01
+ 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)
+{
+ uint loop;
+ time_t tmp;
+ struct tm *l_time,tm_tmp;
+ long diff;
+
+ if (t->hour >= 24)
+ { /* Fix for time-loop */
+ t->day+=t->hour/24;
+ t->hour%=24;
+ }
+ pthread_mutex_lock(&LOCK_timezone);
+ tmp=(time_t) ((calc_daynr((uint) t->year,(uint) t->month,(uint) t->day) -
+ (long) days_at_timestart)*86400L + (long) t->hour*3600L +
+ (long) (t->minute*60 + t->second)) + (time_t) my_time_zone;
+ localtime_r(&tmp,&tm_tmp);
+ l_time=&tm_tmp;
+ for (loop=0;
+ loop < 3 &&
+ (t->hour != (uint) l_time->tm_hour ||
+ t->minute != (uint) l_time->tm_min);
+ loop++)
+ { /* One check should be enough ? */
+ /* Get difference in days */
+ int days= t->day - l_time->tm_mday;
+ if (days < -1)
+ days= 1; // Month has wrapped
+ else if (days > 1)
+ days= -1;
+ diff=(3600L*(long) (days*24+((int) t->hour - (int) l_time->tm_hour)) +
+ (long) (60*((int) t->minute - (int) l_time->tm_min)));
+ my_time_zone+=diff;
+ tmp+=(time_t) diff;
+ localtime_r(&tmp,&tm_tmp);
+ l_time=&tm_tmp;
+ }
+ /* Fix that if we are in the not existing daylight saving time hour
+ we move the start of the next real hour */
+ if (loop == 3 && t->hour != (uint) l_time->tm_hour)
+ {
+ int days= t->day - l_time->tm_mday;
+ if (days < -1)
+ days=1; // Month has wrapped
+ else if (days > 1)
+ days= -1;
+ diff=(3600L*(long) (days*24+((int) t->hour - (int) l_time->tm_hour))+
+ (long) (60*((int) t->minute - (int) l_time->tm_min)));
+ if (diff == 3600)
+ tmp+=3600 - t->minute*60 - t->second; // Move to next hour
+ else if (diff == -3600)
+ tmp-=t->minute*60 + t->second; // Move to next hour
+ }
+ if ((my_time_zone >=0 ? my_time_zone: -my_time_zone) > 3600L*12)
+ my_time_zone=0; /* Wrong date */
+ pthread_mutex_unlock(&LOCK_timezone);
+ return tmp;
+} /* my_gmt_sec */
+
+
+ /* Some functions to calculate dates */
+
+ /* Calculate nr of day since year 0 in new date-system (from 1615) */
+
+long calc_daynr(uint year,uint month,uint day)
+{
+ long delsum;
+ int temp;
+ DBUG_ENTER("calc_daynr");
+
+ if (year == 0 && month == 0 && day == 0)
+ DBUG_RETURN(0); /* Skipp errors */
+ if (year < 200)
+ {
+ if ((year=year+1900) < 1900+YY_PART_YEAR)
+ year+=100;
+ }
+ delsum= (long) (365L * year+ 31*(month-1) +day);
+ if (month <= 2)
+ year--;
+ else
+ delsum-= (long) (month*4+23)/10;
+ temp=(int) ((year/100+1)*3)/4;
+ DBUG_PRINT("exit",("year: %d month: %d day: %d -> daynr: %ld",
+ year+(month <= 2),month,day,delsum+year/4-temp));
+ DBUG_RETURN(delsum+(int) year/4-temp);
+} /* calc_daynr */
+
+
+ /* Calc weekday from daynr */
+ /* Returns 0 for monday, 1 for tuesday .... */
+
+int calc_weekday(long daynr,bool sunday_first_day_of_week)
+{
+ DBUG_ENTER("calc_weekday");
+ DBUG_RETURN ((int) ((daynr + 5L + (sunday_first_day_of_week ? 1L : 0L)) % 7));
+}
+
+ /* Calc days in one year. works with 0 <= year <= 99 */
+
+uint calc_days_in_year(uint year)
+{
+ return (year & 3) == 0 && (year%100 || (year%400 == 0 && year)) ?
+ 366 : 365;
+}
+
+/* Calculate week. If 'with_year' is not set, then return a week 0-53, where
+ 0 means that it's the last week of the previous year.
+ If 'with_year' is set then the week will always be in the range 1-53 and
+ the year out parameter will contain the year for the week */
+
+uint calc_week(TIME *l_time, bool with_year, bool sunday_first_day_of_week,
+ uint *year)
+{
+ uint days;
+ ulong daynr=calc_daynr(l_time->year,l_time->month,l_time->day);
+ 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)
+ {
+ /* Last week of the previous year */
+ if (!with_year)
+ return 0;
+ with_year=0; // Don't check the week again
+ (*year)--;
+ first_daynr-= (days=calc_days_in_year(*year));
+ weekday= (weekday + 53*7- days) % 7;
+ }
+ if (weekday >= 4)
+ days= daynr - (first_daynr+ (7-weekday));
+ else
+ days= daynr - (first_daynr - weekday);
+ if (with_year && days >= 52*7)
+ {
+ /* Check if we are on the first week of the next year (or week 53) */
+ weekday= (weekday + calc_days_in_year(*year)) % 7;
+ if (weekday < 4)
+ { // We are at first week on next year
+ (*year)++;
+ return 1;
+ }
+ }
+ return days/7+1;
+}
+
+ /* Change a daynr to year, month and day */
+ /* Daynr 0 is returned as date 00.00.00 */
+
+void get_date_from_daynr(long daynr,uint *ret_year,uint *ret_month,
+ uint *ret_day)
+{
+ uint year,temp,leap_day,day_of_year,days_in_year;
+ uchar *month_pos;
+ DBUG_ENTER("get_date_from_daynr");
+
+ if (daynr <= 365L || daynr >= 3652500)
+ { /* Fix if wrong daynr */
+ *ret_year= *ret_month = *ret_day =0;
+ }
+ else
+ {
+ year= (uint) (daynr*100 / 36525L);
+ temp=(((year-1)/100+1)*3)/4;
+ day_of_year=(uint) (daynr - (long) year * 365L) - (year-1)/4 +temp;
+ while (day_of_year > (days_in_year= calc_days_in_year(year)))
+ {
+ day_of_year-=days_in_year;
+ (year)++;
+ }
+ leap_day=0;
+ if (days_in_year == 366)
+ {
+ if (day_of_year > 31+28)
+ {
+ day_of_year--;
+ if (day_of_year == 31+28)
+ leap_day=1; /* Handle leapyears leapday */
+ }
+ }
+ *ret_month=1;
+ for (month_pos= days_in_month ;
+ day_of_year > (uint) *month_pos ;
+ day_of_year-= *(month_pos++), (*ret_month)++)
+ ;
+ *ret_year=year;
+ *ret_day=day_of_year+leap_day;
+ }
+ DBUG_VOID_RETURN;
+}
+
+/* find date from string and put it in vektor
+ Input: pos = "YYMMDD" OR "YYYYMMDD" in any order or
+ "xxxxx YYxxxMMxxxDD xxxx" where xxx is anything exept
+ a number. Month or day mustn't exeed 2 digits, year may be 4 digits.
+*/
+
+
+#ifdef NOT_NEEDED
+
+void find_date(string pos,uint *vek,uint flag)
+{
+ uint length,value;
+ string start;
+ DBUG_ENTER("find_date");
+ DBUG_PRINT("enter",("pos: '%s' flag: %d",pos,flag));
+
+ bzero((char*) vek,sizeof(int)*4);
+ while (*pos && !isdigit(*pos))
+ pos++;
+ length=strlen(pos);
+ for (uint i=0 ; i< 3; i++)
+ {
+ start=pos; value=0;
+ while (isdigit(pos[0]) &&
+ ((pos-start) < 2 || ((pos-start) < 4 && length >= 8 &&
+ !(flag & 3))))
+ {
+ value=value*10 + (uint) (uchar) (*pos - '0');
+ pos++;
+ }
+ vek[flag & 3]=value; flag>>=2;
+ while (*pos && (ispunct(*pos) || isspace(*pos)))
+ pos++;
+ }
+ DBUG_PRINT("exit",("year: %d month: %d day: %d",vek[0],vek[1],vek[2]));
+ DBUG_VOID_RETURN;
+} /* find_date */
+
+
+ /* Outputs YYMMDD if input year < 100 or YYYYMMDD else */
+
+static long calc_daynr_from_week(uint year,uint week,uint day)
+{
+ long daynr;
+ int weekday;
+
+ daynr=calc_daynr(year,1,1);
+ if ((weekday= calc_weekday(daynr,0)) >= 3)
+ daynr+= (7-weekday);
+ else
+ daynr-=weekday;
+
+ return (daynr+week*7+day-8);
+}
+
+void convert_week_to_date(string date,uint flag,uint *res_length)
+{
+ string format;
+ uint year,vek[4];
+
+ find_date(date,vek,(uint) (1*4+2*16)); /* YY-WW-DD */
+ year=vek[0];
+
+ get_date_from_daynr(calc_daynr_from_week(vek[0],vek[1],vek[2]),
+ &vek[0],&vek[1],&vek[2]);
+ *res_length=8;
+ format="%04d%02d%02d";
+ if (year < 100)
+ {
+ vek[0]= vek[0]%100;
+ *res_length=6;
+ format="%02d%02d%02d";
+ }
+ sprintf(date,format,vek[flag & 3],vek[(flag >> 2) & 3],
+ vek[(flag >> 4) & 3]);
+ return;
+}
+
+ /* returns YYWWDD or YYYYWWDD according to input year */
+ /* flag only reflects format of input date */
+
+void convert_date_to_week(string date,uint flag,uint *res_length)
+{
+ uint vek[4],weekday,days,year,week,day;
+ long daynr,first_daynr;
+ char buff[256],*format;
+
+ if (! date[0])
+ {
+ get_date(buff,0,0L); /* Use current date */
+ find_date(buff+2,vek,(uint) (1*4+2*16)); /* YY-MM-DD */
+ }
+ else
+ find_date(date,vek,flag);
+
+ year= vek[0];
+ daynr= calc_daynr(year,vek[1],vek[2]);
+ first_daynr=calc_daynr(year,1,1);
+
+ /* Caculate year and first daynr of year */
+ if (vek[1] == 1 && (weekday=calc_weekday(first_daynr,0)) >= 3 &&
+ vek[2] <= 7-weekday)
+ {
+ if (!year--)
+ year=99;
+ first_daynr=first_daynr-calc_days_in_year(year);
+ }
+ else if (vek[1] == 12 &&
+ (weekday=calc_weekday(first_daynr+calc_days_in_year(year)),0) < 3 &&
+ vek[2] > 31-weekday)
+ {
+ first_daynr=first_daynr+calc_days_in_year(year);
+ if (year++ == 99)
+ year=0;
+ }
+
+ /* Calulate daynr of first day of week 1 */
+ if ((weekday= calc_weekday(first_daynr,0)) >= 3)
+ first_daynr+= (7-weekday);
+ else
+ first_daynr-=weekday;
+
+ days=(int) (daynr-first_daynr);
+ week=days/7+1 ; day=calc_weekday(daynr,0)+1;
+
+ *res_length=8;
+ format="%04d%02d%02d";
+ if (year < 100)
+ {
+ *res_length=6;
+ format="%02d%02d%02d";
+ }
+ sprintf(date,format,year,week,day);
+ return;
+}
+
+#endif
+
+ /* Functions to handle periods */
+
+ulong convert_period_to_month(ulong period)
+{
+ ulong a,b;
+ if (period == 0)
+ return 0L;
+ if ((a=period/100) < YY_PART_YEAR)
+ a+=2000;
+ else if (a < 100)
+ a+=1900;
+ b=period%100;
+ return a*12+b-1;
+}
+
+ulong convert_month_to_period(ulong month)
+{
+ ulong year;
+ if (month == 0L)
+ return 0L;
+ if ((year=month/12) < 100)
+ {
+ year+=(year < YY_PART_YEAR) ? 2000 : 1900;
+ }
+ return year*100+month%12+1;
+}
+
+#ifdef NOT_NEEDED
+
+ulong add_to_period(ulong period,int months)
+{
+ if (period == 0L)
+ return 0L;
+ return convert_month_to_period(convert_period_to_month(period)+months);
+}
+#endif
+
+
+/*****************************************************************************
+** convert a timestamp string to a TIME value.
+** At least the following formats are recogniced (based on number of digits)
+** YYMMDD, YYYYMMDD, YYMMDDHHMMSS, YYYYMMDDHHMMSS
+** YY-MM-DD, YYYY-MM-DD, YY-MM-DD HH.MM.SS
+** Returns the type of string
+*****************************************************************************/
+
+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];
+ bool date_used=0;
+ const char *pos;
+ const char *end=str+length;
+ DBUG_ENTER("str_to_TIME");
+ DBUG_PRINT("enter",("str: %.*s",length,str));
+
+ for (; !isdigit(*str) && str != end ; str++) ; // Skipp garbage
+ if (str == end)
+ DBUG_RETURN(TIMESTAMP_NONE);
+ /*
+ ** calculate first number of digits.
+ ** If length= 8 or >= 14 then year is of format YYYY.
+ (YYYY-MM-DD, YYYYMMDD, YYYYYMMDDHHMMSS)
+ */
+ for (pos=str; pos != end && isdigit(*pos) ; pos++) ;
+ digits= (uint) (pos-str);
+ year_length= (digits == 4 || digits == 8 || digits >= 14) ? 4 : 2;
+ field_length=year_length-1;
+ for (i=0 ; i < 6 && str != end && isdigit(*str) ; i++)
+ {
+ uint tmp_value=(uint) (uchar) (*str++ - '0');
+ while (str != end && isdigit(str[0]) && field_length--)
+ {
+ tmp_value=tmp_value*10 + (uint) (uchar) (*str - '0');
+ str++;
+ }
+ if ((date[i]=tmp_value))
+ date_used=1; // Found something
+ if (i == 2 && str != end && *str == 'T')
+ str++; // ISO8601: CCYYMMDDThhmmss
+ else
+ {
+ while (str != end && (ispunct(*str) || isspace(*str)))
+ {
+ // Only allow space between days and hours
+ if (isspace(*str) && i != 2)
+ DBUG_RETURN(TIMESTAMP_NONE);
+ str++;
+ }
+ }
+ field_length=1; // Rest fields can only be 2
+ }
+ /* Handle second fractions */
+ if (i == 6 && (uint) (end-str) >= 2 && *str == '.' && isdigit(str[1]))
+ {
+ str++;
+ uint tmp_value=(uint) (uchar) (*str - '0');
+ field_length=3;
+ while (str++ != end && isdigit(str[0]) && field_length--)
+ tmp_value=tmp_value*10 + (uint) (uchar) (*str - '0');
+ date[6]=tmp_value;
+ }
+ else
+ date[6]=0;
+
+ if (year_length == 2)
+ date[0]+= (date[0] < YY_PART_YEAR ? 2000 : 1900);
+ number_of_fields=i;
+ while (i < 6)
+ date[i++]=0;
+ if (number_of_fields < 3 || !date_used || date[1] > 12 ||
+ date[2] > 31 || date[3] > 23 || date[4] > 59 || date[5] > 59 ||
+ !fuzzy_date && (date[1] == 0 || date[2] == 0))
+ {
+ current_thd->cuted_fields++;
+ DBUG_RETURN(TIMESTAMP_NONE);
+ }
+ if (str != end && current_thd->count_cuted_fields)
+ {
+ for ( ; str != end ; str++)
+ {
+ if (!isspace(*str))
+ {
+ current_thd->cuted_fields++;
+ break;
+ }
+ }
+ }
+ l_time->year= date[0];
+ l_time->month= date[1];
+ l_time->day= date[2];
+ l_time->hour= date[3];
+ l_time->minute=date[4];
+ l_time->second=date[5];
+ l_time->second_part=date[6];
+ DBUG_RETURN(l_time->time_type=
+ (number_of_fields <= 3 ? TIMESTAMP_DATE : TIMESTAMP_FULL));
+}
+
+
+time_t str_to_timestamp(const char *str,uint length)
+{
+ TIME l_time;
+ if (str_to_TIME(str,length,&l_time,0) == TIMESTAMP_NONE)
+ return(0);
+ if (l_time.year >= TIMESTAMP_MAX_YEAR || l_time.year < 1900+YY_PART_YEAR)
+ {
+ current_thd->cuted_fields++;
+ return(0);
+ }
+ return(my_gmt_sec(&l_time));
+}
+
+
+longlong str_to_datetime(const char *str,uint length,bool fuzzy_date)
+{
+ TIME l_time;
+ if (str_to_TIME(str,length,&l_time,fuzzy_date) == TIMESTAMP_NONE)
+ return(0);
+ return (longlong) (l_time.year*LL(10000000000) +
+ l_time.month*LL(100000000)+
+ l_time.day*LL(1000000)+
+ l_time.hour*LL(10000)+
+ (longlong) (l_time.minute*100+l_time.second));
+}
+
+
+/*****************************************************************************
+** convert a time string to a (ulong) value.
+** Can use all full timestamp formats and
+** [-] DAYS [H]H:MM:SS, [H]H:MM:SS, [M]M:SS, [H]HMMSS, [M]MSS or [S]S
+** There may be an optional [.second_part] after seconds
+*****************************************************************************/
+
+bool str_to_time(const char *str,uint length,TIME *l_time)
+{
+ long date[5],value;
+ const char *end=str+length;
+ bool found_days,found_hours;
+ uint state;
+
+ l_time->neg=0;
+ for (; !isdigit(*str) && *str != '-' && str != end ; str++)
+ length--;
+ if (str != end && *str == '-')
+ {
+ l_time->neg=1;
+ str++;
+ length--;
+ }
+ if (str == end)
+ return 1;
+
+ /* Check first if this is a full TIMESTAMP */
+ if (length >= 12)
+ { // Probably full timestamp
+ if (str_to_TIME(str,length,l_time,1) == TIMESTAMP_FULL)
+ return 0; // Was an ok timestamp
+ }
+
+ /* Not a timestamp. Try to get this as a DAYS_TO_SECOND string */
+ for (value=0; str != end && isdigit(*str) ; str++)
+ value=value*10L + (long) (*str - '0');
+
+ if (*str == ' ')
+ {
+ while (++str != end && str[0] == ' ') ;
+ str--;
+ }
+
+ LINT_INIT(state);
+ found_days=found_hours=0;
+ if ((uint) (end-str) > 1 && (*str == ' ' && isdigit(str[1])))
+ { // days !
+ date[0]=value;
+ state=1; // Assume next is hours
+ found_days=1;
+ str++; // Skipp space;
+ }
+ else if ((end-str) > 1 && *str == ':' && isdigit(str[1]))
+ {
+ date[0]=0; // Assume we found hours
+ date[1]=value;
+ state=2;
+ found_hours=1;
+ str++; // skipp ':'
+ }
+ else
+ {
+ /* String given as one number; assume HHMMSS format */
+ date[0]= 0;
+ date[1]= value/10000;
+ date[2]= value/100 % 100;
+ date[3]= value % 100;
+ state=4;
+ goto fractional;
+ }
+
+ /* Read hours, minutes and seconds */
+ for (;;)
+ {
+ for (value=0; str != end && isdigit(*str) ; str++)
+ value=value*10L + (long) (*str - '0');
+ date[state++]=value;
+ if (state == 4 || (end-str) < 2 || *str != ':' || !isdigit(str[1]))
+ break;
+ str++; // Skipp ':'
+ }
+
+ if (state != 4)
+ { // Not HH:MM:SS
+ /* Fix the date to assume that seconds was given */
+ if (!found_hours && !found_days)
+ {
+ bmove_upp((char*) (date+4), (char*) (date+state),
+ sizeof(long)*(state-1));
+ bzero((char*) date, sizeof(long)*(4-state));
+ }
+ else
+ bzero((char*) (date+state), sizeof(long)*(4-state));
+ }
+
+ fractional:
+ /* Get fractional second part */
+ if ((end-str) >= 2 && *str == '.' && isdigit(str[1]))
+ {
+ uint field_length=3;
+ str++; value=(uint) (uchar) (*str - '0');
+ while (++str != end && isdigit(str[0]) && field_length--)
+ value=value*10 + (uint) (uchar) (*str - '0');
+ date[4]=value;
+ }
+ else
+ date[4]=0;
+
+ /* Some simple checks */
+ if (date[2] >= 60 || date[3] >= 60)
+ {
+ current_thd->cuted_fields++;
+ return 1;
+ }
+ l_time->month=0;
+ l_time->day=date[0];
+ l_time->hour=date[1];
+ l_time->minute=date[2];
+ l_time->second=date[3];
+ l_time->second_part=date[4];
+
+ /* Check if there is garbage at end of the TIME specification */
+ if (str != end && current_thd->count_cuted_fields)
+ {
+ do
+ {
+ if (!isspace(*str))
+ {
+ current_thd->cuted_fields++;
+ break;
+ }
+ } while (++str != end);
+ }
+ return 0;
+}
diff --git a/sql/udf_example.cc b/sql/udf_example.cc
new file mode 100644
index 00000000000..9d94006b281
--- /dev/null
+++ b/sql/udf_example.cc
@@ -0,0 +1,868 @@
+/* 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 */
+
+/*
+** example file of UDF (user definable functions) that are dynamicly loaded
+** into the standard mysqld core.
+**
+** The functions name, type and shared library is saved in the new system
+** table 'func'. To be able to create new functions one must have write
+** privilege for the database 'mysql'. If one starts MySQL with
+** --skip-grant, then UDF initialization will also be skipped.
+**
+** Syntax for the new commands are:
+** create function <function_name> returns {string|real|integer}
+** soname <name_of_shared_library>
+** drop function <function_name>
+**
+** Each defined function may have a xxxx_init function and a xxxx_deinit
+** function. The init function should alloc memory for the function
+** and tell the main function about the max length of the result
+** (for string functions), number of decimals (for double functions) and
+** if the result may be a null value.
+**
+** If a function sets the 'error' argument to 1 the function will not be
+** called anymore and mysqld will return NULL for all calls to this copy
+** of the function.
+**
+** All strings arguments to functions are given as string pointer + length
+** to allow handling of binary data.
+** Remember that all functions must be thread safe. This means that one is not
+** allowed to alloc any global or static variables that changes!
+** If one needs memory one should alloc this in the init function and free
+** this on the __deinit function.
+**
+** Note that the init and __deinit functions are only called once per
+** SQL statement while the value function may be called many times
+**
+** Function 'metaphon' returns a metaphon string of the string argument.
+** This is something like a soundex string, but it's more tuned for English.
+**
+** Function 'myfunc_double' returns summary of codes of all letters
+** of arguments divided by summary length of all its arguments.
+**
+** Function 'myfunc_int' returns summary length of all its arguments.
+**
+** On the end is a couple of functions that converts hostnames to ip and
+** vice versa.
+**
+** A dynamicly loadable file should be compiled sharable
+** (something like: gcc -shared -o udf_example.so myfunc.cc).
+** You can easily get all switches right by doing:
+** cd sql ; make udf_example.o
+** Take the compile line that make writes, remove the '-c' near the end of
+** the line and add -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 ?)
+**
+** After the library is made one must notify mysqld about the new
+** functions with the commands:
+**
+** CREATE FUNCTION metaphon RETURNS STRING SONAME "udf_example.so";
+** CREATE FUNCTION myfunc_double RETURNS REAL SONAME "udf_example.so";
+** CREATE FUNCTION myfunc_int RETURNS INTEGER SONAME "udf_example.so";
+** CREATE FUNCTION lookup RETURNS STRING SONAME "udf_example.so";
+** CREATE FUNCTION reverse_lookup RETURNS STRING SONAME "udf_example.so";
+** CREATE AGGREGATE FUNCTION avgcost RETURNS REAL SONAME "udf_example.so";
+**
+** After this the functions will work exactly like native MySQL functions.
+** Functions should be created only once.
+**
+** The functions can be deleted by:
+**
+** DROP FUNCTION metaphon;
+** DROP FUNCTION myfunc_double;
+** DROP FUNCTION myfunc_int;
+** DROP FUNCTION lookup;
+** DROP FUNCTION reverse_lookup;
+** DROP FUNCTION avgcost;
+**
+** The CREATE FUNCTION and DROP FUNCTION update the func@mysql table. All
+** Active function will be reloaded on every restart of server
+** (if --skip-grant-tables is not given)
+**
+*/
+
+#ifdef STANDARD
+#include <stdio.h>
+#include <string.h>
+#else
+#include <global.h>
+#include <my_sys.h>
+#endif
+#include <mysql.h>
+#include <m_ctype.h>
+#include <m_string.h> // To get strmov()
+
+#ifdef HAVE_DLOPEN
+
+/* These must be right or mysqld will not find the symbol! */
+
+extern "C" {
+my_bool metaphon_init(UDF_INIT *initid, UDF_ARGS *args, char *message);
+void metaphon_deinit(UDF_INIT *initid);
+char *metaphon(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *length, char *is_null, char *error);
+my_bool myfunc_double_init(UDF_INIT *, UDF_ARGS *args, char *message);
+double myfunc_double(UDF_INIT *initid, UDF_ARGS *args, char *is_null,
+ char *error);
+longlong myfunc_int(UDF_INIT *initid, UDF_ARGS *args, char *is_null,
+ char *error);
+}
+
+
+/*************************************************************************
+** Example of init function
+** Arguments:
+** initid Points to a structure that the init function should fill.
+** This argument is given to all other functions.
+** my_bool maybe_null 1 if function can return NULL
+** Default value is 1 if any of the arguments
+** is declared maybe_null.
+** unsigned int decimals Number of decimals.
+** Default value is max decimals in any of the
+** arguments.
+** unsigned int max_length Length of string result.
+** The default value for integer functions is 21
+** The default value for real functions is 13+
+** default number of decimals.
+** The default value for string functions is
+** the longest string argument.
+** char *ptr; A pointer that the function can use.
+**
+** args Points to a structure which contains:
+** unsigned int arg_count Number of arguments
+** enum Item_result *arg_type Types for each argument.
+** Types are STRING_RESULT, REAL_RESULT
+** and INT_RESULT.
+** char **args Pointer to constant arguments.
+** Contains 0 for not constant argument.
+** unsigned long *lengths; max string length for each argument
+** char *maybe_null Information of which arguments
+** may be NULL
+**
+** message Error message that should be passed to the user on fail.
+** The message buffer is MYSQL_ERRMSG_SIZE big, but one should
+** try to keep the error message less than 80 bytes long!
+**
+** This function should return 1 if something goes wrong. In this case
+** message should contain something usefull!
+**************************************************************************/
+
+#define MAXMETAPH 8
+
+my_bool metaphon_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ if (args->arg_count != 1 || args->arg_type[0] != STRING_RESULT)
+ {
+ strcpy(message,"Wrong arguments to metaphon; Use the source");
+ return 1;
+ }
+ initid->max_length=MAXMETAPH;
+ return 0;
+}
+
+/****************************************************************************
+** Deinit function. This should free all resources allocated by
+** this function.
+** Arguments:
+** initid Return value from xxxx_init
+****************************************************************************/
+
+
+void metaphon_deinit(UDF_INIT *initid)
+{
+}
+
+/***************************************************************************
+** UDF string function.
+** Arguments:
+** initid Structure filled by xxx_init
+** args The same structure as to xxx_init. This structure
+** contains values for all parameters.
+** Note that the functions MUST check and convert all
+** to the type it wants! Null values are represented by
+** a NULL pointer
+** result Possible buffer to save result. At least 255 byte long.
+** length Pointer to length of the above buffer. In this the function
+** should save the result length
+** is_null If the result is null, one should store 1 here.
+** error If something goes fatally wrong one should store 1 here.
+**
+** This function should return a pointer to the result string.
+** Normally this is 'result' but may also be an alloced string.
+***************************************************************************/
+
+/* Character coding array */
+static char codes[26] = {
+ 1,16,4,16,9,2,4,16,9,2,0,2,2,2,1,4,0,2,4,4,1,0,0,0,8,0
+ /* A B C D E F G H I J K L M N O P Q R S T U V W X Y Z*/
+ };
+
+/*--- Macros to access character coding array -------------*/
+
+#define ISVOWEL(x) (codes[(x) - 'A'] & 1) /* AEIOU */
+
+ /* Following letters are not changed */
+#define NOCHANGE(x) (codes[(x) - 'A'] & 2) /* FJLMNR */
+
+ /* These form diphthongs when preceding H */
+#define AFFECTH(x) (codes[(x) - 'A'] & 4) /* CGPST */
+
+ /* These make C and G soft */
+#define MAKESOFT(x) (codes[(x) - 'A'] & 8) /* EIY */
+
+ /* These prevent GH from becoming F */
+#define NOGHTOF(x) (codes[(x) - 'A'] & 16) /* BDH */
+
+
+char *metaphon(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *length, char *is_null, char *error)
+{
+ const char *word=args->args[0];
+ if (!word) // Null argument
+ {
+ *is_null=1;
+ return 0;
+ }
+ const char *w_end=word+args->lengths[0];
+ char *org_result=result;
+
+ char *n, *n_start, *n_end; /* pointers to string */
+ char *metaph, *metaph_end; /* pointers to metaph */
+ char ntrans[32]; /* word with uppercase letters */
+ char newm[8]; /* new metaph for comparison */
+ int KSflag; /* state flag for X to KS */
+
+ /*--------------------------------------------------------
+ * Copy word to internal buffer, dropping non-alphabetic
+ * characters and converting to uppercase.
+ *-------------------------------------------------------*/
+
+ for ( n = ntrans + 1, n_end = ntrans + sizeof(ntrans)-2;
+ word != w_end && n < n_end; word++ )
+ if ( isalpha ( *word ))
+ *n++ = toupper ( *word );
+
+ if ( n == ntrans + 1 ) /* return empty string if 0 bytes */
+ {
+ *length=0;
+ return result;
+ }
+ n_end = n; /* set n_end to end of string */
+ ntrans[0] = 'Z'; /* ntrans[0] should be a neutral char */
+ n[0]=n[1]=0; /* pad with nulls */
+ n = ntrans + 1; /* assign pointer to start */
+
+ /*------------------------------------------------------------
+ * check for all prefixes:
+ * PN KN GN AE WR WH and X at start.
+ *----------------------------------------------------------*/
+
+ switch ( *n ) {
+ case 'P':
+ case 'K':
+ case 'G':
+ if ( n[1] == 'N')
+ *n++ = 0;
+ break;
+ case 'A':
+ if ( n[1] == 'E')
+ *n++ = 0;
+ break;
+ case 'W':
+ if ( n[1] == 'R' )
+ *n++ = 0;
+ else
+ if ( *(n + 1) == 'H')
+ {
+ n[1] = *n;
+ *n++ = 0;
+ }
+ break;
+ case 'X':
+ *n = 'S';
+ break;
+ }
+
+ /*------------------------------------------------------------
+ * Now, loop step through string, stopping at end of string
+ * or when the computed metaph is MAXMETAPH characters long
+ *----------------------------------------------------------*/
+
+ KSflag = 0; /* state flag for KS translation */
+
+ for ( metaph_end = result + MAXMETAPH, n_start = n;
+ n <= n_end && result < metaph_end; n++ )
+ {
+
+ if ( KSflag )
+ {
+ KSflag = 0;
+ *result++ = *n;
+ }
+ else
+ {
+ /* drop duplicates except for CC */
+ if ( *( n - 1 ) == *n && *n != 'C' )
+ continue;
+
+ /* check for F J L M N R or first letter vowel */
+ if ( NOCHANGE ( *n ) ||
+ ( n == n_start && ISVOWEL ( *n )))
+ *result++ = *n;
+ else
+ switch ( *n ) {
+ case 'B': /* check for -MB */
+ if ( n < n_end || *( n - 1 ) != 'M' )
+ *result++ = *n;
+ break;
+
+ case 'C': /* C = X ("sh" sound) in CH and CIA */
+ /* = S in CE CI and CY */
+ /* dropped in SCI SCE SCY */
+ /* else K */
+ if ( *( n - 1 ) != 'S' ||
+ !MAKESOFT ( n[1]))
+ {
+ if ( n[1] == 'I' && n[2] == 'A' )
+ *result++ = 'X';
+ else
+ if ( MAKESOFT ( n[1]))
+ *result++ = 'S';
+ else
+ if ( n[1] == 'H' )
+ *result++ = (( n == n_start &&
+ !ISVOWEL ( n[2])) ||
+ *( n - 1 ) == 'S' ) ?
+ (char)'K' : (char)'X';
+ else
+ *result++ = 'K';
+ }
+ break;
+
+ case 'D': /* J before DGE, DGI, DGY, else T */
+ *result++ =
+ ( n[1] == 'G' &&
+ MAKESOFT ( n[2])) ?
+ (char)'J' : (char)'T';
+ break;
+
+ case 'G': /* complicated, see table in text */
+ if (( n[1] != 'H' || ISVOWEL ( n[2]))
+ && (
+ n[1] != 'N' ||
+ (
+ (n + 1) < n_end &&
+ (
+ n[2] != 'E' ||
+ *( n + 3 ) != 'D'
+ )
+ )
+ )
+ && (
+ *( n - 1 ) != 'D' ||
+ !MAKESOFT ( n[1])
+ )
+ )
+ *result++ =
+ ( MAKESOFT ( *( n + 1 )) &&
+ n[2] != 'G' ) ?
+ (char)'J' : (char)'K';
+ else
+ if( n[1] == 'H' &&
+ !NOGHTOF( *( n - 3 )) &&
+ *( n - 4 ) != 'H')
+ *result++ = 'F';
+ break;
+
+ case 'H': /* H if before a vowel and not after */
+ /* C, G, P, S, T */
+
+ if ( !AFFECTH ( *( n - 1 )) &&
+ ( !ISVOWEL ( *( n - 1 )) ||
+ ISVOWEL ( n[1])))
+ *result++ = 'H';
+ break;
+
+ case 'K': /* K = K, except dropped after C */
+ if ( *( n - 1 ) != 'C')
+ *result++ = 'K';
+ break;
+
+ case 'P': /* PH = F, else P = P */
+ *result++ = *( n + 1 ) == 'H'
+ ? (char)'F' : (char)'P';
+ break;
+ case 'Q': /* Q = K (U after Q is already gone */
+ *result++ = 'K';
+ break;
+
+ case 'S': /* SH, SIO, SIA = X ("sh" sound) */
+ *result++ = ( n[1] == 'H' ||
+ ( *(n + 1) == 'I' &&
+ ( n[2] == 'O' ||
+ n[2] == 'A'))) ?
+ (char)'X' : (char)'S';
+ break;
+
+ case 'T': /* TIO, TIA = X ("sh" sound) */
+ /* TH = 0, ("th" sound ) */
+ if( *( n + 1 ) == 'I' && ( n[2] == 'O'
+ || n[2] == 'A') )
+ *result++ = 'X';
+ else
+ if ( n[1] == 'H' )
+ *result++ = '0';
+ else
+ if ( *( n + 1) != 'C' || n[2] != 'H')
+ *result++ = 'T';
+ break;
+
+ case 'V': /* V = F */
+ *result++ = 'F';
+ break;
+
+ case 'W': /* only exist if a vowel follows */
+ case 'Y':
+ if ( ISVOWEL ( n[1]))
+ *result++ = *n;
+ break;
+
+ case 'X': /* X = KS, except at start */
+ if ( n == n_start )
+ *result++ = 'S';
+ else
+ {
+ *result++ = 'K'; /* insert K, then S */
+ KSflag = 1; /* this flag will cause S to be
+ inserted on next pass thru loop */
+ }
+ break;
+
+ case 'Z':
+ *result++ = 'S';
+ break;
+ }
+ }
+ }
+ *length= (ulong) (result - org_result);
+ return org_result;
+}
+
+
+/***************************************************************************
+** UDF double function.
+** Arguments:
+** initid Structure filled by xxx_init
+** args The same structure as to xxx_init. This structure
+** contains values for all parameters.
+** Note that the functions MUST check and convert all
+** to the type it wants! Null values are represented by
+** a NULL pointer
+** is_null If the result is null, one should store 1 here.
+** error If something goes fatally wrong one should store 1 here.
+**
+** This function should return the result.
+***************************************************************************/
+
+my_bool myfunc_double_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ if (!args->arg_count)
+ {
+ strcpy(message,"myfunc_double must have at least on argument");
+ return 1;
+ }
+ /*
+ ** As this function wants to have everything as strings, force all arguments
+ ** to strings.
+ */
+ for (uint i=0 ; i < args->arg_count; i++)
+ args->arg_type[i]=STRING_RESULT;
+ initid->maybe_null=1; // The result may be null
+ initid->decimals=2; // We want 2 decimals in the result
+ initid->max_length=6; // 3 digits + . + 2 decimals
+ return 0;
+}
+
+
+double myfunc_double(UDF_INIT *initid, UDF_ARGS *args, char *is_null,
+ char *error)
+{
+ unsigned long val = 0;
+ unsigned long v = 0;
+
+ for (uint i = 0; i < args->arg_count; i++)
+ {
+ if (args->args[i] == NULL)
+ continue;
+ val += args->lengths[i];
+ for (uint j=args->lengths[i] ; j-- > 0 ;)
+ v += args->args[i][j];
+ }
+ if (val)
+ return (double) v/ (double) val;
+ *is_null=1;
+ return 0.0;
+}
+
+
+/***************************************************************************
+** UDF long long function.
+** Arguments:
+** initid Return value from xxxx_init
+** args The same structure as to xxx_init. This structure
+** contains values for all parameters.
+** Note that the functions MUST check and convert all
+** to the type it wants! Null values are represented by
+** a NULL pointer
+** is_null If the result is null, one should store 1 here.
+** error If something goes fatally wrong one should store 1 here.
+**
+** This function should return the result as a long long
+***************************************************************************/
+
+long long myfunc_int(UDF_INIT *initid, UDF_ARGS *args, char *is_null,
+ char *error)
+{
+ long long val = 0;
+ for (uint i = 0; i < args->arg_count; i++)
+ {
+ if (args->args[i] == NULL)
+ continue;
+ switch (args->arg_type[i]) {
+ case STRING_RESULT: // Add string lengths
+ val += args->lengths[i];
+ break;
+ case INT_RESULT: // Add numbers
+ val += *((long long*) args->args[i]);
+ break;
+ case REAL_RESULT: // Add numers as long long
+ val += (long long) *((double*) args->args[i]);
+ break;
+ }
+ }
+ return val;
+}
+
+
+/****************************************************************************
+** Some functions that handles IP and hostname conversions
+** The orignal function was from Zeev Suraski.
+**
+** CREATE FUNCTION lookup RETURNS STRING SONAME "udf_example.so";
+** CREATE FUNCTION reverse_lookup RETURNS STRING SONAME "udf_example.so";
+**
+****************************************************************************/
+
+#if defined(HAVE_GETHOSTBYADDR_R) && defined(HAVE_SOLARIS_STYLE_GETHOST)
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+extern "C" {
+my_bool lookup_init(UDF_INIT *initid, UDF_ARGS *args, char *message);
+char *lookup(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *length, char *null_value, char *error);
+my_bool reverse_lookup_init(UDF_INIT *initid, UDF_ARGS *args, char *message);
+char *reverse_lookup(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *length, char *null_value, char *error);
+}
+
+
+/****************************************************************************
+** lookup IP for an hostname.
+**
+** This code assumes that gethostbyname_r exists and inet_ntoa() is thread
+** safe (As it is in Solaris)
+****************************************************************************/
+
+
+my_bool lookup_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ if (args->arg_count != 1 || args->arg_type[0] != STRING_RESULT)
+ {
+ strmov(message,"Wrong arguments to lookup; Use the source");
+ return 1;
+ }
+ initid->max_length=11;
+ initid->maybe_null=1;
+ return 0;
+}
+
+char *lookup(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *null_value, char *error)
+{
+ uint length;
+ int tmp_errno;
+ char name_buff[256],hostname_buff[2048];
+ struct hostent tmp_hostent,*hostent;
+
+ if (!args->args[0] || !(length=args->lengths[0]))
+ {
+ *null_value=1;
+ return 0;
+ }
+ if (length >= sizeof(name_buff))
+ length=sizeof(name_buff)-1;
+ memcpy(name_buff,args->args[0],length);
+ name_buff[length]=0;
+
+ if (!(hostent=gethostbyname_r(name_buff,&tmp_hostent,hostname_buff,
+ sizeof(hostname_buff), &tmp_errno)))
+ {
+ *null_value=1;
+ return 0;
+ }
+ struct in_addr in;
+ memcpy_fixed((char*) &in,(char*) *hostent->h_addr_list, sizeof(in.s_addr));
+ *res_length= (ulong) (strmov(result, inet_ntoa(in)) - result);
+ return result;
+}
+
+
+/****************************************************************************
+** return hostname for an IP number.
+** The functions can take as arguments a string "xxx.xxx.xxx.xxx" or
+** four numbers.
+****************************************************************************/
+
+my_bool reverse_lookup_init(UDF_INIT *initid, UDF_ARGS *args, char *message)
+{
+ if (args->arg_count == 1)
+ args->arg_type[0]= STRING_RESULT;
+ else if (args->arg_count == 4)
+ args->arg_type[0]=args->arg_type[1]=args->arg_type[2]=args->arg_type[3]=
+ INT_RESULT;
+ else
+ {
+ strmov(message,
+ "Wrong number of arguments to reverse_lookup; Use the source");
+ return 1;
+ }
+ initid->max_length=32;
+ initid->maybe_null=1;
+ return 0;
+}
+
+
+char *reverse_lookup(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *res_length, char *null_value, char *error)
+{
+ char name_buff[256];
+ struct hostent tmp_hostent;
+ uint length;
+
+ if (args->arg_count == 4)
+ {
+ if (!args->args[0] || !args->args[1] ||!args->args[2] ||!args->args[3])
+ {
+ *null_value=1;
+ 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]));
+ }
+ else
+ { // string argument
+ if (!args->args[0]) // Return NULL for NULL values
+ {
+ *null_value=1;
+ return 0;
+ }
+ length=args->lengths[0];
+ if (length >= (uint) *res_length-1)
+ length=(uint) *res_length;
+ memcpy(result,args->args[0],length);
+ result[length]=0;
+ }
+
+ unsigned long taddr = inet_addr(result);
+ if (taddr == (unsigned long) -1L)
+ {
+ *null_value=1;
+ return 0;
+ }
+ struct hostent *hp;
+ int tmp_errno;
+ if (!(hp=gethostbyaddr_r((char*) &taddr,sizeof(taddr), AF_INET,
+ &tmp_hostent, name_buff,sizeof(name_buff),
+ &tmp_errno)))
+ {
+ *null_value=1;
+ return 0;
+ }
+ *res_length=(ulong) (strmov(result,hp->h_name) - result);
+ return result;
+}
+
+/*
+** Syntax for the new aggregate commands are:
+** create aggregate function <function_name> returns {string|real|integer}
+** soname <name_of_shared_library>
+**
+** Syntax for avgcost: avgcost( t.quantity, t.price )
+** with t.quantity=integer, t.price=double
+** (this example is provided by Andreas F. Bobak <bobak@relog.ch>)
+*/
+
+extern "C" {
+my_bool avgcost_init( UDF_INIT* initid, UDF_ARGS* args, char* message );
+void avgcost_deinit( UDF_INIT* initid );
+void avgcost_reset( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char *error );
+void avgcost_add( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char *error );
+double avgcost( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char *error );
+}
+
+struct avgcost_data
+{
+ unsigned long long count;
+ long long totalquantity;
+ double totalprice;
+};
+
+
+/*
+** Average Cost Aggregate Function.
+*/
+my_bool
+avgcost_init( UDF_INIT* initid, UDF_ARGS* args, char* message )
+{
+ struct avgcost_data* data;
+
+ if (args->arg_count != 2)
+ {
+ strcpy(
+ message,
+ "wrong number of arguments: AVGCOST() requires two arguments"
+ );
+ return 1;
+ }
+
+ if ((args->arg_type[0] != INT_RESULT) && (args->arg_type[1] != REAL_RESULT) )
+ {
+ strcpy(
+ message,
+ "wrong argument type: AVGCOST() requires an INT and a REAL"
+ );
+ return 1;
+ }
+
+ /*
+ ** force arguments to double.
+ */
+ /*args->arg_type[0] = REAL_RESULT;
+ args->arg_type[1] = REAL_RESULT;*/
+
+ initid->maybe_null = 0; // The result may be null
+ initid->decimals = 4; // We want 4 decimals in the result
+ initid->max_length = 20; // 6 digits + . + 10 decimals
+
+ data = new struct avgcost_data;
+ data->totalquantity = 0;
+ data->totalprice = 0.0;
+
+ initid->ptr = (char*)data;
+
+ return 0;
+}
+
+void
+avgcost_deinit( UDF_INIT* initid )
+{
+ delete initid->ptr;
+}
+
+void
+avgcost_reset( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char* message )
+{
+ struct avgcost_data* data = (struct avgcost_data*)initid->ptr;
+ data->totalprice = 0.0;
+ data->totalquantity = 0;
+ data->count = 0;
+
+ *is_null = 0;
+ avgcost_add( initid, args, is_null, message );
+}
+
+
+void
+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;
+ double price = *((double*)args->args[1]);
+
+ data->count++;
+
+ if ( ((data->totalquantity >= 0) && (quantity < 0))
+ || ((data->totalquantity < 0) && (quantity > 0)) )
+ {
+ /*
+ ** passing from + to - or from - to +
+ */
+ if ( ((quantity < 0) && (newquantity < 0))
+ || ((quantity > 0) && (newquantity > 0)) )
+ {
+ data->totalprice = price * double(newquantity);
+ }
+ /*
+ ** sub q if totalq > 0
+ ** add q if totalq < 0
+ */
+ else
+ {
+ price = data->totalprice / double(data->totalquantity);
+ data->totalprice = price * double(newquantity);
+ }
+ data->totalquantity = newquantity;
+ }
+ else
+ {
+ data->totalquantity += quantity;
+ data->totalprice += price * double(quantity);
+ }
+
+ if (data->totalquantity == 0)
+ data->totalprice = 0.0;
+ }
+}
+
+
+double
+avgcost( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char* error )
+{
+ struct avgcost_data* data = (struct avgcost_data*)initid->ptr;
+ if (!data->count || !data->totalquantity)
+ {
+ *is_null = 1;
+ return 0.0;
+ }
+
+ *is_null = 0;
+ return data->totalprice/double(data->totalquantity);
+}
+
+#endif // defined(HAVE_GETHOSTBYADDR_R) && defined(HAVE_SOLARIS_STYLE_GETHOST)
+#endif /* HAVE_DLOPEN */
diff --git a/sql/unireg.cc b/sql/unireg.cc
new file mode 100644
index 00000000000..611a4f04ec9
--- /dev/null
+++ b/sql/unireg.cc
@@ -0,0 +1,581 @@
+/* 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 */
+
+
+/*
+ Functions to create a unireg form-file from a FIELD and a fieldname-fieldinfo
+ struct.
+ In the following functions FIELD * is a ordinary field-structure with
+ the following exeptions:
+ sc_length,typepos,row,kol,dtype,regnr and field nead not to be set.
+ str is a (long) to record position where 0 is the first position.
+*/
+
+#define USES_TYPES
+#include "mysql_priv.h"
+#include <m_ctype.h>
+
+#define FCOMP 11 /* Byte per packat f{lt */
+
+static uchar * pack_screens(List<create_field> &create_fields,
+ uint *info_length, uint *screens, bool small_file);
+static uint pack_keys(uchar *keybuff,uint key_count, KEY *key_info);
+static bool pack_header(uchar *forminfo, enum db_type table_type,
+ List<create_field> &create_fields,
+ uint info_length, uint screens, uint table_options,
+ handler *file);
+static uint get_interval_id(uint *int_count,List<create_field> &create_fields,
+ create_field *last_field);
+static bool pack_fields(File file, List<create_field> &create_fields);
+static bool make_empty_rec(int file, enum db_type table_type,
+ uint table_options,
+ List<create_field> &create_fields,
+ uint reclength,uint null_fields);
+
+
+int rea_create_table(my_string file_name,
+ HA_CREATE_INFO *create_info,
+ List<create_field> &create_fields,
+ uint keys, KEY *key_info)
+{
+ uint reclength,info_length,screens,key_info_length,maxlength,null_fields;
+ File file;
+ ulong filepos;
+ uchar fileinfo[64],forminfo[288],*keybuff;
+ TYPELIB formnames;
+ uchar *screen_buff;
+ handler *db_file;
+ DBUG_ENTER("rea_create_table");
+
+ formnames.type_names=0;
+ if (!(screen_buff=pack_screens(create_fields,&info_length,&screens,0)))
+ DBUG_RETURN(1);
+ db_file=get_new_handler((TABLE*) 0, create_info->db_type);
+ if (pack_header(forminfo, create_info->db_type,create_fields,info_length,
+ screens, create_info->table_options, db_file))
+ {
+ NET *net=my_pthread_getspecific_ptr(NET*,THR_NET);
+ my_free((gptr) screen_buff,MYF(0));
+ if (net->last_errno != ER_TOO_MANY_FIELDS)
+ DBUG_RETURN(1);
+
+ // Try again without UNIREG screens (to get more columns)
+ net->last_error[0]=0;
+ if (!(screen_buff=pack_screens(create_fields,&info_length,&screens,1)))
+ DBUG_RETURN(1);
+ if (pack_header(forminfo, create_info->db_type, create_fields,info_length,
+ screens, create_info->table_options, db_file))
+ {
+ my_free((gptr) screen_buff,MYF(0));
+ DBUG_RETURN(1);
+ }
+ }
+ reclength=uint2korr(forminfo+266);
+ null_fields=uint2korr(forminfo+282);
+
+ if ((file=create_frm(file_name, reclength, fileinfo,
+ create_info, keys)) < 0)
+ {
+ my_free((gptr) screen_buff,MYF(0));
+ DBUG_RETURN(1);
+ }
+
+ uint key_buff_length=uint2korr(fileinfo+14);
+ keybuff=(uchar*) my_alloca(key_buff_length);
+ key_info_length=pack_keys(keybuff,keys,key_info);
+ VOID(get_form_pos(file,fileinfo,&formnames));
+ if (!(filepos=make_new_entry(file,fileinfo,&formnames,"")))
+ goto err;
+ maxlength=(uint) next_io_size((ulong) (uint2korr(forminfo)+1000));
+ int2store(forminfo+2,maxlength);
+ int4store(fileinfo+10,(ulong) (filepos+maxlength));
+ 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 : "",
+ 60);
+ forminfo[46]=(uchar) strlen((char*)forminfo+47); // Length of comment
+
+ if (my_pwrite(file,(byte*) fileinfo,64,0L,MYF_RW) ||
+ my_pwrite(file,(byte*) keybuff,key_info_length,
+ (ulong) uint2korr(fileinfo+6),MYF_RW))
+ goto err;
+ VOID(my_seek(file,
+ (ulong) uint2korr(fileinfo+6)+ (ulong) key_buff_length,
+ MY_SEEK_SET,MYF(0)));
+ if (make_empty_rec(file,create_info->db_type,create_info->table_options,
+ create_fields,reclength,null_fields))
+ goto err;
+
+ VOID(my_seek(file,filepos,MY_SEEK_SET,MYF(0)));
+ if (my_write(file,(byte*) forminfo,288,MYF_RW) ||
+ my_write(file,(byte*) screen_buff,info_length,MYF_RW) ||
+ pack_fields(file,create_fields))
+ goto err;
+
+#ifdef HAVE_CRYPTED_FRM
+ if (create_info->password)
+ {
+ char tmp=2,*disk_buff=0;
+ SQL_CRYPT *crypted=new SQL_CRYPT(create_info->password);
+ if (!crypted || my_pwrite(file,&tmp,1,26,MYF_RW)) // Mark crypted
+ goto err;
+ uint read_length=uint2korr(forminfo)-256;
+ VOID(my_seek(file,filepos+256,MY_SEEK_SET,MYF(0)));
+ if (read_string(file,(gptr*) &disk_buff,read_length))
+ goto err;
+ crypted->encode(disk_buff,read_length);
+ delete crypted;
+ if (my_pwrite(file,disk_buff,read_length,filepos+256,MYF_RW))
+ {
+ my_free(disk_buff,MYF(0));
+ goto err;
+ }
+ my_free(disk_buff,MYF(0));
+ }
+#endif
+
+ my_free((gptr) screen_buff,MYF(0));
+ my_afree((gptr) keybuff);
+ VOID(my_close(file,MYF(MY_WME)));
+ if (ha_create_table(file_name,create_info,0))
+ goto err2;
+ DBUG_RETURN(0);
+
+err:
+ my_free((gptr) screen_buff,MYF(0));
+ my_afree((gptr) keybuff);
+ VOID(my_close(file,MYF(MY_WME)));
+ err2:
+ my_delete(file_name,MYF(0));
+ DBUG_RETURN(1);
+} /* rea_create_table */
+
+
+ /* Pack screens to a screen for save in a form-file */
+
+static uchar * pack_screens(List<create_field> &create_fields,
+ uint *info_length, uint *screens,
+ bool small_file)
+{
+ reg1 uint i;
+ uint row,start_row,end_row,fields_on_screen;
+ uint length,cols;
+ uchar *info,*pos,*start_screen;
+ uint fields=create_fields.elements;
+ List_iterator<create_field> it(create_fields);
+ DBUG_ENTER("pack_screens");
+
+ start_row=4; end_row=22; cols=80; fields_on_screen=end_row+1-start_row;
+
+ *screens=(fields-1)/fields_on_screen+1;
+ length= (*screens) * (SC_INFO_LENGTH+ (cols>> 1)+4);
+
+ create_field *field;
+ while ((field=it++))
+ length+=strlen(field->field_name)+1+TE_INFO_LENGTH+cols/2;
+
+ if (!(info=(uchar*) my_malloc(length,MYF(MY_WME))))
+ DBUG_RETURN(0);
+
+ start_screen=0;
+ row=end_row;
+ pos=info;
+ it.rewind();
+ for (i=0 ; i < fields ; i++)
+ {
+ create_field *cfield=it++;
+ if (row++ == end_row)
+ {
+ if (i)
+ {
+ length=(uint) (pos-start_screen);
+ int2store(start_screen,length);
+ start_screen[2]=(uchar) (fields_on_screen+1);
+ start_screen[3]=(uchar) (fields_on_screen);
+ }
+ row=start_row;
+ start_screen=pos;
+ pos+=4;
+ pos[0]= (uchar) start_row-2; /* Header string */
+ pos[1]= (uchar) (cols >> 2);
+ pos[2]= (uchar) (cols >> 1) +1;
+ strfill((my_string) pos+3,(uint) (cols >> 1),' ');
+ pos+=(cols >> 1)+4;
+ }
+ length=strlen(cfield->field_name);
+ if (length > cols-3)
+ length=cols-3;
+
+ if (!small_file)
+ {
+ pos[0]=(uchar) row;
+ pos[1]=0;
+ pos[2]=(uchar) (length+1);
+ pos=(uchar*) strmake((char*) pos+3,cfield->field_name,length)+1;
+ }
+ cfield->row=(uint8) row;
+ cfield->col=(uint8) (length+1);
+ cfield->sc_length=(uint8) min(cfield->length,cols-(length+2));
+ }
+ length=(uint) (pos-start_screen);
+ int2store(start_screen,length);
+ start_screen[2]=(uchar) (row-start_row+2);
+ start_screen[3]=(uchar) (row-start_row+1);
+
+ *info_length=(uint) (pos-info);
+ DBUG_RETURN(info);
+} /* pack_screens */
+
+
+ /* Pack keyinfo and keynames to keybuff for save in form-file. */
+
+static uint pack_keys(uchar *keybuff,uint key_count,KEY *keyinfo)
+{
+ uint key_parts,length;
+ uchar *pos,*keyname_pos;
+ KEY *key,*end;
+ KEY_PART_INFO *key_part,*key_part_end;
+ DBUG_ENTER("pack_keys");
+
+ pos=keybuff+6;
+ key_parts=0;
+ for (key=keyinfo,end=keyinfo+key_count ; key != end ; key++)
+ {
+ pos[0]=(uchar) (key->flags ^ HA_NOSAME);
+ int2store(pos+1,key->key_length);
+ pos[3]=key->key_parts;
+ pos+=4;
+ key_parts+=key->key_parts;
+ DBUG_PRINT("loop",("flags: %d key_parts: %d at %lx",
+ key->flags,key->key_parts,
+ key->key_part));
+ for (key_part=key->key_part,key_part_end=key_part+key->key_parts ;
+ key_part != key_part_end ;
+ key_part++)
+
+ {
+ DBUG_PRINT("loop",("field: %d startpos: %ld length: %ld",
+ key_part->fieldnr,key_part->offset,key_part->length));
+ int2store(pos,key_part->fieldnr+1+FIELD_NAME_USED);
+ int2store(pos+2,key_part->offset+1);
+ pos[4]=0; // Sort order
+ int2store(pos+5,key_part->key_type);
+ int2store(pos+7,key_part->length);
+ pos+=9;
+ }
+ }
+ /* Save keynames */
+ keyname_pos=pos;
+ *pos++=NAMES_SEP_CHAR;
+ for (key=keyinfo ; key != end ; key++)
+ {
+ uchar *tmp=(uchar*) strmov((char*) pos,key->name);
+ *tmp++=NAMES_SEP_CHAR;
+ *tmp=0;
+ pos=tmp;
+ }
+ *(pos++)=0;
+
+ keybuff[0]=(uchar) key_count;
+ keybuff[1]=(uchar) key_parts;
+ length=(uint) (keyname_pos-keybuff);
+ int2store(keybuff+2,length);
+ length=(uint) (pos-keyname_pos);
+ int2store(keybuff+4,length);
+ DBUG_RETURN((uint) (pos-keybuff));
+} /* pack_keys */
+
+
+ /* Make formheader */
+
+static bool pack_header(uchar *forminfo, enum db_type table_type,
+ List<create_field> &create_fields,
+ uint info_length, uint screens,uint table_options,
+ handler *file)
+{
+ uint length,int_count,int_length,no_empty, int_parts,
+ time_stamp_pos,null_fields;
+ ulong reclength,totlength,n_length;
+ DBUG_ENTER("pack_header");
+
+ if (create_fields.elements > MAX_FIELDS)
+ {
+ my_error(ER_TOO_MANY_FIELDS,MYF(0));
+ DBUG_RETURN(1);
+ }
+
+ totlength=reclength=0L;
+ no_empty=int_count=int_parts=int_length=time_stamp_pos=null_fields=0;
+ n_length=2L;
+
+ /* Check fields */
+
+ List_iterator<create_field> it(create_fields);
+ create_field *field;
+ while ((field=it++))
+ {
+ totlength+= field->length;
+ if (MTYP_TYPENR(field->unireg_check) == Field::NOEMPTY ||
+ field->unireg_check & MTYP_NOEMPTY_BIT)
+ {
+ field->unireg_check= (Field::utype) ((uint) field->unireg_check |
+ MTYP_NOEMPTY_BIT);
+ no_empty++;
+ }
+ if ((MTYP_TYPENR(field->unireg_check) == Field::TIMESTAMP_FIELD ||
+ f_packtype(field->pack_flag) == (int) FIELD_TYPE_TIMESTAMP) &&
+ !time_stamp_pos)
+ time_stamp_pos=(int) field->offset+1;
+ length=field->pack_length;
+ if ((int) field->offset+length > reclength)
+ reclength=(int) field->offset+length;
+ n_length+= (ulong) strlen(field->field_name)+1;
+ field->interval_id=0;
+ if (field->interval)
+ {
+ uint old_int_count=int_count;
+ field->interval_id=get_interval_id(&int_count,create_fields,field);
+ if (old_int_count != int_count)
+ {
+ for (const char **pos=field->interval->type_names ; *pos ; pos++)
+ int_length+=strlen(*pos)+1; // field + suffix prefix
+ int_parts+=field->interval->count+1;
+ }
+ }
+ if (f_maybe_null(field->pack_flag))
+ null_fields++;
+ }
+ int_length+=int_count*2; // 255 prefix + 0 suffix
+
+ /* Save values in forminfo */
+
+ if (reclength > (ulong) file->max_record_length())
+ {
+ my_error(ER_TOO_BIG_ROWSIZE, MYF(0), (uint) file->max_record_length());
+ DBUG_RETURN(1);
+ }
+ /* Hack to avoid bugs with small static rows in MySQL */
+ reclength=max(file->min_record_length(table_options),reclength);
+ if (info_length+(ulong) create_fields.elements*FCOMP+288+
+ n_length+int_length > 65535L || int_count > 255)
+ {
+ my_error(ER_TOO_MANY_FIELDS,MYF(0));
+ DBUG_RETURN(1);
+ }
+
+ bzero((char*)forminfo,288);
+ length=info_length+create_fields.elements*FCOMP+288+n_length+int_length;
+ int2store(forminfo,length);
+ forminfo[256] = (uint8) screens;
+ int2store(forminfo+258,create_fields.elements);
+ int2store(forminfo+260,info_length);
+ int2store(forminfo+262,totlength);
+ int2store(forminfo+264,no_empty);
+ int2store(forminfo+266,reclength);
+ int2store(forminfo+268,n_length);
+ int2store(forminfo+270,int_count);
+ 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+282,null_fields);
+ DBUG_RETURN(0);
+} /* pack_header */
+
+
+ /* get each unique interval each own id */
+
+static uint get_interval_id(uint *int_count,List<create_field> &create_fields,
+ create_field *last_field)
+{
+ List_iterator<create_field> it(create_fields);
+ create_field *field;
+ TYPELIB *interval=last_field->interval;
+
+ while ((field=it++) != last_field)
+ {
+ if (field->interval_id && field->interval->count == interval->count)
+ {
+ const char **a,**b;
+ for (a=field->interval->type_names, b=interval->type_names ;
+ *a && !strcmp(*a,*b);
+ a++,b++) ;
+
+ if (! *a)
+ {
+ return field->interval_id; // Re-use last interval
+ }
+ }
+ }
+ return ++*int_count; // New unique interval
+}
+
+
+ /* Save fields, fieldnames and intervals */
+
+static bool pack_fields(File file,List<create_field> &create_fields)
+{
+ reg2 uint i;
+ uint int_count;
+ uchar buff[MAX_FIELD_WIDTH];
+ create_field *field;
+ DBUG_ENTER("pack_fields");
+
+ /* Write field info */
+
+ List_iterator<create_field> it(create_fields);
+
+ int_count=0;
+ while ((field=it++))
+ {
+ buff[0]= (uchar) field->row;
+ buff[1]= (uchar) field->col;
+ buff[2]= (uchar) field->sc_length;
+ buff[3]= (uchar) field->length;
+ uint recpos=(uint) field->offset+1;
+ int2store(buff+4,recpos);
+ int2store(buff+6,field->pack_flag);
+ int2store(buff+8,field->unireg_check);
+ buff[10]= (uchar) field->interval_id;
+ set_if_bigger(int_count,field->interval_id);
+ if (my_write(file,(byte*) buff,FCOMP,MYF_RW))
+ DBUG_RETURN(1);
+ }
+
+ /* Write fieldnames */
+ buff[0]=NAMES_SEP_CHAR;
+ if (my_write(file,(byte*) buff,1,MYF_RW))
+ DBUG_RETURN(1);
+ i=0;
+ it.rewind();
+ while ((field=it++))
+ {
+ char *pos= strmov((char*) buff,field->field_name);
+ *pos++=NAMES_SEP_CHAR;
+ if (i == create_fields.elements-1)
+ *pos++=0;
+ if (my_write(file,(byte*) buff,(uint) (pos-(char*) buff),MYF_RW))
+ DBUG_RETURN(1);
+ i++;
+ }
+
+ /* Write intervals */
+ if (int_count)
+ {
+ String tmp((char*) buff,sizeof(buff));
+ tmp.length(0);
+ it.rewind();
+ int_count=0;
+ while ((field=it++))
+ {
+ if (field->interval_id > int_count)
+ {
+ int_count=field->interval_id;
+ tmp.append('\377');
+ for (const char **pos=field->interval->type_names ; *pos ; pos++)
+ {
+ tmp.append(*pos);
+ tmp.append('\377');
+ }
+ tmp.append('\0'); // End of intervall
+ }
+ }
+ if (my_write(file,(byte*) tmp.ptr(),tmp.length(),MYF_RW))
+ DBUG_RETURN(1);
+ }
+ DBUG_RETURN(0);
+}
+
+
+ /* save a empty record on start of formfile */
+
+static bool make_empty_rec(File file,enum db_type table_type,
+ uint table_options,
+ List<create_field> &create_fields,
+ uint reclength, uint null_fields)
+{
+ int error;
+ Field::utype type;
+ uint firstpos,null_count,null_length;
+ uchar *buff,*null_pos;
+ TABLE table;
+ create_field *field;
+ DBUG_ENTER("make_empty_rec");
+
+ /* We need a table to generate columns for default values */
+ bzero((char*) &table,sizeof(table));
+ table.db_low_byte_first=test(table_type == DB_TYPE_MYISAM ||
+ table_type == DB_TYPE_HEAP);
+ table.blob_ptr_size=portable_sizeof_char_ptr;
+
+ if (!(buff=(uchar*) my_malloc((uint) reclength,MYF(MY_WME | MY_ZEROFILL))))
+ DBUG_RETURN(1);
+ firstpos=reclength;
+ null_count=0;
+ if (!(table_options & HA_OPTION_PACK_RECORD))
+ {
+ null_fields++; // Need one bit for delete mark
+ null_count++;
+ }
+ bfill(buff,(null_length=(null_fields+7)/8),255);
+ null_pos=buff;
+
+ List_iterator<create_field> it(create_fields);
+ while ((field=it++))
+ {
+ Field *regfield=make_field((char*) buff+field->offset,field->length,
+ field->flags & NOT_NULL_FLAG ? 0:
+ null_pos+null_count/8,
+ 1 << (null_count & 7),
+ field->pack_flag,
+ field->unireg_check,
+ field->interval,
+ field->field_name,
+ &table);
+ if (!(field->flags & NOT_NULL_FLAG))
+ null_count++;
+
+ if ((uint) field->offset < firstpos &&
+ regfield->type() != FIELD_TYPE_NULL)
+ firstpos= field->offset;
+
+ type= (Field::utype) MTYP_TYPENR(field->unireg_check);
+
+ if (field->def &&
+ (regfield->real_type() != FIELD_TYPE_YEAR ||
+ field->def->val_int() != 0))
+ field->def->save_in_field(regfield);
+ else if (regfield->real_type() == FIELD_TYPE_ENUM &&
+ (field->flags & NOT_NULL_FLAG))
+ {
+ regfield->set_notnull();
+ regfield->store((longlong) 1);
+ }
+ else if (type == Field::YES) // Old unireg type
+ regfield->store(ER(ER_YES),strlen(ER(ER_YES)));
+ else if (type == Field::NO) // Old unireg type
+ regfield->store(ER(ER_NO),strlen(ER(ER_NO)));
+ else
+ regfield->reset();
+ delete regfield;
+ }
+ bfill((byte*) buff+null_length,firstpos-null_length,255);/* Fill not used startpos */
+ error=(int) my_write(file,(byte*) buff,(uint) reclength,MYF_RW);
+ my_free((gptr) buff,MYF(MY_FAE));
+ DBUG_RETURN(error);
+} /* make_empty_rec */
diff --git a/sql/unireg.h b/sql/unireg.h
new file mode 100644
index 00000000000..a0890473de8
--- /dev/null
+++ b/sql/unireg.h
@@ -0,0 +1,136 @@
+/* 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 */
+
+
+/* Extra functions used by unireg library */
+
+#ifndef _unireg_h
+
+#ifndef NO_ALARM_LOOP
+#define NO_ALARM_LOOP /* lib5 and popen can't use alarm */
+#endif
+
+/* These paths are converted to other systems (WIN95) before use */
+
+#define LANGUAGE "english/"
+#define ERRMSG_FILE "errmsg.sys"
+#define TEMP_PREFIX "MY"
+#define PROGDIR "bin/"
+#ifndef DATADIR
+#define DATADIR "data/"
+#endif
+#ifndef SHAREDIR
+#define SHAREDIR "share/"
+#endif
+
+#define ER(X) errmesg[(X)-1000]
+
+#define ERRMAPP 1 /* Errormap f|r my_error */
+#define LIBLEN FN_REFLEN-FN_LEN /* Max l{ngd p} dev */
+#define MAX_FIELD_NAME 34 /* Max colum name length +2 */
+#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 */
+#if SIZEOF_OFF_T > 4
+#define MAX_REFLENGTH 8 /* Max length for record ref */
+#else
+#define MAX_REFLENGTH 4 /* Max length for record ref */
+#endif
+
+#define MAX_FIELD_WIDTH 256 /* Max column width +1 */
+#define MAX_TABLES (sizeof(table_map)*8-1) /* Max tables in join */
+#define RAND_TABLE_BIT (((table_map) 1) << (sizeof(table_map)*8-1))
+#define MAX_FIELDS 4096 /* Limit in the .frm file */
+
+#define MAX_SORT_MEMORY (2048*1024-MALLOC_OVERHEAD)
+#define MIN_SORT_MEMORY (32*1024-MALLOC_OVERHEAD)
+#define EXTRA_RECORDS 10 /* Extra records in sort */
+#define SCROLL_EXTRA 5 /* Extra scroll-rows. */
+#define FIELD_NAME_USED ((uint) 32768) /* Bit set if fieldname used */
+#define FORM_NAME_USED ((uint) 16384) /* Bit set if formname used */
+#define FIELD_NR_MASK 16383 /* To get fieldnumber */
+#define FERR -1 /* Error from my_functions */
+#define CREATE_MODE 0 /* Default mode on new files */
+#define NAMES_SEP_CHAR '\377' /* Char to sep. names */
+#ifdef MSDOS
+#define EXTRA_FIELD_CHAR (char) '\234' /* Interchangebly with '#' */
+#else
+#define EXTRA_FIELD_CHAR '#' /* Interchangebly with '#' */
+#endif
+
+#define READ_RECORD_BUFFER (uint) (IO_SIZE*8) /* Pointer_buffer_size */
+#define DISK_BUFFER_SIZE (uint) (IO_SIZE*16) /* Size of diskbuffer */
+#define POSTFIX_ERROR DBL_MAX
+
+#define ME_INFO (ME_HOLDTANG+ME_OLDWIN+ME_NOREFRESH)
+#define ME_ERROR (ME_BELL+ME_OLDWIN+ME_NOREFRESH)
+#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_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 */
+#define SPECIAL_ENGLISH 32 /* English error messages */
+#define SPECIAL_NO_RESOLVE 64 /* Don't use gethostname */
+#define SPECIAL_NO_PRIOR 128 /* Don't prioritize threads */
+#define SPECIAL_BIG_SELECTS 256 /* Don't use heap tables */
+#define SPECIAL_NO_HOST_CACHE 512 /* Don't cache hosts */
+#define SPECIAL_LONG_LOG_FORMAT 1024
+#define SPECIAL_SAFE_MODE 2048
+#define SPECIAL_SKIP_SHOW_DB 4096 /* Don't allow 'show db' */
+
+ /* Extern defines */
+#define store_record(A,B) bmove_allign((A)->record[B],(A)->record[0],(size_t) (A)->reclength)
+#define restore_record(A,B) bmove_allign((A)->record[0],(A)->record[B],(size_t) (A)->reclength)
+#define cmp_record(A,B) memcmp((A)->record[0],(A)->record[B],(size_t) (A)->reclength)
+#define empty_record(A) { \
+bmove_allign((A)->record[0],(A)->record[2],(size_t) (A)->reclength); \
+bfill((A)->null_flags,(A)->null_bytes,255);\
+}
+
+#if MAX_REFLENGTH == 4
+#define TEST_IF_LASTREF(A,B) ((long) *((int32*) (A)) == -1L)
+#else
+#define TEST_IF_LASTREF(A,B) (bcmp(A,last_ref,B) == 0)
+#endif
+
+ /* Defines for use with openfrm, openprt and openfrd */
+
+#define READ_ALL 1 /* openfrm: Read all parameters */
+#define CHANGE_FRM 2 /* openfrm: open .frm as O_RDWR */
+#define READ_KEYINFO 4 /* L{s nyckeldata fr}n filen */
+#define EXTRA_RECORD 8 /* Reservera plats f|r extra record */
+#define DONT_OPEN_TABLES 8 /* Don't open database-files (frd) */
+#define DONT_OPEN_MASTER_REG 16 /* Don't open first reg-file (prt) */
+#define EXTRA_LONG_RECORD 16 /* Plats f|r dubbel s|k-record */
+#define COMPUTE_TYPES 32 /* Kontrollera type f|r f{ltena */
+#define SEARCH_PRG 64 /* S|k efter registret i 'prg_dev' */
+#define READ_USED_NAMES 128 /* L{s anv{nda formul{rnamn */
+#define DONT_GIVE_ERROR 256 /* Don't do frm_error on openfrm */
+#define READ_SCREENS 1024 /* Read screens, info and helpfile */
+#define DELAYED_OPEN 4096 /* Open table later */
+
+#define SC_INFO_LENGTH 4 /* Form format constant */
+#define TE_INFO_LENGTH 3
+#define MTYP_NOEMPTY_BIT 128
+
+ /* Include prototypes for unireg */
+
+#include "mysqld_error.h"
+#include "structs.h" /* All structs we need */
+
+#endif
diff --git a/sql/violite.c b/sql/violite.c
new file mode 100644
index 00000000000..7a1424b6595
--- /dev/null
+++ b/sql/violite.c
@@ -0,0 +1,399 @@
+/* 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 */
+
+/*
+ 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>
+
+#if defined(__EMX__)
+#include <sys/ioctl.h>
+#define ioctlsocket(A,B,C) ioctl((A),(B),(void *)(C),sizeof(*(C)))
+#undef HAVE_FCNTL
+#endif /* defined(__EMX__) */
+
+#if defined(MSDOS) || defined(__WIN__)
+#ifdef __WIN__
+#undef errno
+#undef EINTR
+#undef EAGAIN
+#define errno WSAGetLastError()
+#define EINTR WSAEINTR
+#define EAGAIN WSAEINPROGRESS
+#endif /* __WIN__ */
+#define O_NONBLOCK 1 /* For emulation of fcntl() */
+#endif
+#ifndef EWOULDBLOCK
+#define EWOULDBLOCK 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, "socket (%d)", vio->sd);
+#if !defined(___WIN__) && !defined(__EMX__)
+#if !defined(NO_FCNTL_NONBLOCK)
+ vio->fcntl_mode = fcntl(sd, F_GETFL);
+#endif
+#else /* !defined(__WIN__) && !defined(__EMX__) */
+ {
+ /* set to blocking mode by default */
+ ulong arg=0;
+ 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 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, buf=%p, size=%d", vio->sd, buf, size));
+#ifdef __WIN__
+ if (vio->type == VIO_TYPE_NAMEDPIPE)
+ {
+ DWORD length;
+ if (!ReadFile(vio->hPipe, buf, size, &length, NULL))
+ DBUG_RETURN(-1);
+ 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("error", ("Got error %d during read",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, buf=%p, size=%d", vio->sd, buf, size));
+#ifdef __WIN__
+ if ( vio->type == VIO_TYPE_NAMEDPIPE)
+ {
+ DWORD length;
+ if (!WriteFile(vio->hPipe, (char*) buf, size, &length, NULL))
+ DBUG_RETURN(-1);
+ 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("error", ("Got error on write: %d",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__)
+#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)), my_bool onoff)
+{
+ int r=0;
+ DBUG_ENTER("vio_fastsend");
+ DBUG_PRINT("enter", ("onoff:%d", (int) onoff));
+
+#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 = errno;
+ return en == EAGAIN || en == EINTR || en == 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("error", ("close() failed, error: %d",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", 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;
+}
+
+#endif /* HAVE_VIO */
diff --git a/sql/watchdog_mysqld b/sql/watchdog_mysqld
new file mode 100755
index 00000000000..0b26bb15acd
--- /dev/null
+++ b/sql/watchdog_mysqld
@@ -0,0 +1,126 @@
+#!/usr/bin/perl
+# Copyright (C) 1979-1998 TcX AB & Monty Program KB & Detron HB
+#
+# This software is distributed with NO WARRANTY OF ANY KIND. No author or
+# distributor accepts any responsibility for the consequences of using it, or
+# for whether it serves any particular purpose or works at all, unless he or
+# she says so in writing. Refer to the Free Public License (the "License")
+# for full details.
+#
+# Every copy of this file must include a copy of the License, normally in a
+# plain ASCII text file named PUBLIC. The License grants you the right to
+# copy, modify and redistribute this file, but only under certain conditions
+# described in the License. Among other things, the License requires that
+# the copyright notice and this notice be preserved on all copies. */
+
+#
+# This scripts is started by safe_mysqld. It checks that MySQL is alive and
+# working ( = answering to ping). If not, force mysqld down, check all
+# tables and let safe_mysqld restart the server.
+#
+# For this to work, you should have procmail installed as the commands
+# 'lockfile' and is used to sync with safe_mysqld
+#
+# NOTE: You should only use this script as a last resort if mysqld locks
+# up unexpectedly in a critical application and you have to get it to
+# work temporarily while waiting for a solution from mysql@tcx.se or
+# mysql-support@tcx.se
+
+
+use POSIX "waitpid";
+
+# Arguments from safe_mysqld
+
+if ($#ARGV != 4)
+{
+ print "$0: Wrong number of arguments. Aborting\n";
+ exit 1;
+}
+
+$lock_file=shift; # File to lock to sync with safe_mysqld
+$pid_file=shift; # Pid file used by mysqld
+$bin_dir=shift; # Directory where mysqladmin is
+$test_timeout=shift; # Time between testing if mysqld is alive
+$wait_timeout=shift; # How long time to wait for ping
+
+$|=1; # autoflush
+
+# Check that mysqld has started properly
+
+for ($i=1 ; $i < 10 ; $i ++)
+{
+ last if (-e $pid_file);
+}
+sleep(1); # If server has just created the file
+if (($mysqld_pid=`cat $pid_file`) <= 0)
+{
+ print "$0: Error: Invalid pidfile (contains '$mysqld_pid'). Aborting\n";
+}
+
+# Start pinging mysqld
+
+for (;;)
+{
+ sleep($test_timeout); # Time between tests
+ `lockfile $lock_file > /dev/null 2>&1`; # Sync with safe_mysqld
+ if (($pid=fork()) == 0)
+ {
+ setpgrp(0,0);
+ exit(int(system("$bin_dir/mysqladmin -w status > /dev/null")/256));
+ }
+ for ($i=0; ($res=waitpid(-1,&POSIX::WNOHANG)) == 0 && $i < $wait_timeout ; $i++)
+ {
+ sleep(1);
+ }
+ if ($res == 0)
+ {
+ print "$0: Warning: mysqld hanged; Killing it so that safe_mysqld can restart it!\n";
+ $mysqld_pid= `cat $pid_file`;
+ if ($mysqld_pid <= 0)
+ {
+ print "$0: Error: Invalid pidfile (contains '$mysqld_pid'). Aborting\n";
+ system("rm -f $lock_file");
+ kill(-9,$pid);
+ exit 1;
+ }
+ print "$0: Sending signal 15 to $mysqld_pid\n";
+ kill(-15, $pid,$mysqld_pid); # Give it a last change to die nicely
+ for ($i=0 ; $i < 5 ; $i++) { sleep(1); } # Wait 5 seconds (signal safe)
+ waitpid(-1,&POSIX::WNOHANG);
+ if (kill(0,$pid,$mysqld_pid) != 0)
+ {
+ print "$0: Sending signal 9 to $mysqld_pid\n";
+ kill(-9,$pid,$mysqld_pid); # No time to be nice anymore
+ sleep(2); # Give system time to clean up
+ waitpid(-1,&POSIX::WNOHANG);
+ if (kill(0,$mysqld_pid) != 0)
+ {
+ print "$0: Warning: mysqld don't want to die. Aborting\n";
+ system("rm -f $lock_file");
+ exit 1;
+ }
+ }
+ # safe_mysqld will not restart mysqld if the pid file doesn't exists
+ system("rm $pid_file");
+ system("touch $pid_file");
+ }
+ elsif ($res == -1)
+ {
+ print "$0: Error: waitpid returned $res when wating for pid $pid\nPlease verify that $0 is correct for your system\n";
+ system("rm -f $lock_file");
+ exit 1;
+ }
+ else
+ {
+ $exit_code=int($?/256);
+ if ($exit_code != 0)
+ {
+ print "$0: Warning: mysqladmin returned exit code $exit_code\n";
+ }
+ else
+ {
+ #print "mysqld is alive and feeling well\n";
+ }
+ }
+ system("rm -f $lock_file"); # safemysqld will now take over
+}