summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorunknown <konstantin@mysql.com>2005-09-23 00:46:59 +0400
committerunknown <konstantin@mysql.com>2005-09-23 00:46:59 +0400
commitc4eee8bd69d4099132a99948f890b69ae5539b3a (patch)
treec8753cbe4273232bc93f91ecd04ad7014cee9cdb
parent9a0ee3bffb39954c8668f47dd48f98252b3b2b2b (diff)
parentb7ee1513d46d2dd1057ed9a30a9357ea86b31cca (diff)
downloadmariadb-git-c4eee8bd69d4099132a99948f890b69ae5539b3a.tar.gz
Merge mysqldev@production.mysql.com:my/mysql-5.0-release
into mysql.com:/opt/local/work/mysql-5.0-root BitKeeper/etc/ignore: auto-union include/my_sys.h: Auto merged mysql-test/r/information_schema.result: Auto merged mysql-test/t/ctype_ujis.test: Auto merged sql/ha_federated.cc: Auto merged sql/handler.cc: Auto merged sql/item_func.h: Auto merged sql/log.cc: Auto merged sql/sp_head.cc: Auto merged sql/sp_head.h: Auto merged sql/sql_acl.cc: Auto merged sql/sql_base.cc: Auto merged sql/sql_class.cc: Auto merged sql/sql_class.h: Auto merged sql/sql_lex.h: Auto merged sql/sql_prepare.cc: Auto merged sql/sql_select.cc: Auto merged sql/sql_view.cc: Auto merged sql/table.cc: Auto merged
-rw-r--r--.bzrignore2
-rwxr-xr-xVC++Files/libmysqld/libmysqld.vcproj14
-rwxr-xr-xVC++Files/libmysqltest/myTest.vcproj6
-rwxr-xr-xVC++Files/myisamchk/myisamchk.vcproj2
-rwxr-xr-xVC++Files/mysql.sln93
-rwxr-xr-xVC++Files/mysqlserver/dummy.cpp0
-rwxr-xr-xVC++Files/mysqlserver/mysqlserver.vcproj11
-rwxr-xr-xVC++Files/sql/mysqld.vcproj2
-rwxr-xr-xVC++Files/tests/mysql_client_test.vcproj6
-rw-r--r--include/my_sys.h1
-rw-r--r--libmysqld/Makefile.am2
-rw-r--r--mysql-test/r/ctype_ujis.result30
-rw-r--r--mysql-test/r/information_schema.result23
-rw-r--r--mysql-test/r/information_schema_inno.result2
-rw-r--r--mysql-test/r/multi_statement.result1
-rw-r--r--mysql-test/r/sp-big.result46
-rw-r--r--mysql-test/r/temp_table.result29
-rw-r--r--mysql-test/r/type_bit.result10
-rw-r--r--mysql-test/t/ctype_ujis.test42
-rw-r--r--mysql-test/t/information_schema.test11
-rw-r--r--mysql-test/t/information_schema_inno.test2
-rw-r--r--mysql-test/t/multi_statement.test4
-rw-r--r--mysql-test/t/sp-big.test49
-rw-r--r--mysql-test/t/temp_table.test25
-rw-r--r--mysql-test/t/type_bit.test2
-rw-r--r--mysql-test/valgrind.supp24
-rw-r--r--mysys/my_alloc.c51
-rw-r--r--scripts/make_binary_distribution.sh3
-rw-r--r--server-tools/instance-manager/mysqlmanager.vcproj4
-rw-r--r--sql/Makefile.am4
-rw-r--r--sql/ha_federated.cc20
-rw-r--r--sql/handler.cc8
-rw-r--r--sql/item_func.h3
-rw-r--r--sql/item_subselect.cc2
-rw-r--r--sql/log.cc2
-rw-r--r--sql/protocol.h24
-rw-r--r--sql/sp_head.cc112
-rw-r--r--sql/sp_head.h9
-rw-r--r--sql/sp_rcontext.cc209
-rw-r--r--sql/sp_rcontext.h43
-rw-r--r--sql/sql_acl.cc2
-rw-r--r--sql/sql_base.cc2
-rw-r--r--sql/sql_class.cc28
-rw-r--r--sql/sql_class.h26
-rw-r--r--sql/sql_cursor.cc660
-rw-r--r--sql/sql_cursor.h65
-rw-r--r--sql/sql_derived.cc43
-rw-r--r--sql/sql_lex.h10
-rw-r--r--sql/sql_list.h2
-rw-r--r--sql/sql_prepare.cc256
-rw-r--r--sql/sql_select.cc430
-rw-r--r--sql/sql_select.h59
-rw-r--r--sql/sql_union.cc176
-rw-r--r--sql/sql_view.cc2
-rw-r--r--sql/table.cc59
-rw-r--r--sql/table.h3
-rw-r--r--sql/unireg.cc4
-rw-r--r--tests/mysql_client_test.c7
58 files changed, 1785 insertions, 982 deletions
diff --git a/.bzrignore b/.bzrignore
index 3932ed4d127..388dcac2ca6 100644
--- a/.bzrignore
+++ b/.bzrignore
@@ -478,6 +478,8 @@ libmysqld/sql_cache.cc
libmysqld/sql_class.cc
libmysqld/sql_command
libmysqld/sql_crypt.cc
+libmysqld/sql_cursor.cc
+libmysqld/sql_cursor.h
libmysqld/sql_db.cc
libmysqld/sql_delete.cc
libmysqld/sql_derived.cc
diff --git a/VC++Files/libmysqld/libmysqld.vcproj b/VC++Files/libmysqld/libmysqld.vcproj
index 187a63ece38..eb411f79362 100755
--- a/VC++Files/libmysqld/libmysqld.vcproj
+++ b/VC++Files/libmysqld/libmysqld.vcproj
@@ -24,7 +24,7 @@
AdditionalIncludeDirectories="../include,../libmysqld,../sql,../regex,../extra/yassl/include,../bdb/build_win32,../zlib"
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;SAFEMALLOC;HAVE_BERKELEY_DB;USE_SYMDIR;SIGNAL_WITH_VIO_CLOSE;HAVE_DLOPEN;EMBEDDED_LIBRARY;HAVE_INNOBASE_DB;USE_TLS;__WIN__"
BasicRuntimeChecks="3"
- RuntimeLibrary="0"
+ RuntimeLibrary="1"
PrecompiledHeaderFile=".\debug/libmysqld.pch"
AssemblerListingLocation=".\debug/"
ObjectFile=".\debug/"
@@ -37,11 +37,11 @@
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
- AdditionalDependencies="odbc32.lib odbccp32.lib Wsock32.lib ..\lib_debug\dbug_tls.lib ..\lib_debug\mysys_tls.lib ..\lib_debug\strings.lib ..\lib_debug\regex.lib ..\lib_debug\heap_tls.lib ..\lib_debug\innodb.lib ..\extra\yassl\Debug\yassl.lib"
+ AdditionalDependencies="odbc32.lib odbccp32.lib Wsock32.lib"
OutputFile="../lib_debug/libmysqld.dll"
LinkIncremental="1"
SuppressStartupBanner="TRUE"
- IgnoreDefaultLibraryNames="LIBCMTD"
+ IgnoreDefaultLibraryNames=""
ModuleDefinitionFile=".\libmysqld.def"
GenerateDebugInformation="TRUE"
ProgramDatabaseFile=".\debug/libmysqld.pdb"
@@ -106,7 +106,7 @@
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
- AdditionalDependencies="odbc32.lib odbccp32.lib Wsock32.lib ..\lib_release\myisam_tls.lib ..\lib_release\myisammrg_tls.lib ..\lib_release\mysys_tls.lib ..\lib_release\strings.lib ..\lib_release\regex.lib ..\lib_release\heap_tls.lib ..\lib_release\innodb.lib ..\lib_release\zlib.lib ..\extra\yassl\Release\yassl.lib"
+ AdditionalDependencies="odbc32.lib odbccp32.lib Wsock32.lib"
OutputFile="../lib_pro/libmysqld.dll"
LinkIncremental="1"
SuppressStartupBanner="TRUE"
@@ -172,7 +172,7 @@
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
- AdditionalDependencies="odbc32.lib odbccp32.lib Wsock32.lib ..\lib_release\myisam_tls.lib ..\lib_release\myisammrg_tls.lib ..\lib_release\mysys_tls.lib ..\lib_release\strings.lib ..\lib_release\regex.lib ..\lib_release\heap_tls.lib ..\lib_release\innodb.lib ..\lib_release\bdb.lib ..\lib_release\zlib.lib ..\extra\yassl\Release\yassl.lib"
+ AdditionalDependencies="odbc32.lib odbccp32.lib Wsock32.lib"
OutputFile="../lib_release/libmysqld.dll"
LinkIncremental="1"
SuppressStartupBanner="TRUE"
@@ -238,7 +238,7 @@
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
- AdditionalDependencies="odbc32.lib odbccp32.lib Wsock32.lib ..\lib_release\myisam_tls.lib ..\lib_release\myisammrg_tls.lib ..\lib_release\mysys_tls.lib ..\lib_release\strings.lib ..\lib_release\regex.lib ..\lib_release\heap_tls.lib ..\lib_release\zlib.lib ..\extra\yassl\Release\yassl.lib"
+ AdditionalDependencies="odbc32.lib odbccp32.lib Wsock32.lib"
OutputFile="../lib_classic/libmysqld.dll"
LinkIncremental="1"
SuppressStartupBanner="TRUE"
@@ -2299,7 +2299,7 @@
</FileConfiguration>
</File>
<File
- RelativePath="..\sql\protocol_cursor.cpp">
+ RelativePath="..\sql\sql_cursor.cpp">
<FileConfiguration
Name="Debug|Win32">
<Tool
diff --git a/VC++Files/libmysqltest/myTest.vcproj b/VC++Files/libmysqltest/myTest.vcproj
index 06ce20bf021..afc44b482c9 100755
--- a/VC++Files/libmysqltest/myTest.vcproj
+++ b/VC++Files/libmysqltest/myTest.vcproj
@@ -40,7 +40,7 @@
<Tool
Name="VCLinkerTool"
AdditionalDependencies="libmysql.lib odbc32.lib odbccp32.lib"
- OutputFile=".\release/myTest.exe"
+ OutputFile="..\client_release/myTest.exe"
LinkIncremental="1"
SuppressStartupBanner="TRUE"
AdditionalLibraryDirectories="..\lib_release"
@@ -74,7 +74,7 @@
</Configuration>
<Configuration
Name="Debug|Win32"
- OutputDirectory=".\debug"
+ OutputDirectory="..\client_debug"
IntermediateDirectory=".\debug"
ConfigurationType="1"
UseOfMFC="0"
@@ -100,7 +100,7 @@
<Tool
Name="VCLinkerTool"
AdditionalDependencies="libmysql.lib odbc32.lib odbccp32.lib"
- OutputFile=".\debug/myTest.exe"
+ OutputFile="../client_debug/myTest.exe"
LinkIncremental="1"
SuppressStartupBanner="TRUE"
AdditionalLibraryDirectories="..\lib_debug"
diff --git a/VC++Files/myisamchk/myisamchk.vcproj b/VC++Files/myisamchk/myisamchk.vcproj
index ce2435bc34a..33f813024b5 100755
--- a/VC++Files/myisamchk/myisamchk.vcproj
+++ b/VC++Files/myisamchk/myisamchk.vcproj
@@ -101,7 +101,7 @@
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
- AdditionalDependencies="wsock32.lib setargv.obj ..\lib_release\myisam.lib ..\lib_release\mysys.lib ..\lib_release\strings.lib ..\lib_release\zlib.lib"
+ AdditionalDependencies="wsock32.lib setargv.obj"
OutputFile="../client_classic/myisamchk.exe"
LinkIncremental="1"
SuppressStartupBanner="TRUE"
diff --git a/VC++Files/mysql.sln b/VC++Files/mysql.sln
index 5b062a31aab..ffa2050bbed 100755
--- a/VC++Files/mysql.sln
+++ b/VC++Files/mysql.sln
@@ -37,6 +37,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmysqld", "libmysqld\libm
{13D37150-54D0-46C5-9519-03923243C7C7} = {13D37150-54D0-46C5-9519-03923243C7C7}
{BA86AE72-0CF5-423D-BBA2-E12B0D72EBFB} = {BA86AE72-0CF5-423D-BBA2-E12B0D72EBFB}
{6EEF697A-3772-48D8-A5BA-EF11B9AC46E3} = {6EEF697A-3772-48D8-A5BA-EF11B9AC46E3}
+ {DB28DE80-837F-4497-9AA9-CC0A20584C98} = {DB28DE80-837F-4497-9AA9-CC0A20584C98}
{D8E4B489-C5DD-407D-99DB-FE7C7A5A83A0} = {D8E4B489-C5DD-407D-99DB-FE7C7A5A83A0}
{262280A8-37D5-4037-BDFB-242468DFB3D2} = {262280A8-37D5-4037-BDFB-242468DFB3D2}
{8762A9B8-72A9-462E-A9A2-F3265081F8AF} = {8762A9B8-72A9-462E-A9A2-F3265081F8AF}
@@ -268,6 +269,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mysqltest", "client\mysqlte
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mysql_client_test", "tests\mysql_client_test.vcproj", "{DA224DAB-5006-42BE-BB77-16E8BE5326D5}"
ProjectSection(ProjectDependencies) = postProject
+ {26383276-4843-494B-8BE0-8936ED3EBAAB} = {26383276-4843-494B-8BE0-8936ED3EBAAB}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mysql_test_run_new", "mysql-test\mysql_test_run_new.vcproj", "{6189F838-21C6-42A1-B2D0-9146316573F7}"
@@ -305,24 +307,32 @@ Global
Release = Release
EndGlobalSection
GlobalSection(ProjectConfiguration) = postSolution
- {6EEF697A-3772-48D8-A5BA-EF11B9AC46E3}.classic.ActiveCfg = Debug|Win32
- {6EEF697A-3772-48D8-A5BA-EF11B9AC46E3}.classic nt.ActiveCfg = Debug|Win32
+ {6EEF697A-3772-48D8-A5BA-EF11B9AC46E3}.classic.ActiveCfg = Max|Win32
+ {6EEF697A-3772-48D8-A5BA-EF11B9AC46E3}.classic.Build.0 = Max|Win32
+ {6EEF697A-3772-48D8-A5BA-EF11B9AC46E3}.classic nt.ActiveCfg = Max|Win32
+ {6EEF697A-3772-48D8-A5BA-EF11B9AC46E3}.classic nt.Build.0 = Max|Win32
{6EEF697A-3772-48D8-A5BA-EF11B9AC46E3}.Debug.ActiveCfg = Debug|Win32
{6EEF697A-3772-48D8-A5BA-EF11B9AC46E3}.Debug.Build.0 = Debug|Win32
- {6EEF697A-3772-48D8-A5BA-EF11B9AC46E3}.Embedded_Classic.ActiveCfg = Debug|Win32
- {6EEF697A-3772-48D8-A5BA-EF11B9AC46E3}.Embedded_Classic.Build.0 = Debug|Win32
+ {6EEF697A-3772-48D8-A5BA-EF11B9AC46E3}.Embedded_Classic.ActiveCfg = Max|Win32
+ {6EEF697A-3772-48D8-A5BA-EF11B9AC46E3}.Embedded_Classic.Build.0 = Max|Win32
{6EEF697A-3772-48D8-A5BA-EF11B9AC46E3}.Embedded_Debug.ActiveCfg = Debug|Win32
{6EEF697A-3772-48D8-A5BA-EF11B9AC46E3}.Embedded_Debug.Build.0 = Debug|Win32
- {6EEF697A-3772-48D8-A5BA-EF11B9AC46E3}.Embedded_Pro.ActiveCfg = Debug|Win32
+ {6EEF697A-3772-48D8-A5BA-EF11B9AC46E3}.Embedded_Pro.ActiveCfg = Max|Win32
+ {6EEF697A-3772-48D8-A5BA-EF11B9AC46E3}.Embedded_Pro.Build.0 = Max|Win32
{6EEF697A-3772-48D8-A5BA-EF11B9AC46E3}.Embedded_Release.ActiveCfg = Max|Win32
+ {6EEF697A-3772-48D8-A5BA-EF11B9AC46E3}.Embedded_Release.Build.0 = Max|Win32
{6EEF697A-3772-48D8-A5BA-EF11B9AC46E3}.Max.ActiveCfg = Max|Win32
{6EEF697A-3772-48D8-A5BA-EF11B9AC46E3}.Max.Build.0 = Max|Win32
{6EEF697A-3772-48D8-A5BA-EF11B9AC46E3}.Max nt.ActiveCfg = Max|Win32
{6EEF697A-3772-48D8-A5BA-EF11B9AC46E3}.Max nt.Build.0 = Max|Win32
- {6EEF697A-3772-48D8-A5BA-EF11B9AC46E3}.nt.ActiveCfg = Debug|Win32
- {6EEF697A-3772-48D8-A5BA-EF11B9AC46E3}.pro.ActiveCfg = Debug|Win32
- {6EEF697A-3772-48D8-A5BA-EF11B9AC46E3}.pro nt.ActiveCfg = Debug|Win32
- {6EEF697A-3772-48D8-A5BA-EF11B9AC46E3}.Release.ActiveCfg = Debug|Win32
+ {6EEF697A-3772-48D8-A5BA-EF11B9AC46E3}.nt.ActiveCfg = Max|Win32
+ {6EEF697A-3772-48D8-A5BA-EF11B9AC46E3}.nt.Build.0 = Max|Win32
+ {6EEF697A-3772-48D8-A5BA-EF11B9AC46E3}.pro.ActiveCfg = Max|Win32
+ {6EEF697A-3772-48D8-A5BA-EF11B9AC46E3}.pro.Build.0 = Max|Win32
+ {6EEF697A-3772-48D8-A5BA-EF11B9AC46E3}.pro nt.ActiveCfg = Max|Win32
+ {6EEF697A-3772-48D8-A5BA-EF11B9AC46E3}.pro nt.Build.0 = Max|Win32
+ {6EEF697A-3772-48D8-A5BA-EF11B9AC46E3}.Release.ActiveCfg = Max|Win32
+ {6EEF697A-3772-48D8-A5BA-EF11B9AC46E3}.Release.Build.0 = Max|Win32
{1FD8A136-B86A-4B54-95B0-FA4E2EE2CCBC}.classic.ActiveCfg = Release|Win32
{1FD8A136-B86A-4B54-95B0-FA4E2EE2CCBC}.classic nt.ActiveCfg = Release|Win32
{1FD8A136-B86A-4B54-95B0-FA4E2EE2CCBC}.classic nt.Build.0 = Release|Win32
@@ -349,11 +359,14 @@ Global
{FC369DF4-AEB7-4531-BF34-A638C4363BFE}.classic nt.Build.0 = Release|Win32
{FC369DF4-AEB7-4531-BF34-A638C4363BFE}.Debug.ActiveCfg = Debug|Win32
{FC369DF4-AEB7-4531-BF34-A638C4363BFE}.Debug.Build.0 = Debug|Win32
- {FC369DF4-AEB7-4531-BF34-A638C4363BFE}.Embedded_Classic.ActiveCfg = Debug|Win32
+ {FC369DF4-AEB7-4531-BF34-A638C4363BFE}.Embedded_Classic.ActiveCfg = Release|Win32
+ {FC369DF4-AEB7-4531-BF34-A638C4363BFE}.Embedded_Classic.Build.0 = Release|Win32
{FC369DF4-AEB7-4531-BF34-A638C4363BFE}.Embedded_Debug.ActiveCfg = TLS_DEBUG|Win32
{FC369DF4-AEB7-4531-BF34-A638C4363BFE}.Embedded_Debug.Build.0 = TLS_DEBUG|Win32
- {FC369DF4-AEB7-4531-BF34-A638C4363BFE}.Embedded_Pro.ActiveCfg = Debug|Win32
+ {FC369DF4-AEB7-4531-BF34-A638C4363BFE}.Embedded_Pro.ActiveCfg = Release|Win32
+ {FC369DF4-AEB7-4531-BF34-A638C4363BFE}.Embedded_Pro.Build.0 = Release|Win32
{FC369DF4-AEB7-4531-BF34-A638C4363BFE}.Embedded_Release.ActiveCfg = Release|Win32
+ {FC369DF4-AEB7-4531-BF34-A638C4363BFE}.Embedded_Release.Build.0 = Release|Win32
{FC369DF4-AEB7-4531-BF34-A638C4363BFE}.Max.ActiveCfg = Release|Win32
{FC369DF4-AEB7-4531-BF34-A638C4363BFE}.Max.Build.0 = Release|Win32
{FC369DF4-AEB7-4531-BF34-A638C4363BFE}.Max nt.ActiveCfg = Release|Win32
@@ -392,11 +405,14 @@ Global
{C70A6DC7-7D45-4C16-8654-7E57713A4C04}.pro nt.Build.0 = Release|Win32
{C70A6DC7-7D45-4C16-8654-7E57713A4C04}.Release.ActiveCfg = Release|Win32
{C70A6DC7-7D45-4C16-8654-7E57713A4C04}.Release.Build.0 = Release|Win32
- {13D37150-54D0-46C5-9519-03923243C7C7}.classic.ActiveCfg = nt|Win32
+ {13D37150-54D0-46C5-9519-03923243C7C7}.classic.ActiveCfg = Release|Win32
+ {13D37150-54D0-46C5-9519-03923243C7C7}.classic.Build.0 = Release|Win32
{13D37150-54D0-46C5-9519-03923243C7C7}.classic nt.ActiveCfg = nt|Win32
+ {13D37150-54D0-46C5-9519-03923243C7C7}.classic nt.Build.0 = nt|Win32
{13D37150-54D0-46C5-9519-03923243C7C7}.Debug.ActiveCfg = Debug|Win32
{13D37150-54D0-46C5-9519-03923243C7C7}.Debug.Build.0 = Debug|Win32
- {13D37150-54D0-46C5-9519-03923243C7C7}.Embedded_Classic.ActiveCfg = nt|Win32
+ {13D37150-54D0-46C5-9519-03923243C7C7}.Embedded_Classic.ActiveCfg = Release|Win32
+ {13D37150-54D0-46C5-9519-03923243C7C7}.Embedded_Classic.Build.0 = Release|Win32
{13D37150-54D0-46C5-9519-03923243C7C7}.Embedded_Debug.ActiveCfg = Debug|Win32
{13D37150-54D0-46C5-9519-03923243C7C7}.Embedded_Debug.Build.0 = Debug|Win32
{13D37150-54D0-46C5-9519-03923243C7C7}.Embedded_Pro.ActiveCfg = Release|Win32
@@ -454,8 +470,8 @@ Global
{93CA92A0-D7B8-4FAE-9EBB-D92EFBF631C9}.pro.ActiveCfg = pro|Win32
{93CA92A0-D7B8-4FAE-9EBB-D92EFBF631C9}.pro nt.ActiveCfg = pro|Win32
{93CA92A0-D7B8-4FAE-9EBB-D92EFBF631C9}.Release.ActiveCfg = Release|Win32
- {2794E434-7CCE-44DB-B2FB-789ABE53D6B9}.classic.ActiveCfg = Debug|Win32
- {2794E434-7CCE-44DB-B2FB-789ABE53D6B9}.classic.Build.0 = Debug|Win32
+ {2794E434-7CCE-44DB-B2FB-789ABE53D6B9}.classic.ActiveCfg = Release|Win32
+ {2794E434-7CCE-44DB-B2FB-789ABE53D6B9}.classic.Build.0 = Release|Win32
{2794E434-7CCE-44DB-B2FB-789ABE53D6B9}.classic nt.ActiveCfg = Release|Win32
{2794E434-7CCE-44DB-B2FB-789ABE53D6B9}.classic nt.Build.0 = Release|Win32
{2794E434-7CCE-44DB-B2FB-789ABE53D6B9}.Debug.ActiveCfg = Debug|Win32
@@ -507,6 +523,7 @@ Global
{262280A8-37D5-4037-BDFB-242468DFB3D2}.Embedded_Classic.ActiveCfg = TLS|Win32
{262280A8-37D5-4037-BDFB-242468DFB3D2}.Embedded_Classic.Build.0 = TLS|Win32
{262280A8-37D5-4037-BDFB-242468DFB3D2}.Embedded_Debug.ActiveCfg = Debug|Win32
+ {262280A8-37D5-4037-BDFB-242468DFB3D2}.Embedded_Debug.Build.0 = Debug|Win32
{262280A8-37D5-4037-BDFB-242468DFB3D2}.Embedded_Pro.ActiveCfg = TLS|Win32
{262280A8-37D5-4037-BDFB-242468DFB3D2}.Embedded_Pro.Build.0 = TLS|Win32
{262280A8-37D5-4037-BDFB-242468DFB3D2}.Embedded_Release.ActiveCfg = TLS|Win32
@@ -589,8 +606,8 @@ Global
{194F5EE6-9440-4298-A6FE-A9B4B480B44C}.pro nt.Build.0 = Release|Win32
{194F5EE6-9440-4298-A6FE-A9B4B480B44C}.Release.ActiveCfg = Release|Win32
{194F5EE6-9440-4298-A6FE-A9B4B480B44C}.Release.Build.0 = Release|Win32
- {D8E4B489-C5DD-407D-99DB-FE7C7A5A83A0}.classic.ActiveCfg = Debug|Win32
- {D8E4B489-C5DD-407D-99DB-FE7C7A5A83A0}.classic.Build.0 = Debug|Win32
+ {D8E4B489-C5DD-407D-99DB-FE7C7A5A83A0}.classic.ActiveCfg = Release|Win32
+ {D8E4B489-C5DD-407D-99DB-FE7C7A5A83A0}.classic.Build.0 = Release|Win32
{D8E4B489-C5DD-407D-99DB-FE7C7A5A83A0}.classic nt.ActiveCfg = Release|Win32
{D8E4B489-C5DD-407D-99DB-FE7C7A5A83A0}.classic nt.Build.0 = Release|Win32
{D8E4B489-C5DD-407D-99DB-FE7C7A5A83A0}.Debug.ActiveCfg = Debug|Win32
@@ -598,6 +615,7 @@ Global
{D8E4B489-C5DD-407D-99DB-FE7C7A5A83A0}.Embedded_Classic.ActiveCfg = TLS|Win32
{D8E4B489-C5DD-407D-99DB-FE7C7A5A83A0}.Embedded_Classic.Build.0 = TLS|Win32
{D8E4B489-C5DD-407D-99DB-FE7C7A5A83A0}.Embedded_Debug.ActiveCfg = Debug|Win32
+ {D8E4B489-C5DD-407D-99DB-FE7C7A5A83A0}.Embedded_Debug.Build.0 = Debug|Win32
{D8E4B489-C5DD-407D-99DB-FE7C7A5A83A0}.Embedded_Pro.ActiveCfg = TLS|Win32
{D8E4B489-C5DD-407D-99DB-FE7C7A5A83A0}.Embedded_Pro.Build.0 = TLS|Win32
{D8E4B489-C5DD-407D-99DB-FE7C7A5A83A0}.Embedded_Release.ActiveCfg = TLS|Win32
@@ -724,8 +742,8 @@ Global
{67154F28-D076-419E-B149-819EF548E670}.pro nt.Build.0 = Release|Win32
{67154F28-D076-419E-B149-819EF548E670}.Release.ActiveCfg = Release|Win32
{67154F28-D076-419E-B149-819EF548E670}.Release.Build.0 = Release|Win32
- {26383276-4843-494B-8BE0-8936ED3EBAAB}.classic.ActiveCfg = Debug|Win32
- {26383276-4843-494B-8BE0-8936ED3EBAAB}.classic.Build.0 = Debug|Win32
+ {26383276-4843-494B-8BE0-8936ED3EBAAB}.classic.ActiveCfg = Release|Win32
+ {26383276-4843-494B-8BE0-8936ED3EBAAB}.classic.Build.0 = Release|Win32
{26383276-4843-494B-8BE0-8936ED3EBAAB}.classic nt.ActiveCfg = Release|Win32
{26383276-4843-494B-8BE0-8936ED3EBAAB}.classic nt.Build.0 = Release|Win32
{26383276-4843-494B-8BE0-8936ED3EBAAB}.Debug.ActiveCfg = Debug|Win32
@@ -911,9 +929,13 @@ Global
{DB28DE80-837F-4497-9AA9-CC0A20584C98}.Debug.ActiveCfg = Debug|Win32
{DB28DE80-837F-4497-9AA9-CC0A20584C98}.Debug.Build.0 = Debug|Win32
{DB28DE80-837F-4497-9AA9-CC0A20584C98}.Embedded_Classic.ActiveCfg = Release|Win32
+ {DB28DE80-837F-4497-9AA9-CC0A20584C98}.Embedded_Classic.Build.0 = Release|Win32
{DB28DE80-837F-4497-9AA9-CC0A20584C98}.Embedded_Debug.ActiveCfg = Debug|Win32
+ {DB28DE80-837F-4497-9AA9-CC0A20584C98}.Embedded_Debug.Build.0 = Debug|Win32
{DB28DE80-837F-4497-9AA9-CC0A20584C98}.Embedded_Pro.ActiveCfg = Release|Win32
+ {DB28DE80-837F-4497-9AA9-CC0A20584C98}.Embedded_Pro.Build.0 = Release|Win32
{DB28DE80-837F-4497-9AA9-CC0A20584C98}.Embedded_Release.ActiveCfg = Release|Win32
+ {DB28DE80-837F-4497-9AA9-CC0A20584C98}.Embedded_Release.Build.0 = Release|Win32
{DB28DE80-837F-4497-9AA9-CC0A20584C98}.Max.ActiveCfg = Release|Win32
{DB28DE80-837F-4497-9AA9-CC0A20584C98}.Max.Build.0 = Release|Win32
{DB28DE80-837F-4497-9AA9-CC0A20584C98}.Max nt.ActiveCfg = Release|Win32
@@ -1022,14 +1044,14 @@ Global
{16699B52-ECC6-4A96-A99F-A043059BA2E7}.pro nt.Build.0 = Release|Win32
{16699B52-ECC6-4A96-A99F-A043059BA2E7}.Release.ActiveCfg = Release|Win32
{16699B52-ECC6-4A96-A99F-A043059BA2E7}.Release.Build.0 = Release|Win32
- {EEC1300B-85A5-497C-B3E1-F708021DF859}.classic.ActiveCfg = Debug|Win32
- {EEC1300B-85A5-497C-B3E1-F708021DF859}.classic.Build.0 = Debug|Win32
+ {EEC1300B-85A5-497C-B3E1-F708021DF859}.classic.ActiveCfg = Release|Win32
+ {EEC1300B-85A5-497C-B3E1-F708021DF859}.classic.Build.0 = Release|Win32
{EEC1300B-85A5-497C-B3E1-F708021DF859}.classic nt.ActiveCfg = Release|Win32
{EEC1300B-85A5-497C-B3E1-F708021DF859}.classic nt.Build.0 = Release|Win32
{EEC1300B-85A5-497C-B3E1-F708021DF859}.Debug.ActiveCfg = Debug|Win32
{EEC1300B-85A5-497C-B3E1-F708021DF859}.Debug.Build.0 = Debug|Win32
- {EEC1300B-85A5-497C-B3E1-F708021DF859}.Embedded_Classic.ActiveCfg = Debug|Win32
- {EEC1300B-85A5-497C-B3E1-F708021DF859}.Embedded_Classic.Build.0 = Debug|Win32
+ {EEC1300B-85A5-497C-B3E1-F708021DF859}.Embedded_Classic.ActiveCfg = Release|Win32
+ {EEC1300B-85A5-497C-B3E1-F708021DF859}.Embedded_Classic.Build.0 = Release|Win32
{EEC1300B-85A5-497C-B3E1-F708021DF859}.Embedded_Debug.ActiveCfg = Debug|Win32
{EEC1300B-85A5-497C-B3E1-F708021DF859}.Embedded_Debug.Build.0 = Debug|Win32
{EEC1300B-85A5-497C-B3E1-F708021DF859}.Embedded_Pro.ActiveCfg = Release|Win32
@@ -1048,8 +1070,8 @@ Global
{EEC1300B-85A5-497C-B3E1-F708021DF859}.pro nt.Build.0 = Release|Win32
{EEC1300B-85A5-497C-B3E1-F708021DF859}.Release.ActiveCfg = Release|Win32
{EEC1300B-85A5-497C-B3E1-F708021DF859}.Release.Build.0 = Release|Win32
- {8CB5AB80-05DA-49DA-BC9F-EAC20667E0D0}.classic.ActiveCfg = Debug|Win32
- {8CB5AB80-05DA-49DA-BC9F-EAC20667E0D0}.classic.Build.0 = Debug|Win32
+ {8CB5AB80-05DA-49DA-BC9F-EAC20667E0D0}.classic.ActiveCfg = Release|Win32
+ {8CB5AB80-05DA-49DA-BC9F-EAC20667E0D0}.classic.Build.0 = Release|Win32
{8CB5AB80-05DA-49DA-BC9F-EAC20667E0D0}.classic nt.ActiveCfg = Debug|Win32
{8CB5AB80-05DA-49DA-BC9F-EAC20667E0D0}.Debug.ActiveCfg = Debug|Win32
{8CB5AB80-05DA-49DA-BC9F-EAC20667E0D0}.Debug.Build.0 = Debug|Win32
@@ -1065,7 +1087,6 @@ Global
{8CB5AB80-05DA-49DA-BC9F-EAC20667E0D0}.Release.ActiveCfg = Release|Win32
{8CB5AB80-05DA-49DA-BC9F-EAC20667E0D0}.Release.Build.0 = Release|Win32
{6F01B69C-B1A5-4C45-B3A9-744E1EB0BED5}.classic.ActiveCfg = Release|Win32
- {6F01B69C-B1A5-4C45-B3A9-744E1EB0BED5}.classic.Build.0 = Release|Win32
{6F01B69C-B1A5-4C45-B3A9-744E1EB0BED5}.classic nt.ActiveCfg = Release|Win32
{6F01B69C-B1A5-4C45-B3A9-744E1EB0BED5}.Debug.ActiveCfg = Release|Win32
{6F01B69C-B1A5-4C45-B3A9-744E1EB0BED5}.Embedded_Classic.ActiveCfg = Release|Win32
@@ -1074,12 +1095,10 @@ Global
{6F01B69C-B1A5-4C45-B3A9-744E1EB0BED5}.Embedded_Release.ActiveCfg = Release|Win32
{6F01B69C-B1A5-4C45-B3A9-744E1EB0BED5}.Max.ActiveCfg = Release|Win32
{6F01B69C-B1A5-4C45-B3A9-744E1EB0BED5}.Max nt.ActiveCfg = Release|Win32
- {6F01B69C-B1A5-4C45-B3A9-744E1EB0BED5}.Max nt.Build.0 = Release|Win32
{6F01B69C-B1A5-4C45-B3A9-744E1EB0BED5}.nt.ActiveCfg = Release|Win32
{6F01B69C-B1A5-4C45-B3A9-744E1EB0BED5}.pro.ActiveCfg = Release|Win32
{6F01B69C-B1A5-4C45-B3A9-744E1EB0BED5}.pro nt.ActiveCfg = Release|Win32
{6F01B69C-B1A5-4C45-B3A9-744E1EB0BED5}.Release.ActiveCfg = Release|Win32
- {6F01B69C-B1A5-4C45-B3A9-744E1EB0BED5}.Release.Build.0 = Release|Win32
{7FFA3009-E0E1-4E4E-9CDF-F408AA108CC8}.classic.ActiveCfg = Release|Win32
{7FFA3009-E0E1-4E4E-9CDF-F408AA108CC8}.classic.Build.0 = Release|Win32
{7FFA3009-E0E1-4E4E-9CDF-F408AA108CC8}.classic nt.ActiveCfg = Release|Win32
@@ -1102,16 +1121,20 @@ Global
{7FFA3009-E0E1-4E4E-9CDF-F408AA108CC8}.pro nt.Build.0 = Release|Win32
{7FFA3009-E0E1-4E4E-9CDF-F408AA108CC8}.Release.ActiveCfg = Release|Win32
{7FFA3009-E0E1-4E4E-9CDF-F408AA108CC8}.Release.Build.0 = Release|Win32
- {F74653C4-8003-4A79-8F53-FC69E0AD7A9B}.classic.ActiveCfg = Debug|Win32
- {F74653C4-8003-4A79-8F53-FC69E0AD7A9B}.classic.Build.0 = Debug|Win32
+ {F74653C4-8003-4A79-8F53-FC69E0AD7A9B}.classic.ActiveCfg = Release|Win32
+ {F74653C4-8003-4A79-8F53-FC69E0AD7A9B}.classic.Build.0 = Release|Win32
{F74653C4-8003-4A79-8F53-FC69E0AD7A9B}.classic nt.ActiveCfg = Release|Win32
{F74653C4-8003-4A79-8F53-FC69E0AD7A9B}.classic nt.Build.0 = Release|Win32
{F74653C4-8003-4A79-8F53-FC69E0AD7A9B}.Debug.ActiveCfg = Debug|Win32
{F74653C4-8003-4A79-8F53-FC69E0AD7A9B}.Debug.Build.0 = Debug|Win32
- {F74653C4-8003-4A79-8F53-FC69E0AD7A9B}.Embedded_Classic.ActiveCfg = Debug|Win32
+ {F74653C4-8003-4A79-8F53-FC69E0AD7A9B}.Embedded_Classic.ActiveCfg = Release|Win32
+ {F74653C4-8003-4A79-8F53-FC69E0AD7A9B}.Embedded_Classic.Build.0 = Release|Win32
{F74653C4-8003-4A79-8F53-FC69E0AD7A9B}.Embedded_Debug.ActiveCfg = Debug|Win32
- {F74653C4-8003-4A79-8F53-FC69E0AD7A9B}.Embedded_Pro.ActiveCfg = Debug|Win32
+ {F74653C4-8003-4A79-8F53-FC69E0AD7A9B}.Embedded_Debug.Build.0 = Debug|Win32
+ {F74653C4-8003-4A79-8F53-FC69E0AD7A9B}.Embedded_Pro.ActiveCfg = Release|Win32
+ {F74653C4-8003-4A79-8F53-FC69E0AD7A9B}.Embedded_Pro.Build.0 = Release|Win32
{F74653C4-8003-4A79-8F53-FC69E0AD7A9B}.Embedded_Release.ActiveCfg = Release|Win32
+ {F74653C4-8003-4A79-8F53-FC69E0AD7A9B}.Embedded_Release.Build.0 = Release|Win32
{F74653C4-8003-4A79-8F53-FC69E0AD7A9B}.Max.ActiveCfg = Release|Win32
{F74653C4-8003-4A79-8F53-FC69E0AD7A9B}.Max.Build.0 = Release|Win32
{F74653C4-8003-4A79-8F53-FC69E0AD7A9B}.Max nt.ActiveCfg = Release|Win32
@@ -1133,6 +1156,7 @@ Global
{8762A9B8-72A9-462E-A9A2-F3265081F8AF}.Embedded_Classic.ActiveCfg = Release|Win32
{8762A9B8-72A9-462E-A9A2-F3265081F8AF}.Embedded_Classic.Build.0 = Release|Win32
{8762A9B8-72A9-462E-A9A2-F3265081F8AF}.Embedded_Debug.ActiveCfg = Debug|Win32
+ {8762A9B8-72A9-462E-A9A2-F3265081F8AF}.Embedded_Debug.Build.0 = Debug|Win32
{8762A9B8-72A9-462E-A9A2-F3265081F8AF}.Embedded_Pro.ActiveCfg = Release|Win32
{8762A9B8-72A9-462E-A9A2-F3265081F8AF}.Embedded_Pro.Build.0 = Release|Win32
{8762A9B8-72A9-462E-A9A2-F3265081F8AF}.Embedded_Release.ActiveCfg = Release|Win32
@@ -1171,8 +1195,8 @@ Global
{8961F149-C68A-4154-A499-A2AB39E607E8}.pro nt.Build.0 = Release|Win32
{8961F149-C68A-4154-A499-A2AB39E607E8}.Release.ActiveCfg = Release|Win32
{8961F149-C68A-4154-A499-A2AB39E607E8}.Release.Build.0 = Release|Win32
- {DA224DAB-5006-42BE-BB77-16E8BE5326D5}.classic.ActiveCfg = Debug|Win32
- {DA224DAB-5006-42BE-BB77-16E8BE5326D5}.classic.Build.0 = Debug|Win32
+ {DA224DAB-5006-42BE-BB77-16E8BE5326D5}.classic.ActiveCfg = Release|Win32
+ {DA224DAB-5006-42BE-BB77-16E8BE5326D5}.classic.Build.0 = Release|Win32
{DA224DAB-5006-42BE-BB77-16E8BE5326D5}.classic nt.ActiveCfg = Release|Win32
{DA224DAB-5006-42BE-BB77-16E8BE5326D5}.classic nt.Build.0 = Release|Win32
{DA224DAB-5006-42BE-BB77-16E8BE5326D5}.Debug.ActiveCfg = Debug|Win32
@@ -1184,7 +1208,6 @@ Global
{DA224DAB-5006-42BE-BB77-16E8BE5326D5}.Max.ActiveCfg = Release|Win32
{DA224DAB-5006-42BE-BB77-16E8BE5326D5}.Max.Build.0 = Release|Win32
{DA224DAB-5006-42BE-BB77-16E8BE5326D5}.Max nt.ActiveCfg = Release|Win32
- {DA224DAB-5006-42BE-BB77-16E8BE5326D5}.Max nt.Build.0 = Release|Win32
{DA224DAB-5006-42BE-BB77-16E8BE5326D5}.nt.ActiveCfg = Release|Win32
{DA224DAB-5006-42BE-BB77-16E8BE5326D5}.nt.Build.0 = Release|Win32
{DA224DAB-5006-42BE-BB77-16E8BE5326D5}.pro.ActiveCfg = Release|Win32
diff --git a/VC++Files/mysqlserver/dummy.cpp b/VC++Files/mysqlserver/dummy.cpp
new file mode 100755
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/VC++Files/mysqlserver/dummy.cpp
diff --git a/VC++Files/mysqlserver/mysqlserver.vcproj b/VC++Files/mysqlserver/mysqlserver.vcproj
index 2c587e0a2d7..43988b8489c 100755
--- a/VC++Files/mysqlserver/mysqlserver.vcproj
+++ b/VC++Files/mysqlserver/mysqlserver.vcproj
@@ -12,7 +12,7 @@
<Configurations>
<Configuration
Name="Debug|Win32"
- OutputDirectory=".\Debug"
+ OutputDirectory="..\lib_debug"
IntermediateDirectory=".\Debug"
ConfigurationType="4"
UseOfMFC="0"
@@ -38,7 +38,7 @@
Name="VCCustomBuildTool"/>
<Tool
Name="VCLibrarianTool"
- OutputFile=".\Debug\mysqlserver.lib"
+ OutputFile="$(OutDir)\mysqlserver.lib"
SuppressStartupBanner="TRUE"/>
<Tool
Name="VCMIDLTool"/>
@@ -63,7 +63,7 @@
</Configuration>
<Configuration
Name="Release|Win32"
- OutputDirectory=".\release"
+ OutputDirectory="..\lib_release"
IntermediateDirectory=".\release"
ConfigurationType="4"
UseOfMFC="0"
@@ -90,7 +90,7 @@
Name="VCCustomBuildTool"/>
<Tool
Name="VCLibrarianTool"
- OutputFile=".\release\mysqlserver.lib"
+ OutputFile="$(OutDir)\mysqlserver.lib"
SuppressStartupBanner="TRUE"/>
<Tool
Name="VCMIDLTool"/>
@@ -117,6 +117,9 @@
<References>
</References>
<Files>
+ <File
+ RelativePath=".\dummy.cpp">
+ </File>
</Files>
<Globals>
</Globals>
diff --git a/VC++Files/sql/mysqld.vcproj b/VC++Files/sql/mysqld.vcproj
index db84f081ac8..26d23022a50 100755
--- a/VC++Files/sql/mysqld.vcproj
+++ b/VC++Files/sql/mysqld.vcproj
@@ -4578,7 +4578,7 @@
</FileConfiguration>
</File>
<File
- RelativePath="protocol_cursor.cpp">
+ RelativePath="sql_cursor.cpp">
<FileConfiguration
Name="classic nt|Win32">
<Tool
diff --git a/VC++Files/tests/mysql_client_test.vcproj b/VC++Files/tests/mysql_client_test.vcproj
index adcc680b6e7..89a9b71e60a 100755
--- a/VC++Files/tests/mysql_client_test.vcproj
+++ b/VC++Files/tests/mysql_client_test.vcproj
@@ -25,7 +25,7 @@
AdditionalIncludeDirectories="../include,../"
PreprocessorDefinitions="DBUG_OFF;_WINDOWS;SAFE_MUTEX;USE_TLS;MYSQL_CLIENT;__WIN__;_WIN32"
StringPooling="TRUE"
- RuntimeLibrary="1"
+ RuntimeLibrary="0"
EnableFunctionLevelLinking="TRUE"
PrecompiledHeaderFile=".\Release/mysql_client_test.pch"
AssemblerListingLocation=".\Release/"
@@ -38,7 +38,7 @@
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
- AdditionalDependencies="odbc32.lib odbccp32.lib Ws2_32.lib mysqlclient.lib mysys.lib regex.lib ..\extra\yassl\Release\yassl.lib"
+ AdditionalDependencies="odbc32.lib odbccp32.lib Ws2_32.lib"
OutputFile="..\client_release\mysql_client_test.exe"
LinkIncremental="1"
SuppressStartupBanner="TRUE"
@@ -98,7 +98,7 @@
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
- AdditionalDependencies="odbc32.lib odbccp32.lib mysqlclient.lib wsock32.lib mysys.lib regex.lib ..\extra\yassl\Debug\yassl.lib"
+ AdditionalDependencies="odbc32.lib odbccp32.lib wsock32.lib"
OutputFile="..\client_debug\mysql_client_test.exe"
LinkIncremental="1"
SuppressStartupBanner="TRUE"
diff --git a/include/my_sys.h b/include/my_sys.h
index 2970f68805b..e21af78fcaa 100644
--- a/include/my_sys.h
+++ b/include/my_sys.h
@@ -775,6 +775,7 @@ extern void my_free_lock(byte *ptr,myf flags);
extern void init_alloc_root(MEM_ROOT *mem_root, uint block_size,
uint pre_alloc_size);
extern gptr alloc_root(MEM_ROOT *mem_root,unsigned int Size);
+extern gptr multi_alloc_root(MEM_ROOT *mem_root, ...);
extern void free_root(MEM_ROOT *root, myf MyFLAGS);
extern void set_prealloc_root(MEM_ROOT *root, char *ptr);
extern void reset_root_defaults(MEM_ROOT *mem_root, uint block_size,
diff --git a/libmysqld/Makefile.am b/libmysqld/Makefile.am
index 9aef03f20d2..1f5c707f538 100644
--- a/libmysqld/Makefile.am
+++ b/libmysqld/Makefile.am
@@ -60,7 +60,7 @@ sqlsources = derror.cc field.cc field_conv.cc strfunc.cc filesort.cc \
sql_string.cc sql_table.cc sql_test.cc sql_udf.cc \
sql_update.cc sql_yacc.cc table.cc thr_malloc.cc time.cc \
unireg.cc uniques.cc stacktrace.c sql_union.cc hash_filo.cc \
- spatial.cc gstream.cc sql_help.cc tztime.cc protocol_cursor.cc \
+ spatial.cc gstream.cc sql_help.cc tztime.cc sql_cursor.cc \
sp_head.cc sp_pcontext.cc sp.cc sp_cache.cc sp_rcontext.cc \
parse_file.cc sql_view.cc sql_trigger.cc my_decimal.cc \
ha_blackhole.cc
diff --git a/mysql-test/r/ctype_ujis.result b/mysql-test/r/ctype_ujis.result
index 0bc101d491d..0fee6fc3456 100644
--- a/mysql-test/r/ctype_ujis.result
+++ b/mysql-test/r/ctype_ujis.result
@@ -2271,3 +2271,33 @@ select c1 from t1 where c1 like 'abcde111%' order by c1;
c1
abcde111
drop table t1;
+DROP TABLE IF EXISTS t1, t2;
+DROP PROCEDURE IF EXISTS sp1;
+set names ujis;
+set character_set_database = ujis;
+set character_set_server = ujis;
+CREATE TABLE t1(c1 char(2)) default charset = ujis;
+CREATE TABLE t2(c2 char(2)) default charset = ujis;
+INSERT INTO t1 VALUES(_ujis 0xA4A2);
+CREATE PROCEDURE sp1()
+BEGIN
+DECLARE a CHAR(1);
+DECLARE cur1 CURSOR FOR SELECT c1 FROM t1;
+OPEN cur1;
+FETCH cur1 INTO a;
+INSERT INTO t2 VALUES (a);
+CLOSE cur1;
+END|
+CALL sp1();
+SELECT c1,c2 FROM t1,t2;
+c1 c2
+¤¢ ¤¢
+SELECT hex(convert(_latin1 0xA4A2 using ujis)),hex(c2) FROM t1,t2;
+hex(convert(_latin1 0xA4A2 using ujis)) hex(c2)
+8FA2F0A1F1 A4A2
+DROP PROCEDURE sp1;
+DROP TABLE t1;
+DROP TABLE t2;
+set names default;
+set character_set_database=default;
+set character_set_server=default;
diff --git a/mysql-test/r/information_schema.result b/mysql-test/r/information_schema.result
index 1407c17f623..0d53180b6d6 100644
--- a/mysql-test/r/information_schema.result
+++ b/mysql-test/r/information_schema.result
@@ -1,4 +1,5 @@
-DROP TABLE IF EXISTS t0,t1,t2,t3,t5;
+DROP TABLE IF EXISTS t0,t1,t2,t3,t4,t5;
+DROP VIEW IF EXISTS v1;
show variables where variable_name like "skip_show_database";
Variable_name Value
skip_show_database OFF
@@ -638,8 +639,8 @@ use test;
create function sub1(i int) returns int
return i+1;
create table t1(f1 int);
-create view t2 (c) as select f1 from t1;
-create view t3 (c) as select sub1(1);
+create view v2 (c) as select f1 from t1;
+create view v3 (c) as select sub1(1);
create table t4(f1 int, KEY f1_key (f1));
drop table t1;
drop function sub1;
@@ -647,29 +648,29 @@ select table_name from information_schema.views
where table_schema='test';
table_name
Warnings:
-Warning 1356 View 'test.t2' references invalid table(s) or column(s) or function(s)
-Warning 1356 View 'test.t3' references invalid table(s) or column(s) or function(s)
+Warning 1356 View 'test.v2' references invalid table(s) or column(s) or function(s)
+Warning 1356 View 'test.v3' references invalid table(s) or column(s) or function(s)
select table_name from information_schema.views
where table_schema='test';
table_name
Warnings:
-Warning 1356 View 'test.t2' references invalid table(s) or column(s) or function(s)
-Warning 1356 View 'test.t3' references invalid table(s) or column(s) or function(s)
+Warning 1356 View 'test.v2' references invalid table(s) or column(s) or function(s)
+Warning 1356 View 'test.v3' references invalid table(s) or column(s) or function(s)
select column_name from information_schema.columns
where table_schema='test';
column_name
f1
Warnings:
-Warning 1356 View 'test.t2' references invalid table(s) or column(s) or function(s)
-Warning 1356 View 'test.t3' references invalid table(s) or column(s) or function(s)
+Warning 1356 View 'test.v2' references invalid table(s) or column(s) or function(s)
+Warning 1356 View 'test.v3' references invalid table(s) or column(s) or function(s)
select index_name from information_schema.statistics where table_schema='test';
index_name
f1_key
select constraint_name from information_schema.table_constraints
where table_schema='test';
constraint_name
-drop view t2;
-drop view t3;
+drop view v2;
+drop view v3;
drop table t4;
select * from information_schema.table_names;
ERROR 42S02: Unknown table 'table_names' in information_schema
diff --git a/mysql-test/r/information_schema_inno.result b/mysql-test/r/information_schema_inno.result
index 9dd92baf62f..fb6584673f6 100644
--- a/mysql-test/r/information_schema_inno.result
+++ b/mysql-test/r/information_schema_inno.result
@@ -1,4 +1,4 @@
-DROP TABLE IF EXISTS t1,t2;
+DROP TABLE IF EXISTS t1,t2,t3;
CREATE TABLE t1 (id INT NOT NULL, PRIMARY KEY (id)) ENGINE=INNODB;
CREATE TABLE t2 (id INT PRIMARY KEY, t1_id INT, INDEX par_ind (t1_id, id),
FOREIGN KEY (t1_id) REFERENCES t1(id) ON DELETE CASCADE,
diff --git a/mysql-test/r/multi_statement.result b/mysql-test/r/multi_statement.result
index 3a8d86bf349..ff19cbdd698 100644
--- a/mysql-test/r/multi_statement.result
+++ b/mysql-test/r/multi_statement.result
@@ -1,3 +1,4 @@
+DROP TABLE IF EXISTS t1;
select 1;
1
1
diff --git a/mysql-test/r/sp-big.result b/mysql-test/r/sp-big.result
index 004ff586aab..1f0b6b34651 100644
--- a/mysql-test/r/sp-big.result
+++ b/mysql-test/r/sp-big.result
@@ -13,3 +13,49 @@ select @value;
3
drop procedure test.longprocedure;
drop table t1;
+create table t1 (f1 char(100) , f2 mediumint , f3 int , f4 real, f5 numeric);
+insert into t1 (f1, f2, f3, f4, f5) values
+("This is a test case for for Bug#9819", 1, 2, 3.0, 4.598);
+Warnings:
+Note 1265 Data truncated for column 'f5' at row 1
+create table t2 like t1;
+select count(*) from t1;
+count(*)
+256
+select count(*) from t2;
+count(*)
+0
+create procedure p1()
+begin
+declare done integer default 0;
+declare vf1 char(100) ;
+declare vf2 mediumint;
+declare vf3 int ;
+declare vf4 real ;
+declare vf5 numeric ;
+declare cur1 cursor for select f1,f2,f3,f4,f5 from t1;
+declare continue handler for sqlstate '02000' set done = 1;
+open cur1;
+while done <> 1 do
+fetch cur1 into vf1, vf2, vf3, vf4, vf5;
+if not done then
+insert into t2 values (vf1, vf2, vf3, vf4, vf5);
+end if;
+end while;
+close cur1;
+end|
+call p1();
+select count(*) from t1;
+count(*)
+256
+select count(*) from t2;
+count(*)
+256
+select f1 from t1 limit 1;
+f1
+This is a test case for for Bug#9819
+select f1 from t2 limit 1;
+f1
+This is a test case for for Bug#9819
+drop procedure p1;
+drop table t1, t2;
diff --git a/mysql-test/r/temp_table.result b/mysql-test/r/temp_table.result
index 726861bb525..82479504b10 100644
--- a/mysql-test/r/temp_table.result
+++ b/mysql-test/r/temp_table.result
@@ -1,4 +1,5 @@
drop table if exists t1,t2;
+drop view if exists v1;
CREATE TABLE t1 (c int not null, d char (10) not null);
insert into t1 values(1,""),(2,"a"),(3,"b");
CREATE TEMPORARY TABLE t1 (a int not null, b char (10) not null);
@@ -99,32 +100,32 @@ Variable_name Value
Created_tmp_disk_tables 0
Created_tmp_tables 2
drop table t1;
-create temporary table t1 as select 'This is temp. table' A;
-create view t1 as select 'This is view' A;
-select * from t1;
+create temporary table v1 as select 'This is temp. table' A;
+create view v1 as select 'This is view' A;
+select * from v1;
A
This is temp. table
-show create table t1;
+show create table v1;
Table Create Table
-t1 CREATE TEMPORARY TABLE `t1` (
+v1 CREATE TEMPORARY TABLE `v1` (
`A` varchar(19) NOT NULL default ''
) ENGINE=MyISAM DEFAULT CHARSET=latin1
-show create view t1;
+show create view v1;
View Create View
-t1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `t1` AS select _latin1'This is view' AS `A`
-drop view t1;
-select * from t1;
+v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select _latin1'This is view' AS `A`
+drop view v1;
+select * from v1;
A
This is temp. table
-create view t1 as select 'This is view again' A;
-select * from t1;
+create view v1 as select 'This is view again' A;
+select * from v1;
A
This is temp. table
-drop table t1;
-select * from t1;
+drop table v1;
+select * from v1;
A
This is view again
-drop view t1;
+drop view v1;
create table t1 (a int, b int, index(a), index(b));
create table t2 (c int auto_increment, d varchar(255), primary key (c));
insert into t1 values (3,1),(3,2);
diff --git a/mysql-test/r/type_bit.result b/mysql-test/r/type_bit.result
index 5988b4f745e..0213dbaffde 100644
--- a/mysql-test/r/type_bit.result
+++ b/mysql-test/r/type_bit.result
@@ -553,3 +553,13 @@ sum(a1) b1+0 b2+0
2 0 0
4 2 2
8 1 1
+select 1 from t1 join t2 on b1 = b2 group by b1 order by 1;
+1
+1
+1
+1
+select b1+0,sum(b1), sum(b2) from t1 join t2 on b1 = b2 group by b1 order by 1;
+b1+0 sum(b1) sum(b2)
+0 0 0
+1 4 4
+2 2 2
diff --git a/mysql-test/t/ctype_ujis.test b/mysql-test/t/ctype_ujis.test
index 88386500c9f..7730fd0db6d 100644
--- a/mysql-test/t/ctype_ujis.test
+++ b/mysql-test/t/ctype_ujis.test
@@ -1151,3 +1151,45 @@ SET collation_connection='ujis_bin';
-- source include/ctype_innodb_like.inc
# End of 4.1 tests
+--disable_warnings
+DROP TABLE IF EXISTS t1, t2;
+DROP PROCEDURE IF EXISTS sp1;
+--enable_warnings
+
+set names ujis;
+set character_set_database = ujis;
+set character_set_server = ujis;
+
+CREATE TABLE t1(c1 char(2)) default charset = ujis;
+CREATE TABLE t2(c2 char(2)) default charset = ujis;
+
+INSERT INTO t1 VALUES(_ujis 0xA4A2);
+
+DELIMITER |;
+CREATE PROCEDURE sp1()
+BEGIN
+ DECLARE a CHAR(1);
+ DECLARE cur1 CURSOR FOR SELECT c1 FROM t1;
+ OPEN cur1;
+ FETCH cur1 INTO a;
+ INSERT INTO t2 VALUES (a);
+ CLOSE cur1;
+END|
+DELIMITER ;|
+CALL sp1();
+
+#The data in t1 and t2 should be the same but different
+SELECT c1,c2 FROM t1,t2;
+
+#Since the result of hex(convert(_latin1 0xA4A2 using ujis))
+#equals to hex(c2), it seems that the value which was inserted
+#by using cursor is interpreted as latin1 character set
+SELECT hex(convert(_latin1 0xA4A2 using ujis)),hex(c2) FROM t1,t2;
+
+DROP PROCEDURE sp1;
+DROP TABLE t1;
+DROP TABLE t2;
+
+set names default;
+set character_set_database=default;
+set character_set_server=default;
diff --git a/mysql-test/t/information_schema.test b/mysql-test/t/information_schema.test
index 51cca0a3db1..f351d315680 100644
--- a/mysql-test/t/information_schema.test
+++ b/mysql-test/t/information_schema.test
@@ -5,7 +5,8 @@
# show databases
--disable_warnings
-DROP TABLE IF EXISTS t0,t1,t2,t3,t5;
+DROP TABLE IF EXISTS t0,t1,t2,t3,t4,t5;
+DROP VIEW IF EXISTS v1;
--enable_warnings
@@ -364,8 +365,8 @@ use test;
create function sub1(i int) returns int
return i+1;
create table t1(f1 int);
-create view t2 (c) as select f1 from t1;
-create view t3 (c) as select sub1(1);
+create view v2 (c) as select f1 from t1;
+create view v3 (c) as select sub1(1);
create table t4(f1 int, KEY f1_key (f1));
drop table t1;
drop function sub1;
@@ -378,8 +379,8 @@ where table_schema='test';
select index_name from information_schema.statistics where table_schema='test';
select constraint_name from information_schema.table_constraints
where table_schema='test';
-drop view t2;
-drop view t3;
+drop view v2;
+drop view v3;
drop table t4;
#
diff --git a/mysql-test/t/information_schema_inno.test b/mysql-test/t/information_schema_inno.test
index dd7015bfd9a..9cd64a54ad9 100644
--- a/mysql-test/t/information_schema_inno.test
+++ b/mysql-test/t/information_schema_inno.test
@@ -1,6 +1,6 @@
-- source include/have_innodb.inc
--disable_warnings
-DROP TABLE IF EXISTS t1,t2;
+DROP TABLE IF EXISTS t1,t2,t3;
--enable_warnings
#
diff --git a/mysql-test/t/multi_statement.test b/mysql-test/t/multi_statement.test
index eb8d867f3f0..785aa749f5e 100644
--- a/mysql-test/t/multi_statement.test
+++ b/mysql-test/t/multi_statement.test
@@ -1,6 +1,10 @@
# PS doesn't support multi-statements
--disable_ps_protocol
+--disable_warnings
+DROP TABLE IF EXISTS t1;
+--enable_warnings
+
select 1;
delimiter ||||;
select 2;
diff --git a/mysql-test/t/sp-big.test b/mysql-test/t/sp-big.test
index 769d77dbef9..389a6f04504 100644
--- a/mysql-test/t/sp-big.test
+++ b/mysql-test/t/sp-big.test
@@ -31,3 +31,52 @@ call test.longprocedure(@value); select @value;
drop procedure test.longprocedure;
drop table t1;
+#
+# Bug #9819 "Cursors: Mysql Server Crash while fetching from table with 5
+# million records.":
+# To really test the bug, increase the number of loop iterations ($1).
+# For 4 millions set $1 to 22.
+create table t1 (f1 char(100) , f2 mediumint , f3 int , f4 real, f5 numeric);
+insert into t1 (f1, f2, f3, f4, f5) values
+("This is a test case for for Bug#9819", 1, 2, 3.0, 4.598);
+create table t2 like t1;
+let $1=8;
+--disable_query_log
+--disable_result_log
+while ($1)
+{
+ eval insert into t1 select * from t1;
+ dec $1;
+}
+--enable_result_log
+--enable_query_log
+select count(*) from t1;
+select count(*) from t2;
+delimiter |;
+create procedure p1()
+begin
+ declare done integer default 0;
+ declare vf1 char(100) ;
+ declare vf2 mediumint;
+ declare vf3 int ;
+ declare vf4 real ;
+ declare vf5 numeric ;
+ declare cur1 cursor for select f1,f2,f3,f4,f5 from t1;
+ declare continue handler for sqlstate '02000' set done = 1;
+ open cur1;
+ while done <> 1 do
+ fetch cur1 into vf1, vf2, vf3, vf4, vf5;
+ if not done then
+ insert into t2 values (vf1, vf2, vf3, vf4, vf5);
+ end if;
+ end while;
+ close cur1;
+end|
+delimiter ;|
+call p1();
+select count(*) from t1;
+select count(*) from t2;
+select f1 from t1 limit 1;
+select f1 from t2 limit 1;
+drop procedure p1;
+drop table t1, t2;
diff --git a/mysql-test/t/temp_table.test b/mysql-test/t/temp_table.test
index 9a7678ed712..6b3991c9c78 100644
--- a/mysql-test/t/temp_table.test
+++ b/mysql-test/t/temp_table.test
@@ -4,6 +4,7 @@
--disable_warnings
drop table if exists t1,t2;
+drop view if exists v1;
--enable_warnings
CREATE TABLE t1 (c int not null, d char (10) not null);
@@ -91,18 +92,18 @@ show status like "created_tmp%tables";
drop table t1;
# Fix for BUG#8921: Check that temporary table is ingored by view commands.
-create temporary table t1 as select 'This is temp. table' A;
-create view t1 as select 'This is view' A;
-select * from t1;
-show create table t1;
-show create view t1;
-drop view t1;
-select * from t1;
-create view t1 as select 'This is view again' A;
-select * from t1;
-drop table t1;
-select * from t1;
-drop view t1;
+create temporary table v1 as select 'This is temp. table' A;
+create view v1 as select 'This is view' A;
+select * from v1;
+show create table v1;
+show create view v1;
+drop view v1;
+select * from v1;
+create view v1 as select 'This is view again' A;
+select * from v1;
+drop table v1;
+select * from v1;
+drop view v1;
# Bug #8497: tmpdir with extra slashes would cause failures
#
diff --git a/mysql-test/t/type_bit.test b/mysql-test/t/type_bit.test
index 6906cfc2808..0c45dea21bb 100644
--- a/mysql-test/t/type_bit.test
+++ b/mysql-test/t/type_bit.test
@@ -224,3 +224,5 @@ select a1, a2, b1+0, b2+0 from t1 join t2 on a1 = a2;
select a1, a2, b1+0, b2+0 from t1 join t2 on a1 = a2 order by a1;
select a1, a2, b1+0, b2+0 from t1 join t2 on b1 = b2;
select sum(a1), b1+0, b2+0 from t1 join t2 on b1 = b2 group by b1 order by 1;
+select 1 from t1 join t2 on b1 = b2 group by b1 order by 1;
+select b1+0,sum(b1), sum(b2) from t1 join t2 on b1 = b2 group by b1 order by 1;
diff --git a/mysql-test/valgrind.supp b/mysql-test/valgrind.supp
index 26f3477140b..7737810653d 100644
--- a/mysql-test/valgrind.supp
+++ b/mysql-test/valgrind.supp
@@ -52,6 +52,16 @@
}
{
+ pthread pthread_key_create
+ Memcheck:Leak
+ fun:malloc
+ fun:*
+ fun:*
+ fun:pthread_key_create
+ fun:my_thread_global_init
+}
+
+{
pthread strstr uninit
Memcheck:Cond
fun:strstr
@@ -127,8 +137,18 @@
{
libz deflate
Memcheck:Cond
- obj:/usr/lib/libz.so.*
- obj:/usr/lib/libz.so.*
+ obj:*/libz.so.*
+ obj:*/libz.so.*
fun:deflate
fun:compress2
}
+
+{
+ libz deflate2
+ Memcheck:Cond
+ obj:*/libz.so.*
+ obj:*/libz.so.*
+ fun:deflate
+ obj:*/libz.so.*
+ fun:gzflush
+}
diff --git a/mysys/my_alloc.c b/mysys/my_alloc.c
index fd5a4908572..d5346d530c3 100644
--- a/mysys/my_alloc.c
+++ b/mysys/my_alloc.c
@@ -221,6 +221,57 @@ gptr alloc_root(MEM_ROOT *mem_root,unsigned int Size)
#endif
}
+
+/*
+ Allocate many pointers at the same time.
+
+ DESCRIPTION
+ ptr1, ptr2, etc all point into big allocated memory area.
+
+ SYNOPSIS
+ multi_alloc_root()
+ root Memory root
+ ptr1, length1 Multiple arguments terminated by a NULL pointer
+ ptr2, length2 ...
+ ...
+ NULL
+
+ RETURN VALUE
+ A pointer to the beginning of the allocated memory block
+ in case of success or NULL if out of memory.
+*/
+
+gptr multi_alloc_root(MEM_ROOT *root, ...)
+{
+ va_list args;
+ char **ptr, *start, *res;
+ uint tot_length, length;
+ DBUG_ENTER("multi_alloc_root");
+
+ va_start(args, root);
+ tot_length= 0;
+ while ((ptr= va_arg(args, char **)))
+ {
+ length= va_arg(args, uint);
+ tot_length+= ALIGN_SIZE(length);
+ }
+ va_end(args);
+
+ if (!(start= (char*) alloc_root(root, tot_length)))
+ DBUG_RETURN(0); /* purecov: inspected */
+
+ va_start(args, root);
+ res= start;
+ while ((ptr= va_arg(args, char **)))
+ {
+ *ptr= res;
+ length= va_arg(args, uint);
+ res+= ALIGN_SIZE(length);
+ }
+ va_end(args);
+ DBUG_RETURN((gptr) start);
+}
+
#define TRASH_MEM(X) TRASH(((char*)(X) + ((X)->size-(X)->left)), (X)->left)
/* Mark all data in blocks free for reusage */
diff --git a/scripts/make_binary_distribution.sh b/scripts/make_binary_distribution.sh
index fbdbd998138..9c2ae9c42f2 100644
--- a/scripts/make_binary_distribution.sh
+++ b/scripts/make_binary_distribution.sh
@@ -169,7 +169,8 @@ for i in \
libmysql_r/.libs/libmysqlclient_r.so* libmysql_r/libmysqlclient_r.* \
mysys/libmysys.a strings/libmystrings.a dbug/libdbug.a \
libmysqld/.libs/libmysqld.a libmysqld/.libs/libmysqld.so* \
- libmysqld/libmysqld.a netware/libmysql.imp
+ libmysqld/libmysqld.a netware/libmysql.imp \
+ zlib/.libs/libz.a
do
if [ -f $i ]
then
diff --git a/server-tools/instance-manager/mysqlmanager.vcproj b/server-tools/instance-manager/mysqlmanager.vcproj
index a5ef7cb21e3..ef8b2dd017e 100644
--- a/server-tools/instance-manager/mysqlmanager.vcproj
+++ b/server-tools/instance-manager/mysqlmanager.vcproj
@@ -12,7 +12,7 @@
<Configurations>
<Configuration
Name="Debug|Win32"
- OutputDirectory="Debug"
+ OutputDirectory="../../client_debug"
IntermediateDirectory="Debug"
ConfigurationType="1"
CharacterSet="2">
@@ -63,7 +63,7 @@
</Configuration>
<Configuration
Name="Release|Win32"
- OutputDirectory="Release"
+ OutputDirectory="../../client_release"
IntermediateDirectory="Release"
ConfigurationType="1"
CharacterSet="2">
diff --git a/sql/Makefile.am b/sql/Makefile.am
index 4824a75d6fa..60c485d79f9 100644
--- a/sql/Makefile.am
+++ b/sql/Makefile.am
@@ -61,7 +61,7 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \
tztime.h my_decimal.h\
sp_head.h sp_pcontext.h sp_rcontext.h sp.h sp_cache.h \
parse_file.h sql_view.h sql_trigger.h \
- sql_array.h \
+ sql_array.h sql_cursor.h \
examples/ha_example.h examples/ha_archive.h \
examples/ha_tina.h ha_blackhole.h \
ha_federated.h
@@ -94,7 +94,7 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc \
client.c sql_client.cc mini_client_errors.c pack.c\
stacktrace.c repl_failsafe.h repl_failsafe.cc \
sql_olap.cc sql_view.cc \
- gstream.cc spatial.cc sql_help.cc protocol_cursor.cc \
+ gstream.cc spatial.cc sql_help.cc sql_cursor.cc \
tztime.cc my_time.c my_decimal.cc\
sp_head.cc sp_pcontext.cc sp_rcontext.cc sp.cc \
sp_cache.cc parse_file.cc sql_trigger.cc \
diff --git a/sql/ha_federated.cc b/sql/ha_federated.cc
index d83b047d75a..9a8b5eb794d 100644
--- a/sql/ha_federated.cc
+++ b/sql/ha_federated.cc
@@ -588,12 +588,15 @@ static int parse_url(FEDERATED_SHARE *share, TABLE *table,
DBUG_ENTER("ha_federated::parse_url");
share->port= 0;
+ share->socket= 0;
DBUG_PRINT("info", ("Length %d \n", table->s->connect_string.length));
DBUG_PRINT("info", ("String %.*s \n", table->s->connect_string.length,
table->s->connect_string.str));
- share->scheme= my_strdup_with_length(table->s->connect_string.str,
- table->s->connect_string.length+1,
- MYF(0));
+ share->scheme= my_strdup_with_length((const byte*)table->s->
+ connect_string.str,
+ table->s->connect_string.length,
+ MYF(0));
+
// Add a null for later termination of table name
share->scheme[table->s->connect_string.length]= 0;
DBUG_PRINT("info",("parse_url alloced share->scheme %lx", share->scheme));
@@ -1374,13 +1377,9 @@ static int free_share(FEDERATED_SHARE *share)
if (!--share->use_count)
{
- if (share->scheme)
- {
- my_free((gptr) share->scheme, MYF(0));
- share->scheme= 0;
- }
-
hash_delete(&federated_open_tables, (byte*) share);
+ my_free((gptr) share->scheme, MYF(MY_ALLOW_ZERO_PTR));
+ share->scheme= 0;
thr_lock_delete(&share->lock);
VOID(pthread_mutex_destroy(&share->mutex));
my_free((gptr) share, MYF(0));
@@ -2626,7 +2625,8 @@ int ha_federated::stash_remote_error()
{
DBUG_ENTER("ha_federated::stash_remote_error()");
remote_error_number= mysql_errno(mysql);
- snprintf(remote_error_buf, FEDERATED_QUERY_BUFFER_SIZE, mysql_error(mysql));
+ my_snprintf(remote_error_buf, FEDERATED_QUERY_BUFFER_SIZE,
+ mysql_error(mysql));
DBUG_RETURN(HA_FEDERATED_ERROR_WITH_REMOTE_SYSTEM);
}
diff --git a/sql/handler.cc b/sql/handler.cc
index 5f8fa33fecc..809ed188d59 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -1456,11 +1456,9 @@ int handler::ha_open(const char *name, int mode, int test_if_locked)
table->db_stat|=HA_READ_ONLY;
(void) extra(HA_EXTRA_NO_READCHECK); // Not needed in SQL
- if (!alloc_root_inited(&table->mem_root)) // If temporary table
- ref=(byte*) sql_alloc(ALIGN_SIZE(ref_length)*2);
- else
- ref=(byte*) alloc_root(&table->mem_root, ALIGN_SIZE(ref_length)*2);
- if (!ref)
+ DBUG_ASSERT(alloc_root_inited(&table->mem_root));
+
+ if (!(ref= (byte*) alloc_root(&table->mem_root, ALIGN_SIZE(ref_length)*2)))
{
close();
error=HA_ERR_OUT_OF_MEM;
diff --git a/sql/item_func.h b/sql/item_func.h
index de4d60e9cbb..223144a5d51 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -1286,9 +1286,6 @@ public:
{
ft_handler->please->close_search(ft_handler);
ft_handler=0;
- if (join_key)
- table->file->ft_handler=0;
- table->fulltext_searched=0;
}
concat= 0;
DBUG_VOID_RETURN;
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index 52a74b6f4c6..1ef3a92f548 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -1468,7 +1468,7 @@ int subselect_single_select_engine::prepare()
int subselect_union_engine::prepare()
{
- return unit->prepare(thd, result, SELECT_NO_UNLOCK, "");
+ return unit->prepare(thd, result, SELECT_NO_UNLOCK);
}
int subselect_uniquesubquery_engine::prepare()
diff --git a/sql/log.cc b/sql/log.cc
index 8010d03b46a..6e372938752 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -2793,7 +2793,7 @@ void TC_LOG_MMAP::close()
case 3:
my_free((gptr)pages, MYF(0));
case 2:
- my_munmap(data, (size_t)file_length);
+ my_munmap((byte*)data, (size_t)file_length);
case 1:
my_close(fd, MYF(0));
}
diff --git a/sql/protocol.h b/sql/protocol.h
index 2717d2258fa..c00bbba4cc9 100644
--- a/sql/protocol.h
+++ b/sql/protocol.h
@@ -150,30 +150,6 @@ public:
virtual bool store(Field *field);
};
-class Protocol_cursor :public Protocol_simple
-{
-public:
- MEM_ROOT *alloc;
- MYSQL_FIELD *fields;
- MYSQL_ROWS *data;
- MYSQL_ROWS **prev_record;
- ulong row_count;
-
- Protocol_cursor() :data(NULL) {}
- Protocol_cursor(THD *thd_arg, MEM_ROOT *ini_alloc) :Protocol_simple(thd_arg), alloc(ini_alloc), data(NULL) {}
- bool prepare_for_send(List<Item> *item_list)
- {
- row_count= 0;
- fields= NULL;
- data= NULL;
- prev_record= &data;
- return Protocol_simple::prepare_for_send(item_list);
- }
- bool send_fields(List<Item> *list, uint flags);
- bool write();
- uint get_field_count() { return field_count; }
-};
-
void send_warning(THD *thd, uint sql_errno, const char *err=0);
void net_printf_error(THD *thd, uint sql_errno, ...);
void net_send_error(THD *thd, uint sql_errno=0, const char *err=0);
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 157672f2a86..36a37924ea4 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -199,11 +199,18 @@ sp_eval_func_item(THD *thd, Item **it_addr, enum enum_field_types type,
Item *it= sp_prepare_func_item(thd, it_addr);
uint rsize;
Query_arena backup_arena;
+ Item *old_item_next, *old_free_list, **p_free_list;
DBUG_PRINT("info", ("type: %d", type));
if (!it)
- {
DBUG_RETURN(NULL);
+
+ if (reuse)
+ {
+ old_item_next= reuse->next;
+ p_free_list= use_callers_arena ? &thd->spcont->callers_arena->free_list :
+ &thd->free_list;
+ old_free_list= *p_free_list;
}
switch (sp_map_result_type(type)) {
@@ -312,15 +319,23 @@ sp_eval_func_item(THD *thd, Item **it_addr, enum enum_field_types type,
default:
DBUG_ASSERT(0);
}
- it->rsize= rsize;
-
- DBUG_RETURN(it);
+ goto end;
return_null_item:
CREATE_ON_CALLERS_ARENA(it= new(reuse, &rsize) Item_null(),
use_callers_arena, &backup_arena);
+end:
it->rsize= rsize;
+ if (reuse && it == reuse)
+ {
+ /*
+ The Item constructor registered itself in the arena free list,
+ while the item slot is reused, so we have to restore the list.
+ */
+ it->next= old_item_next;
+ *p_free_list= old_free_list;
+ }
DBUG_RETURN(it);
}
@@ -1358,14 +1373,6 @@ int sp_head::execute_procedure(THD *thd, List<Item> *args)
uint offset= static_cast<Item_splocal *>(it)->get_offset();
Item *val= nctx->get_item(i);
Item *orig= octx->get_item(offset);
- Item *o_item_next;
- /* we'll use callers_arena in sp_eval_func_item */
- Item *o_free_list= thd->spcont->callers_arena->free_list;
-
- LINT_INIT(o_item_next);
-
- if (orig)
- o_item_next= orig->next;
/*
We might need to allocate new item if we weren't able to
@@ -1380,15 +1387,6 @@ int sp_head::execute_procedure(THD *thd, List<Item> *args)
}
if (copy != orig)
octx->set_item(offset, copy);
- if (orig && copy == orig)
- {
- /*
- A reused item slot, where the constructor put it in the
- free_list, so we have to restore the list.
- */
- thd->spcont->callers_arena->free_list= o_free_list;
- copy->next= o_item_next;
- }
}
else
{
@@ -2478,6 +2476,10 @@ sp_instr_cpop::backpatch(uint dest, sp_pcontext *dst_ctx)
int
sp_instr_copen::execute(THD *thd, uint *nextp)
{
+ /*
+ We don't store a pointer to the cursor in the instruction to be
+ able to reuse the same instruction among different threads in future.
+ */
sp_cursor *c= thd->spcont->get_cursor(m_cursor);
int res;
DBUG_ENTER("sp_instr_copen::execute");
@@ -2486,41 +2488,33 @@ sp_instr_copen::execute(THD *thd, uint *nextp)
res= -1;
else
{
- sp_lex_keeper *lex_keeper= c->pre_open(thd);
- if (!lex_keeper) // cursor already open or OOM
- {
- res= -1;
- *nextp= m_ip+1;
- }
- else
- {
- Query_arena *old_arena= thd->stmt_arena;
+ sp_lex_keeper *lex_keeper= c->get_lex_keeper();
+ Query_arena *old_arena= thd->stmt_arena;
- /*
- Get the Query_arena from the cpush instruction, which contains
- the free_list of the query, so new items (if any) are stored in
- the right free_list, and we can cleanup after each open.
- */
- thd->stmt_arena= c->get_instr();
- res= lex_keeper->reset_lex_and_exec_core(thd, nextp, FALSE, this);
- /* Cleanup the query's items */
- if (thd->stmt_arena->free_list)
- cleanup_items(thd->stmt_arena->free_list);
- thd->stmt_arena= old_arena;
- /*
- Work around the fact that errors in selects are not returned properly
- (but instead converted into a warning), so if a condition handler
- caught, we have lost the result code.
- */
- if (!res)
- {
- uint dummy1, dummy2;
+ /*
+ Get the Query_arena from the cpush instruction, which contains
+ the free_list of the query, so new items (if any) are stored in
+ the right free_list, and we can cleanup after each open.
+ */
+ thd->stmt_arena= c->get_instr();
+ res= lex_keeper->reset_lex_and_exec_core(thd, nextp, FALSE, this);
+ /* Cleanup the query's items */
+ if (thd->stmt_arena->free_list)
+ cleanup_items(thd->stmt_arena->free_list);
+ thd->stmt_arena= old_arena;
+ /*
+ Work around the fact that errors in selects are not returned properly
+ (but instead converted into a warning), so if a condition handler
+ caught, we have lost the result code.
+ */
+ if (!res)
+ {
+ uint dummy1, dummy2;
- if (thd->spcont->found_handler(&dummy1, &dummy2))
- res= -1;
- }
- c->post_open(thd, res ? FALSE : TRUE);
+ if (thd->spcont->found_handler(&dummy1, &dummy2))
+ res= -1;
}
+ /* TODO: Assert here that we either have an error or a cursor */
}
DBUG_RETURN(res);
}
@@ -2529,7 +2523,8 @@ sp_instr_copen::execute(THD *thd, uint *nextp)
int
sp_instr_copen::exec_core(THD *thd, uint *nextp)
{
- int res= mysql_execute_command(thd);
+ sp_cursor *c= thd->spcont->get_cursor(m_cursor);
+ int res= c->open(thd);
*nextp= m_ip+1;
return res;
}
@@ -2584,14 +2579,7 @@ sp_instr_cfetch::execute(THD *thd, uint *nextp)
Query_arena backup_arena;
DBUG_ENTER("sp_instr_cfetch::execute");
- if (! c)
- res= -1;
- else
- {
- thd->set_n_backup_active_arena(thd->spcont->callers_arena, &backup_arena);
- res= c->fetch(thd, &m_varlist);
- thd->restore_active_arena(thd->spcont->callers_arena, &backup_arena);
- }
+ res= c ? c->fetch(thd, &m_varlist) : -1;
*nextp= m_ip+1;
DBUG_RETURN(res);
diff --git a/sql/sp_head.h b/sql/sp_head.h
index 59169dc35cd..ed0f3987e01 100644
--- a/sql/sp_head.h
+++ b/sql/sp_head.h
@@ -866,6 +866,12 @@ public:
virtual void print(String *str);
+ /*
+ This call is used to cleanup the instruction when a sensitive
+ cursor is closed. For now stored procedures always use materialized
+ cursors and the call is not used.
+ */
+ virtual void cleanup_stmt() { /* no op */ }
private:
sp_lex_keeper m_lex_keeper;
@@ -1036,4 +1042,7 @@ sp_add_to_query_tables(THD *thd, LEX *lex,
const char *db, const char *name,
thr_lock_type locktype);
+Item *sp_eval_func_item(THD *thd, Item **it, enum_field_types type,
+ Item *reuse, bool use_callers_arena);
+
#endif /* _SP_HEAD_H_ */
diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc
index 748c09f56c7..fcb7719aeb1 100644
--- a/sql/sp_rcontext.cc
+++ b/sql/sp_rcontext.cc
@@ -25,6 +25,7 @@
#include "mysql.h"
#include "sp_head.h"
+#include "sql_cursor.h"
#include "sp_rcontext.h"
#include "sp_pcontext.h"
@@ -45,31 +46,18 @@ int
sp_rcontext::set_item_eval(THD *thd, uint idx, Item **item_addr,
enum_field_types type)
{
- extern Item *sp_eval_func_item(THD *thd, Item **it, enum_field_types type,
- Item *reuse, bool use_callers_arena);
Item *it;
Item *reuse_it;
- Item *old_item_next;
/* sp_eval_func_item will use callers_arena */
- Item *old_free_list= thd->spcont->callers_arena->free_list;
int res;
- LINT_INIT(old_item_next);
- if ((reuse_it= get_item(idx)))
- old_item_next= reuse_it->next;
+ reuse_it= get_item(idx);
it= sp_eval_func_item(thd, item_addr, type, reuse_it, TRUE);
if (! it)
res= -1;
else
{
res= 0;
- if (reuse_it && it == reuse_it)
- {
- // A reused item slot, where the constructor put it in the free_list,
- // so we have to restore the list.
- thd->spcont->callers_arena->free_list= old_free_list;
- it->next= old_item_next;
- }
set_item(idx, it);
}
@@ -170,7 +158,8 @@ sp_rcontext::pop_cursors(uint count)
*/
sp_cursor::sp_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i)
- :m_lex_keeper(lex_keeper), m_prot(NULL), m_isopen(0), m_current_row(NULL),
+ :m_lex_keeper(lex_keeper),
+ server_side_cursor(NULL),
m_i(i)
{
/*
@@ -182,59 +171,37 @@ sp_cursor::sp_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i)
/*
- pre_open cursor
+ Open an SP cursor
SYNOPSIS
- pre_open()
- THD Thread handler
+ open()
+ THD Thread handler
- NOTES
- We have to open cursor in two steps to make it easy for sp_instr_copen
- to reuse the sp_instr::exec_stmt() code.
- If this function returns 0, post_open should not be called
RETURN
- 0 ERROR
+ 0 in case of success, -1 otherwise
*/
-sp_lex_keeper*
-sp_cursor::pre_open(THD *thd)
+int
+sp_cursor::open(THD *thd)
{
- if (m_isopen)
+ if (server_side_cursor)
{
my_message(ER_SP_CURSOR_ALREADY_OPEN, ER(ER_SP_CURSOR_ALREADY_OPEN),
MYF(0));
- return NULL;
+ return -1;
}
- init_alloc_root(&m_mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC);
- if ((m_prot= new Protocol_cursor(thd, &m_mem_root)) == NULL)
- return NULL;
-
- /* Save for execution. Will be restored in post_open */
- m_oprot= thd->protocol;
- m_nseof= thd->net.no_send_eof;
-
- /* Change protocol for execution */
- thd->protocol= m_prot;
- thd->net.no_send_eof= TRUE;
- return m_lex_keeper;
-}
-
-
-void
-sp_cursor::post_open(THD *thd, my_bool was_opened)
-{
- thd->net.no_send_eof= m_nseof; // Restore the originals
- thd->protocol= m_oprot;
- if ((m_isopen= was_opened))
- m_current_row= m_prot->data;
+ if (mysql_open_cursor(thd, (uint) ALWAYS_MATERIALIZED_CURSOR, &result,
+ &server_side_cursor))
+ return -1;
+ return 0;
}
int
sp_cursor::close(THD *thd)
{
- if (! m_isopen)
+ if (! server_side_cursor)
{
my_message(ER_SP_CURSOR_NOT_OPEN, ER(ER_SP_CURSOR_NOT_OPEN), MYF(0));
return -1;
@@ -247,106 +214,82 @@ sp_cursor::close(THD *thd)
void
sp_cursor::destroy()
{
- if (m_prot)
- {
- delete m_prot;
- m_prot= NULL;
- free_root(&m_mem_root, MYF(0));
- }
- m_isopen= FALSE;
+ delete server_side_cursor;
+ server_side_cursor= 0;
}
+
int
sp_cursor::fetch(THD *thd, List<struct sp_pvar> *vars)
{
- List_iterator_fast<struct sp_pvar> li(*vars);
- sp_pvar_t *pv;
- MYSQL_ROW row;
- uint fldcount;
-
- if (! m_isopen)
+ if (! server_side_cursor)
{
my_message(ER_SP_CURSOR_NOT_OPEN, ER(ER_SP_CURSOR_NOT_OPEN), MYF(0));
return -1;
}
- if (m_current_row == NULL)
+ if (vars->elements != result.get_field_count())
{
- my_message(ER_SP_FETCH_NO_DATA, ER(ER_SP_FETCH_NO_DATA), MYF(0));
+ my_message(ER_SP_WRONG_NO_OF_FETCH_ARGS,
+ ER(ER_SP_WRONG_NO_OF_FETCH_ARGS), MYF(0));
return -1;
}
- row= m_current_row->data;
- for (fldcount= 0 ; (pv= li++) ; fldcount++)
- {
- Item *it;
- Item *reuse;
- uint rsize;
- Item *old_item_next;
- Item *old_free_list= thd->free_list;
- const char *s;
- LINT_INIT(old_item_next);
-
- if (fldcount >= m_prot->get_field_count())
- {
- my_message(ER_SP_WRONG_NO_OF_FETCH_ARGS,
- ER(ER_SP_WRONG_NO_OF_FETCH_ARGS), MYF(0));
- return -1;
- }
+ result.set_spvar_list(vars);
- if ((reuse= thd->spcont->get_item(pv->offset)))
- old_item_next= reuse->next;
+ /* Attempt to fetch one row */
+ if (server_side_cursor->is_open())
+ server_side_cursor->fetch(1);
- s= row[fldcount];
- if (!s)
- it= new(reuse, &rsize) Item_null();
- else
- {
- /*
- Length of data can be calculated as:
- pointer_to_next_not_null_object - s -1
- where the last -1 is to remove the end \0
- */
- uint len;
- MYSQL_ROW next= row+fldcount+1;
- while (!*next) // Skip nulls
- next++;
- len= (*next -s)-1;
- switch (sp_map_result_type(pv->type)) {
- case INT_RESULT:
- it= new(reuse, &rsize) Item_int(s);
- break;
- case REAL_RESULT:
- it= new(reuse, &rsize) Item_float(s, len);
- break;
- case DECIMAL_RESULT:
- it= new(reuse, &rsize) Item_decimal(s, len, thd->db_charset);
- break;
- case STRING_RESULT:
- /* TODO: Document why we do an extra copy of the string 's' here */
- it= new(reuse, &rsize) Item_string(thd->strmake(s, len), len,
- thd->db_charset);
- break;
- case ROW_RESULT:
- default:
- DBUG_ASSERT(0);
- }
- }
- it->rsize= rsize;
- if (reuse && it == reuse)
- {
- // A reused item slot, where the constructor put it in the free_list,
- // so we have to restore the list.
- thd->free_list= old_free_list;
- it->next= old_item_next;
- }
- thd->spcont->set_item(pv->offset, it);
- }
- if (fldcount < m_prot->get_field_count())
+ /*
+ If the cursor was pointing after the last row, the fetch will
+ close it instead of sending any rows.
+ */
+ if (! server_side_cursor->is_open())
{
- my_message(ER_SP_WRONG_NO_OF_FETCH_ARGS,
- ER(ER_SP_WRONG_NO_OF_FETCH_ARGS), MYF(0));
+ my_message(ER_SP_FETCH_NO_DATA, ER(ER_SP_FETCH_NO_DATA), MYF(0));
return -1;
}
- m_current_row= m_current_row->next;
+
return 0;
}
+
+
+/***************************************************************************
+ Select_fetch_into_spvars
+****************************************************************************/
+
+int Select_fetch_into_spvars::prepare(List<Item> &fields, SELECT_LEX_UNIT *u)
+{
+ /*
+ Cache the number of columns in the result set in order to easily
+ return an error if column count does not match value count.
+ */
+ field_count= fields.elements;
+ return select_result_interceptor::prepare(fields, u);
+}
+
+
+bool Select_fetch_into_spvars::send_data(List<Item> &items)
+{
+ List_iterator_fast<struct sp_pvar> pv_iter(*spvar_list);
+ List_iterator_fast<Item> item_iter(items);
+ sp_pvar_t *pv;
+ Item *item;
+
+ /* Must be ensured by the caller */
+ DBUG_ASSERT(spvar_list->elements == items.elements);
+
+ /*
+ Assign the row fetched from a server side cursor to stored
+ procedure variables.
+ */
+ for (; pv= pv_iter++, item= item_iter++; )
+ {
+ Item *reuse= thd->spcont->get_item(pv->offset);
+ /* Evaluate a new item on the arena of the calling instruction */
+ Item *it= sp_eval_func_item(thd, &item, pv->type, reuse, TRUE);
+
+ thd->spcont->set_item(pv->offset, it);
+ }
+ return FALSE;
+}
diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h
index 36380952e5d..9c0fa88fe34 100644
--- a/sql/sp_rcontext.h
+++ b/sql/sp_rcontext.h
@@ -216,6 +216,27 @@ private:
}; // class sp_rcontext : public Sql_alloc
+/*
+ An interceptor of cursor result set used to implement
+ FETCH <cname> INTO <varlist>.
+*/
+
+class Select_fetch_into_spvars: public select_result_interceptor
+{
+ List<struct sp_pvar> *spvar_list;
+ uint field_count;
+public:
+ uint get_field_count() { return field_count; }
+ void set_spvar_list(List<struct sp_pvar> *vars) { spvar_list= vars; }
+
+ virtual bool send_eof() { return FALSE; }
+ virtual bool send_data(List<Item> &items);
+ virtual int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
+};
+
+
+/* A mediator between stored procedures and server side cursors */
+
class sp_cursor : public Sql_alloc
{
public:
@@ -227,12 +248,11 @@ public:
destroy();
}
- // We have split this in two to make it easy for sp_instr_copen
- // to reuse the sp_instr::exec_stmt() code.
sp_lex_keeper *
- pre_open(THD *thd);
- void
- post_open(THD *thd, my_bool was_opened);
+ get_lex_keeper() { return m_lex_keeper; }
+
+ int
+ open(THD *thd);
int
close(THD *thd);
@@ -240,7 +260,7 @@ public:
inline my_bool
is_open()
{
- return m_isopen;
+ return test(server_side_cursor);
}
int
@@ -251,18 +271,13 @@ public:
{
return m_i;
}
-
+
private:
- MEM_ROOT m_mem_root; // My own mem_root
+ Select_fetch_into_spvars result;
sp_lex_keeper *m_lex_keeper;
- Protocol_cursor *m_prot;
- my_bool m_isopen;
- my_bool m_nseof; // Original no_send_eof
- Protocol *m_oprot; // Original protcol
- MYSQL_ROWS *m_current_row;
+ Server_side_cursor *server_side_cursor;
sp_instr_cpush *m_i; // My push instruction
-
void
destroy();
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index d6c473ec8bc..bb182232e0d 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -1488,7 +1488,7 @@ bool is_acl_user(const char *host, const char *user)
{
bool res;
VOID(pthread_mutex_lock(&acl_cache->lock));
- res= find_acl_user(host, user, TRUE);
+ res= find_acl_user(host, user, TRUE) != NULL;
VOID(pthread_mutex_unlock(&acl_cache->lock));
return res;
}
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 1417f13989e..16842c4969c 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -1327,6 +1327,8 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
table->keys_in_use_for_query= table->s->keys_in_use;
table->insert_values= 0;
table->used_keys= table->s->keys_for_keyread;
+ table->fulltext_searched= 0;
+ table->file->ft_handler= 0;
if (table->timestamp_field)
table->timestamp_field_type= table->timestamp_field->get_auto_set_type();
table_list->updatable= 1; // It is not derived table nor non-updatable VIEW
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 3d2322f7fff..2917626ff35 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -1538,6 +1538,19 @@ void Query_arena::free_items()
}
+void Query_arena::set_query_arena(Query_arena *set)
+{
+ mem_root= set->mem_root;
+ free_list= set->free_list;
+ state= set->state;
+}
+
+
+void Query_arena::cleanup_stmt()
+{
+ DBUG_ASSERT("Query_arena::cleanup_stmt()" == "not implemented");
+}
+
/*
Statement functions
*/
@@ -1595,12 +1608,6 @@ void Statement::restore_backup_statement(Statement *stmt, Statement *backup)
}
-void Statement::close_cursor()
-{
- DBUG_ASSERT("Statement::close_cursor()" == "not implemented");
-}
-
-
void THD::end_statement()
{
/* Cleanup SQL processing state to resuse this statement in next query. */
@@ -1642,13 +1649,6 @@ void THD::restore_active_arena(Query_arena *set, Query_arena *backup)
DBUG_VOID_RETURN;
}
-void Query_arena::set_query_arena(Query_arena *set)
-{
- mem_root= set->mem_root;
- free_list= set->free_list;
- state= set->state;
-}
-
Statement::~Statement()
{
/*
@@ -1717,9 +1717,11 @@ int Statement_map::insert(Statement *statement)
void Statement_map::close_transient_cursors()
{
+#ifdef TO_BE_IMPLEMENTED
Statement *stmt;
while ((stmt= transient_cursor_list.head()))
stmt->close_cursor(); /* deletes itself from the list */
+#endif
}
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 6756902a4e5..7cbfc19123f 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -738,10 +738,12 @@ public:
void set_query_arena(Query_arena *set);
void free_items();
+ /* Close the active state associated with execution of this statement */
+ virtual void cleanup_stmt();
};
-class Cursor;
+class Server_side_cursor;
/*
State of a single command executed against this connection.
@@ -817,7 +819,7 @@ public:
*/
char *query;
uint32 query_length; // current query length
- Cursor *cursor;
+ Server_side_cursor *cursor;
public:
@@ -834,8 +836,6 @@ public:
void restore_backup_statement(Statement *stmt, Statement *backup);
/* return class type */
virtual Type type() const;
- /* Close the cursor open for this statement, if there is one */
- virtual void close_cursor();
};
@@ -887,9 +887,6 @@ public:
}
hash_delete(&st_hash, (byte *) statement);
}
- void add_transient_cursor(Statement *stmt)
- { transient_cursor_list.append(stmt); }
- void erase_transient_cursor(Statement *stmt) { stmt->unlink(); }
/*
Close all cursors of this connection that use tables of a storage
engine that has transaction-specific state and therefore can not
@@ -1834,18 +1831,21 @@ public:
}
};
-class select_union :public select_result_interceptor {
- public:
- TABLE *table;
+class select_union :public select_result_interceptor
+{
TMP_TABLE_PARAM tmp_table_param;
+public:
+ TABLE *table;
- select_union(TABLE *table_par);
- ~select_union();
+ select_union() :table(0) {}
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
bool send_data(List<Item> &items);
bool send_eof();
bool flush();
- void set_table(TABLE *tbl) { table= tbl; }
+
+ bool create_result_table(THD *thd, List<Item> *column_types,
+ bool is_distinct, ulonglong options,
+ const char *alias);
};
/* Base subselect interface class */
diff --git a/sql/sql_cursor.cc b/sql/sql_cursor.cc
new file mode 100644
index 00000000000..e8da691ea18
--- /dev/null
+++ b/sql/sql_cursor.cc
@@ -0,0 +1,660 @@
+/* Copyright (C) 2005 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 USE_PRAGMA_IMPLEMENTATION
+#pragma implementation /* gcc class implementation */
+#endif
+
+#include "mysql_priv.h"
+#include "sql_cursor.h"
+#include "sql_select.h"
+
+/****************************************************************************
+ Declarations.
+****************************************************************************/
+
+/*
+ Sensitive_cursor -- a sensitive non-materialized server side
+ cursor An instance of this class cursor has its own runtime
+ state -- list of used items and memory root for runtime memory,
+ open and locked tables, change list for the changes of the
+ parsed tree. This state is freed when the cursor is closed.
+*/
+
+class Sensitive_cursor: public Server_side_cursor
+{
+ MEM_ROOT main_mem_root;
+ Query_arena *stmt_arena;
+ JOIN *join;
+ TABLE *open_tables;
+ MYSQL_LOCK *lock;
+ TABLE *derived_tables;
+ /* List of items created during execution */
+ query_id_t query_id;
+ struct Engine_info
+ {
+ const handlerton *ht;
+ void *read_view;
+ };
+ Engine_info ht_info[MAX_HA];
+ Item_change_list change_list;
+ my_bool close_at_commit;
+ THR_LOCK_OWNER lock_id;
+private:
+ /* bzero cursor state in THD */
+ void reset_thd(THD *thd);
+public:
+ Sensitive_cursor(THD *thd, select_result *result_arg);
+
+ THR_LOCK_OWNER *get_lock_id() { return &lock_id; }
+ /* Save THD state into cursor */
+ void post_open(THD *thd);
+
+ virtual bool is_open() const { return join != 0; }
+ virtual int open(JOIN *join);
+ virtual void fetch(ulong num_rows);
+ virtual void close();
+ virtual ~Sensitive_cursor();
+};
+
+
+/*
+ Materialized_cursor -- an insensitive materialized server-side
+ cursor. The result set of this cursor is saved in a temporary
+ table at open. The cursor itself is simply an interface for the
+ handler of the temporary table.
+*/
+
+class Materialized_cursor: public Server_side_cursor
+{
+ MEM_ROOT main_mem_root;
+ /* A fake unit to supply to select_send when fetching */
+ SELECT_LEX_UNIT fake_unit;
+ TABLE *table;
+ List<Item> item_list;
+ ulong fetch_limit;
+ ulong fetch_count;
+public:
+ Materialized_cursor(select_result *result, TABLE *table);
+
+ virtual bool is_open() const { return table != 0; }
+ virtual int open(JOIN *join __attribute__((unused)));
+ virtual void fetch(ulong num_rows);
+ virtual void close();
+ virtual ~Materialized_cursor();
+};
+
+
+/*
+ Select_materialize -- a mediator between a cursor query and the
+ protocol. In case we were not able to open a non-materialzed
+ cursor, it creates an internal temporary HEAP table, and insert
+ all rows into it. When the table reaches max_heap_table_size,
+ it's converted to a MyISAM table. Later this table is used to
+ create a Materialized_cursor.
+*/
+
+class Select_materialize: public select_union
+{
+ select_result *result; /* the result object of the caller (PS or SP) */
+public:
+ Select_materialize(select_result *result_arg) :result(result_arg) {}
+ virtual bool send_fields(List<Item> &list, uint flags);
+};
+
+
+/**************************************************************************/
+
+/*
+ Attempt to open a materialized or non-materialized cursor.
+
+ SYNOPSIS
+ mysql_open_cursor()
+ thd thread handle
+ flags [in] create a materialized cursor or not
+ result [in] result class of the caller used as a destination
+ for the rows fetched from the cursor
+ pcursor [out] a pointer to store a pointer to cursor in
+
+ RETURN VALUE
+ 0 the query has been successfully executed; in this
+ case pcursor may or may not contain
+ a pointer to an open cursor.
+ non-zero an error, 'pcursor' has been left intact.
+*/
+
+int mysql_open_cursor(THD *thd, uint flags, select_result *result,
+ Server_side_cursor **pcursor)
+{
+ Sensitive_cursor *sensitive_cursor;
+ select_result *save_result;
+ Select_materialize *result_materialize;
+ LEX *lex= thd->lex;
+ int rc;
+
+ /*
+ The lifetime of the sensitive cursor is the same or less as the
+ lifetime of the runtime memory of the statement it's opened for.
+ */
+ if (! (result_materialize= new (thd->mem_root) Select_materialize(result)))
+ return 1;
+
+ if (! (sensitive_cursor= new (thd->mem_root) Sensitive_cursor(thd, result)))
+ {
+ delete result;
+ return 1;
+ }
+
+ save_result= lex->result;
+
+ lex->result= result_materialize;
+ if (! (flags & (uint) ALWAYS_MATERIALIZED_CURSOR))
+ {
+ thd->lock_id= sensitive_cursor->get_lock_id();
+ thd->cursor= sensitive_cursor;
+ }
+
+ rc= mysql_execute_command(thd);
+
+ lex->result= save_result;
+ thd->lock_id= &thd->main_lock_id;
+ thd->cursor= 0;
+
+ /*
+ Possible options here:
+ - a sensitive cursor is open. In this case rc is 0 and
+ result_materialize->table is NULL, or
+ - a materialized cursor is open. In this case rc is 0 and
+ result_materialize->table is not NULL
+ - an error occured during materializaton.
+ result_materialize->table is not NULL, but rc != 0
+ - successful completion of mysql_execute_command without
+ a cursor: rc is 0, result_materialize->table is NULL,
+ sensitive_cursor is not open.
+ This is possible if some command writes directly to the
+ network, bypassing select_result mechanism. An example of
+ such command is SHOW VARIABLES or SHOW STATUS.
+ */
+ if (rc)
+ goto err_open;
+
+ if (sensitive_cursor->is_open())
+ {
+ DBUG_ASSERT(!result_materialize->table);
+ /*
+ It's safer if we grab THD state after mysql_execute_command
+ is finished and not in Sensitive_cursor::open(), because
+ currently the call to Sensitive_cursor::open is buried deep
+ in JOIN::exec of the top level join.
+ */
+ sensitive_cursor->post_open(thd);
+ *pcursor= sensitive_cursor;
+ goto end;
+ }
+ else if (result_materialize->table)
+ {
+ Materialized_cursor *materialized_cursor;
+ TABLE *table= result_materialize->table;
+ MEM_ROOT *mem_root= &table->mem_root;
+
+ if (!(materialized_cursor= new (mem_root)
+ Materialized_cursor(result, table)))
+ {
+ rc= 1;
+ goto err_open;
+ }
+
+ if ((rc= materialized_cursor->open(0)))
+ {
+ delete materialized_cursor;
+ goto err_open;
+ }
+
+ *pcursor= materialized_cursor;
+ thd->stmt_arena->cleanup_stmt();
+ goto end;
+ }
+
+err_open:
+ DBUG_ASSERT(! (sensitive_cursor && sensitive_cursor->is_open()));
+ delete sensitive_cursor;
+ if (result_materialize->table)
+ free_tmp_table(thd, result_materialize->table);
+end:
+ delete result_materialize;
+ return rc;
+}
+
+/****************************************************************************
+ Server_side_cursor
+****************************************************************************/
+
+Server_side_cursor::~Server_side_cursor()
+{
+}
+
+
+void Server_side_cursor::operator delete(void *ptr, size_t size)
+{
+ Server_side_cursor *cursor= (Server_side_cursor*) ptr;
+ MEM_ROOT own_root= *cursor->mem_root;
+
+ DBUG_ENTER("Server_side_cursor::operator delete");
+ TRASH(ptr, size);
+ /*
+ If this cursor has never been opened mem_root is empty. Otherwise
+ mem_root points to the memory the cursor object was allocated in.
+ In this case it's important to call free_root last, and free a copy
+ instead of *mem_root to avoid writing into freed memory.
+ */
+ free_root(&own_root, MYF(0));
+ DBUG_VOID_RETURN;
+}
+
+/****************************************************************************
+ Sensitive_cursor
+****************************************************************************/
+
+Sensitive_cursor::Sensitive_cursor(THD *thd, select_result *result_arg)
+ :Server_side_cursor(&main_mem_root, result_arg),
+ stmt_arena(0),
+ join(0),
+ close_at_commit(FALSE)
+{
+ /* We will overwrite it at open anyway. */
+ init_sql_alloc(&main_mem_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0);
+ thr_lock_owner_init(&lock_id, &thd->lock_info);
+ bzero((void*) ht_info, sizeof(ht_info));
+}
+
+
+void
+Sensitive_cursor::post_open(THD *thd)
+{
+ Engine_info *info;
+ /*
+ We need to save and reset thd->mem_root, otherwise it'll be
+ freed later in mysql_parse.
+
+ We can't just change thd->mem_root here as we want to keep the
+ things that are already allocated in thd->mem_root for
+ Sensitive_cursor::fetch()
+ */
+ *mem_root= *thd->mem_root;
+ stmt_arena= thd->stmt_arena;
+ state= stmt_arena->state;
+ /* Allocate a new memory root for thd */
+ init_sql_alloc(thd->mem_root,
+ thd->variables.query_alloc_block_size,
+ thd->variables.query_prealloc_size);
+
+ /*
+ Save tables and zero THD pointers to prevent table close in
+ close_thread_tables.
+ */
+ derived_tables= thd->derived_tables;
+ open_tables= thd->open_tables;
+ lock= thd->lock;
+ query_id= thd->query_id;
+ free_list= thd->free_list;
+ change_list= thd->change_list;
+ reset_thd(thd);
+ /* Now we have an active cursor and can cause a deadlock */
+ thd->lock_info.n_cursors++;
+
+ close_at_commit= FALSE; /* reset in case we're reusing the cursor */
+ info= &ht_info[0];
+ for (handlerton **pht= thd->transaction.stmt.ht; *pht; pht++)
+ {
+ const handlerton *ht= *pht;
+ close_at_commit|= test(ht->flags & HTON_CLOSE_CURSORS_AT_COMMIT);
+ if (ht->create_cursor_read_view)
+ {
+ info->ht= ht;
+ info->read_view= (ht->create_cursor_read_view)();
+ ++info;
+ }
+ }
+ /*
+ XXX: thd->locked_tables is not changed.
+ What problems can we have with it if cursor is open?
+ TODO: must be fixed because of the prelocked mode.
+ */
+}
+
+
+void
+Sensitive_cursor::reset_thd(THD *thd)
+{
+ thd->derived_tables= 0;
+ thd->open_tables= 0;
+ thd->lock= 0;
+ thd->free_list= 0;
+ thd->change_list.empty();
+}
+
+
+int
+Sensitive_cursor::open(JOIN *join_arg)
+{
+ join= join_arg;
+ THD *thd= join->thd;
+ /* First non-constant table */
+ JOIN_TAB *join_tab= join->join_tab + join->const_tables;
+ DBUG_ENTER("Sensitive_cursor::open");
+
+ join->change_result(result);
+ /*
+ Send fields description to the client; server_status is sent
+ in 'EOF' packet, which follows send_fields().
+ We don't simply use SEND_EOF flag of send_fields because we also
+ want to flush the network buffer, which is done only in a standalone
+ send_eof().
+ */
+ result->send_fields(*join->fields, Protocol::SEND_NUM_ROWS);
+ thd->server_status|= SERVER_STATUS_CURSOR_EXISTS;
+ result->send_eof();
+ thd->server_status&= ~SERVER_STATUS_CURSOR_EXISTS;
+
+ /* Prepare JOIN for reading rows. */
+ join->tmp_table= 0;
+ join->join_tab[join->tables-1].next_select= setup_end_select_func(join);
+ join->send_records= 0;
+ join->fetch_limit= join->unit->offset_limit_cnt;
+
+ /* Disable JOIN CACHE as it is not working with cursors yet */
+ for (JOIN_TAB *tab= join_tab;
+ tab != join->join_tab + join->tables - 1;
+ tab++)
+ {
+ if (tab->next_select == sub_select_cache)
+ tab->next_select= sub_select;
+ }
+
+ DBUG_ASSERT(join_tab->table->reginfo.not_exists_optimize == 0);
+ DBUG_ASSERT(join_tab->not_used_in_distinct == 0);
+ /*
+ null_row is set only if row not found and it's outer join: should never
+ happen for the first table in join_tab list
+ */
+ DBUG_ASSERT(join_tab->table->null_row == 0);
+ DBUG_RETURN(0);
+}
+
+
+/*
+ SYNOPSIS
+ Sensitive_cursor::fetch()
+ num_rows fetch up to this number of rows (maybe less)
+
+ DESCRIPTION
+ Fetch next num_rows rows from the cursor and send them to the client
+
+ Precondition:
+ Sensitive_cursor is open
+
+ RETURN VALUES:
+ none, this function will send OK to the clinet or set an error
+ message in THD
+*/
+
+void
+Sensitive_cursor::fetch(ulong num_rows)
+{
+ THD *thd= join->thd;
+ JOIN_TAB *join_tab= join->join_tab + join->const_tables;
+ enum_nested_loop_state error= NESTED_LOOP_OK;
+ Query_arena backup_arena;
+ Engine_info *info;
+ DBUG_ENTER("Sensitive_cursor::fetch");
+ DBUG_PRINT("enter",("rows: %lu", num_rows));
+
+ DBUG_ASSERT(thd->derived_tables == 0 && thd->open_tables == 0 &&
+ thd->lock == 0);
+
+ thd->derived_tables= derived_tables;
+ thd->open_tables= open_tables;
+ thd->lock= lock;
+ thd->query_id= query_id;
+ thd->change_list= change_list;
+ /* save references to memory allocated during fetch */
+ thd->set_n_backup_active_arena(this, &backup_arena);
+
+ for (info= ht_info; info->read_view ; info++)
+ (info->ht->set_cursor_read_view)(info->read_view);
+
+ join->fetch_limit+= num_rows;
+
+ error= sub_select(join, join_tab, 0);
+ if (error == NESTED_LOOP_OK || error == NESTED_LOOP_NO_MORE_ROWS)
+ error= sub_select(join,join_tab,1);
+ if (error == NESTED_LOOP_QUERY_LIMIT)
+ error= NESTED_LOOP_OK; /* select_limit used */
+ if (error == NESTED_LOOP_CURSOR_LIMIT)
+ join->resume_nested_loop= TRUE;
+
+#ifdef USING_TRANSACTIONS
+ ha_release_temporary_latches(thd);
+#endif
+ /* Grab free_list here to correctly free it in close */
+ thd->restore_active_arena(this, &backup_arena);
+
+ change_list= thd->change_list;
+ reset_thd(thd);
+
+ for (info= ht_info; info->read_view; info++)
+ (info->ht->set_cursor_read_view)(0);
+
+ if (error == NESTED_LOOP_CURSOR_LIMIT)
+ {
+ /* Fetch limit worked, possibly more rows are there */
+ thd->server_status|= SERVER_STATUS_CURSOR_EXISTS;
+ result->send_eof();
+ thd->server_status&= ~SERVER_STATUS_CURSOR_EXISTS;
+ }
+ else
+ {
+ close();
+ if (error == NESTED_LOOP_OK)
+ {
+ thd->server_status|= SERVER_STATUS_LAST_ROW_SENT;
+ result->send_eof();
+ thd->server_status&= ~SERVER_STATUS_LAST_ROW_SENT;
+ }
+ else if (error != NESTED_LOOP_KILLED)
+ my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+void
+Sensitive_cursor::close()
+{
+ THD *thd= join->thd;
+ DBUG_ENTER("Sensitive_cursor::close");
+
+ for (Engine_info *info= ht_info; info->read_view; info++)
+ {
+ (info->ht->close_cursor_read_view)(info->read_view);
+ info->read_view= 0;
+ info->ht= 0;
+ }
+
+ thd->change_list= change_list;
+ {
+ /*
+ XXX: Another hack: we need to set THD state as if in a fetch to be
+ able to call stmt close.
+ */
+ DBUG_ASSERT(lock || open_tables || derived_tables);
+
+ TABLE *tmp_derived_tables= thd->derived_tables;
+ MYSQL_LOCK *tmp_lock= thd->lock;
+
+ thd->open_tables= open_tables;
+ thd->derived_tables= derived_tables;
+ thd->lock= lock;
+
+ /* Is expected to at least close tables and empty thd->change_list */
+ stmt_arena->cleanup_stmt();
+
+ thd->open_tables= tmp_derived_tables;
+ thd->derived_tables= tmp_derived_tables;
+ thd->lock= tmp_lock;
+ }
+ thd->lock_info.n_cursors--; /* Decrease the number of active cursors */
+ join= 0;
+ stmt_arena= 0;
+ free_items();
+ change_list.empty();
+ DBUG_VOID_RETURN;
+}
+
+
+Sensitive_cursor::~Sensitive_cursor()
+{
+ if (is_open())
+ close();
+}
+
+/***************************************************************************
+ Materialized_cursor
+****************************************************************************/
+
+Materialized_cursor::Materialized_cursor(select_result *result_arg,
+ TABLE *table_arg)
+ :Server_side_cursor(&table_arg->mem_root, result_arg),
+ table(table_arg),
+ fetch_limit(0),
+ fetch_count(0)
+{
+ fake_unit.init_query();
+ fake_unit.thd= table->in_use;
+}
+
+
+int Materialized_cursor::open(JOIN *join __attribute__((unused)))
+{
+ THD *thd= fake_unit.thd;
+ int rc;
+ Query_arena backup_arena;
+
+ thd->set_n_backup_active_arena(this, &backup_arena);
+ /* Create a list of fields and start sequential scan */
+ rc= (table->fill_item_list(&item_list) ||
+ result->prepare(item_list, &fake_unit) ||
+ table->file->ha_rnd_init(TRUE));
+ thd->restore_active_arena(this, &backup_arena);
+ return rc;
+}
+
+
+/*
+ Fetch up to the given number of rows from a materialized cursor.
+
+ DESCRIPTION
+ Precondition: the cursor is open.
+
+ If the cursor points after the last row, the fetch will automatically
+ close the cursor and not send any data (except the 'EOF' packet
+ with SERVER_STATUS_LAST_ROW_SENT). This is an extra round trip
+ and probably should be improved to return
+ SERVER_STATUS_LAST_ROW_SENT along with the last row.
+
+ RETURN VALUE
+ none, in case of success the row is sent to the client, otherwise
+ an error message is set in THD
+*/
+
+void Materialized_cursor::fetch(ulong num_rows)
+{
+ THD *thd= table->in_use;
+
+ int res= 0;
+ for (fetch_limit+= num_rows; fetch_count < fetch_limit; fetch_count++)
+ {
+ if ((res= table->file->rnd_next(table->record[0])))
+ break;
+ /* Send data only if the read was successful. */
+ result->send_data(item_list);
+ }
+
+ switch (res) {
+ case 0:
+ thd->server_status|= SERVER_STATUS_CURSOR_EXISTS;
+ result->send_eof();
+ thd->server_status&= ~SERVER_STATUS_CURSOR_EXISTS;
+ break;
+ case HA_ERR_END_OF_FILE:
+ thd->server_status|= SERVER_STATUS_LAST_ROW_SENT;
+ result->send_eof();
+ thd->server_status&= ~SERVER_STATUS_LAST_ROW_SENT;
+ close();
+ break;
+ default:
+ table->file->print_error(res, MYF(0));
+ close();
+ break;
+ }
+}
+
+
+void Materialized_cursor::close()
+{
+ /* Free item_list items */
+ free_items();
+ (void) table->file->ha_rnd_end();
+ /*
+ We need to grab table->mem_root to prevent free_tmp_table from freeing:
+ the cursor object was allocated in this memory.
+ */
+ main_mem_root= table->mem_root;
+ mem_root= &main_mem_root;
+ clear_alloc_root(&table->mem_root);
+ free_tmp_table(table->in_use, table);
+ table= 0;
+}
+
+
+Materialized_cursor::~Materialized_cursor()
+{
+ if (is_open())
+ close();
+}
+
+
+/***************************************************************************
+ Select_materialize
+****************************************************************************/
+
+bool Select_materialize::send_fields(List<Item> &list, uint flags)
+{
+ bool rc;
+ DBUG_ASSERT(table == 0);
+ if (create_result_table(unit->thd, unit->get_unit_column_types(),
+ FALSE, thd->options | TMP_TABLE_ALL_COLUMNS, ""))
+ return TRUE;
+ /*
+ We can't simply supply SEND_EOF flag to send_fields, because send_fields
+ doesn't flush the network buffer.
+ */
+ rc= result->send_fields(list, Protocol::SEND_NUM_ROWS);
+ thd->server_status|= SERVER_STATUS_CURSOR_EXISTS;
+ result->send_eof();
+ thd->server_status&= ~SERVER_STATUS_CURSOR_EXISTS;
+ return rc;
+}
+
diff --git a/sql/sql_cursor.h b/sql/sql_cursor.h
new file mode 100644
index 00000000000..d1156dfba8d
--- /dev/null
+++ b/sql/sql_cursor.h
@@ -0,0 +1,65 @@
+#ifndef _sql_cursor_h_
+#define _sql_cursor_h_
+/* Copyright (C) 2005 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 USE_PRAGMA_INTERFACE
+#pragma interface /* gcc class interface */
+#endif
+
+/*
+ Declarations for implementation of server side cursors. Only
+ read-only non-scrollable cursors are currently implemented.
+*/
+
+/*
+ Server_side_cursor -- an interface for materialized and
+ sensitive (non-materialized) implementation of cursors. All
+ cursors are self-contained (created in their own memory root).
+ For that reason they must be deleted only using a pointer to
+ Server_side_cursor, not to its base class.
+*/
+
+class Server_side_cursor: protected Query_arena, public Sql_alloc
+{
+protected:
+ /* Row destination used for fetch */
+ select_result *result;
+public:
+ Server_side_cursor(MEM_ROOT *mem_root_arg, select_result *result_arg)
+ :Query_arena(mem_root_arg, INITIALIZED), result(result_arg)
+ {}
+
+ virtual bool is_open() const= 0;
+
+ virtual int open(JOIN *top_level_join)= 0;
+ virtual void fetch(ulong num_rows)= 0;
+ virtual void close()= 0;
+ virtual ~Server_side_cursor();
+
+ static void operator delete(void *ptr, size_t size);
+};
+
+
+int mysql_open_cursor(THD *thd, uint flags,
+ select_result *result,
+ Server_side_cursor **res);
+
+/* Possible values for flags */
+
+enum { ANY_CURSOR= 1, ALWAYS_MATERIALIZED_CURSOR= 2 };
+
+#endif /* _sql_cusor_h_ */
diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc
index 7b9191cd841..74b239e1637 100644
--- a/sql/sql_derived.cc
+++ b/sql/sql_derived.cc
@@ -27,7 +27,7 @@
/*
- call given derived table processor (preparing or filling tables)
+ Call given derived table processor (preparing or filling tables)
SYNOPSIS
mysql_handle_derived()
@@ -36,7 +36,6 @@
RETURN
0 ok
- -1 Error
1 Error and error message given
*/
@@ -97,14 +96,14 @@ out:
RETURN
0 ok
- 1 Error
- -1 Error and error message given
- */
+ 1 Error and an error message was given
+*/
int mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *orig_table_list)
{
SELECT_LEX_UNIT *unit= orig_table_list->derived;
int res= 0;
+ ulonglong create_options;
DBUG_ENTER("mysql_derived_prepare");
if (unit)
{
@@ -118,21 +117,18 @@ int mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *orig_table_list)
for (SELECT_LEX *sl= first_select; sl; sl= sl->next_select())
sl->context.outer_context= 0;
- if (!(derived_result= new select_union(0)))
+ if (!(derived_result= new select_union))
DBUG_RETURN(1); // out of memory
// st_select_lex_unit::prepare correctly work for single select
- if ((res= unit->prepare(thd, derived_result, 0, orig_table_list->alias)))
+ if ((res= unit->prepare(thd, derived_result, 0)))
goto exit;
- if (check_duplicate_names(unit->types, 0))
- {
- res= -1;
+ if ((res= check_duplicate_names(unit->types, 0)))
goto exit;
- }
- derived_result->tmp_table_param.init();
- derived_result->tmp_table_param.field_count= unit->types.elements;
+ create_options= (first_select->options | thd->options |
+ TMP_TABLE_ALL_COLUMNS);
/*
Temp table is created so that it hounours if UNION without ALL is to be
processed
@@ -143,18 +139,12 @@ int mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *orig_table_list)
!unit->union_distinct->next_select() (i.e. it is union and last distinct
SELECT is last SELECT of UNION).
*/
- if (!(table= create_tmp_table(thd, &derived_result->tmp_table_param,
- unit->types, (ORDER*) 0,
- FALSE, 1,
- (first_select->options | thd->options |
- TMP_TABLE_ALL_COLUMNS),
- HA_POS_ERROR,
- orig_table_list->alias)))
- {
- res= -1;
+ if ((res= derived_result->create_result_table(thd, &unit->types, FALSE,
+ create_options,
+ orig_table_list->alias)))
goto exit;
- }
- derived_result->set_table(table);
+
+ table= derived_result->table;
exit:
/* Hide "Unknown column" or "Unknown function" error */
@@ -231,9 +221,8 @@ exit:
RETURN
0 ok
- 1 Error
- -1 Error and error message given
- */
+ 1 Error and an error message was given
+*/
int mysql_derived_filling(THD *thd, LEX *lex, TABLE_LIST *orig_table_list)
{
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index dd5fdf51daf..8d7ec25f97b 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -432,10 +432,6 @@ public:
{
return my_reinterpret_cast(st_select_lex*)(slave);
}
- st_select_lex* first_select_in_union()
- {
- return my_reinterpret_cast(st_select_lex*)(slave);
- }
st_select_lex_unit* next_unit()
{
return my_reinterpret_cast(st_select_lex_unit*)(next);
@@ -445,8 +441,7 @@ public:
void exclude_tree();
/* UNION methods */
- bool prepare(THD *thd, select_result *result, ulong additional_options,
- const char *tmp_table_alias);
+ bool prepare(THD *thd, select_result *result, ulong additional_options);
bool exec();
bool cleanup();
inline void unclean() { cleaned= 0; }
@@ -462,7 +457,10 @@ public:
friend void lex_start(THD *thd, uchar *buf, uint length);
friend int subselect_union_engine::exec();
+
+ List<Item> *get_unit_column_types();
};
+
typedef class st_select_lex_unit SELECT_LEX_UNIT;
/*
diff --git a/sql/sql_list.h b/sql/sql_list.h
index e4a34cc0aa1..285f1d6e501 100644
--- a/sql/sql_list.h
+++ b/sql/sql_list.h
@@ -32,6 +32,8 @@ public:
{
return (void*) sql_alloc((uint) size);
}
+ static void *operator new[](size_t size, MEM_ROOT *mem_root)
+ { return (void*) alloc_root(mem_root, (uint) size); }
static void *operator new(size_t size, MEM_ROOT *mem_root)
{ return (void*) alloc_root(mem_root, (uint) size); }
static void operator delete(void *ptr, size_t size) { TRASH(ptr, size); }
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 1201a0fb1ab..2dbcfc9d6c5 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -71,6 +71,7 @@ When one supplies long data for a placeholder:
#include "mysql_priv.h"
#include "sql_select.h" // for JOIN
+#include "sql_cursor.h"
#include "sp_head.h"
#include "sp.h"
#include "sp_cache.h"
@@ -81,6 +82,18 @@ When one supplies long data for a placeholder:
#include <mysql_com.h>
#endif
+/* A result class used to send cursor rows using the binary protocol. */
+
+class Select_fetch_protocol_prep: public select_send
+{
+ Protocol_prep protocol;
+public:
+ Select_fetch_protocol_prep(THD *thd);
+ virtual bool send_fields(List<Item> &list, uint flags);
+ virtual bool send_data(List<Item> &items);
+ virtual bool send_eof();
+};
+
/******************************************************************************
Prepared_statement: a statement that can contain placeholders
******************************************************************************/
@@ -89,6 +102,7 @@ class Prepared_statement: public Statement
{
public:
THD *thd;
+ Select_fetch_protocol_prep result;
Protocol *protocol;
Item_param **param_array;
uint param_count;
@@ -109,8 +123,9 @@ public:
virtual ~Prepared_statement();
void setup_set_params();
virtual Query_arena::Type type() const;
- virtual void close_cursor();
+ virtual void cleanup_stmt();
bool set_name(LEX_STRING *name);
+ inline void close_cursor() { delete cursor; cursor= 0; }
bool prepare(const char *packet, uint packet_length);
bool execute(String *expanded_query, bool open_cursor);
@@ -140,8 +155,6 @@ inline bool is_param_null(const uchar *pos, ulong param_no)
return pos[param_no/8] & (1 << (param_no & 7));
}
-enum { STMT_QUERY_LOG_LENGTH= 8192 };
-
/*
Find a prepared statement in the statement map by id.
@@ -1264,7 +1277,7 @@ static int mysql_test_select(Prepared_statement *stmt,
It is not SELECT COMMAND for sure, so setup_tables will be called as
usual, and we pass 0 as setup_tables_done_option
*/
- if (unit->prepare(thd, 0, 0, ""))
+ if (unit->prepare(thd, 0, 0))
goto error;
if (!lex->describe && !text_protocol)
{
@@ -1395,7 +1408,7 @@ static bool select_like_stmt_test(Prepared_statement *stmt,
thd->used_tables= 0; // Updated by setup_fields
/* Calls JOIN::prepare */
- DBUG_RETURN(lex->unit.prepare(thd, 0, setup_tables_done_option, ""));
+ DBUG_RETURN(lex->unit.prepare(thd, 0, setup_tables_done_option));
}
/*
@@ -1785,19 +1798,6 @@ static bool init_param_array(Prepared_statement *stmt)
}
-/* Cleanup PS after execute/prepare and restore THD state */
-
-static void cleanup_stmt_and_thd_after_use(Statement *stmt, THD *thd)
-{
- DBUG_ENTER("cleanup_stmt_and_thd_after_use");
- stmt->lex->unit.cleanup();
- cleanup_items(stmt->free_list);
- thd->rollback_item_tree_changes();
- thd->cleanup_after_query();
- DBUG_VOID_RETURN;
-}
-
-
/*
COM_STMT_PREPARE handler.
@@ -2221,16 +2221,14 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
test(flags & (ulong) CURSOR_TYPE_READ_ONLY));
if (!(specialflag & SPECIAL_NO_PRIOR))
my_pthread_setprio(pthread_self(), WAIT_PRIOR);
- if (rc)
- goto err;
- mysql_log.write(thd, COM_STMT_EXECUTE, "[%lu] %s", stmt->id, thd->query);
+ if (rc == 0)
+ mysql_log.write(thd, COM_STMT_EXECUTE, "[%lu] %s", stmt->id, thd->query);
DBUG_VOID_RETURN;
set_params_data_err:
my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_stmt_execute");
-err:
reset_stmt_params(stmt);
DBUG_VOID_RETURN;
}
@@ -2285,14 +2283,16 @@ void mysql_sql_stmt_execute(THD *thd)
if (stmt->set_params_from_vars(stmt, lex->prepared_stmt_params,
&expanded_query))
- {
- my_error(ER_WRONG_ARGUMENTS, MYF(0), "EXECUTE");
- DBUG_VOID_RETURN;
- }
+ goto set_params_data_err;
(void) stmt->execute(&expanded_query, FALSE);
DBUG_VOID_RETURN;
+
+set_params_data_err:
+ my_error(ER_WRONG_ARGUMENTS, MYF(0), "EXECUTE");
+ reset_stmt_params(stmt);
+ DBUG_VOID_RETURN;
}
@@ -2313,7 +2313,7 @@ void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length)
ulong num_rows= uint4korr(packet+4);
Prepared_statement *stmt;
Statement stmt_backup;
- Cursor *cursor;
+ Server_side_cursor *cursor;
DBUG_ENTER("mysql_stmt_fetch");
statistic_increment(thd->status_var.com_stmt_fetch, &LOCK_status);
@@ -2321,7 +2321,7 @@ void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length)
DBUG_VOID_RETURN;
cursor= stmt->cursor;
- if (!cursor || !cursor->is_open())
+ if (!cursor)
{
my_error(ER_STMT_HAS_NO_OPEN_CURSOR, MYF(0), stmt_id);
DBUG_VOID_RETURN;
@@ -2333,25 +2333,16 @@ void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length)
if (!(specialflag & SPECIAL_NO_PRIOR))
my_pthread_setprio(pthread_self(), QUERY_PRIOR);
- thd->protocol= stmt->protocol; // Switch to binary protocol
cursor->fetch(num_rows);
- thd->protocol= &thd->protocol_simple; // Use normal protocol
if (!(specialflag & SPECIAL_NO_PRIOR))
my_pthread_setprio(pthread_self(), WAIT_PRIOR);
if (!cursor->is_open())
{
- /* We're done with the fetch: reset PS for next execution */
- cleanup_stmt_and_thd_after_use(stmt, thd);
+ stmt->close_cursor();
+ thd->cursor= 0;
reset_stmt_params(stmt);
- /*
- Must be the last, as some memory is still needed for
- the previous calls.
- */
- free_root(cursor->mem_root, MYF(0));
- if (cursor->close_at_commit)
- thd->stmt_map.erase_transient_cursor(stmt);
}
thd->restore_backup_statement(stmt, &stmt_backup);
@@ -2384,14 +2375,19 @@ void mysql_stmt_reset(THD *thd, char *packet)
/* There is always space for 4 bytes in buffer */
ulong stmt_id= uint4korr(packet);
Prepared_statement *stmt;
- Cursor *cursor;
DBUG_ENTER("mysql_stmt_reset");
statistic_increment(thd->status_var.com_stmt_reset, &LOCK_status);
if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_reset")))
DBUG_VOID_RETURN;
- stmt->close_cursor(); /* will reset statement params */
+ stmt->close_cursor();
+
+ /*
+ Clear parameters from data which could be set by
+ mysql_stmt_send_long_data() call.
+ */
+ reset_stmt_params(stmt);
stmt->state= Query_arena::PREPARED;
@@ -2533,11 +2529,65 @@ void mysql_stmt_get_longdata(THD *thd, char *packet, ulong packet_length)
}
+/***************************************************************************
+ Select_fetch_protocol_prep
+****************************************************************************/
+
+Select_fetch_protocol_prep::Select_fetch_protocol_prep(THD *thd)
+ :protocol(thd)
+{}
+
+bool Select_fetch_protocol_prep::send_fields(List<Item> &list, uint flags)
+{
+ bool rc;
+ Protocol *save_protocol= thd->protocol;
+
+ /*
+ Protocol::send_fields caches the information about column types:
+ this information is later used to send data. Therefore, the same
+ dedicated Protocol object must be used for all operations with
+ a cursor.
+ */
+ thd->protocol= &protocol;
+ rc= select_send::send_fields(list, flags);
+ thd->protocol= save_protocol;
+
+ return rc;
+}
+
+bool Select_fetch_protocol_prep::send_eof()
+{
+ Protocol *save_protocol= thd->protocol;
+
+ thd->protocol= &protocol;
+ ::send_eof(thd);
+ thd->protocol= save_protocol;
+ return FALSE;
+}
+
+
+bool
+Select_fetch_protocol_prep::send_data(List<Item> &fields)
+{
+ Protocol *save_protocol= thd->protocol;
+ bool rc;
+
+ thd->protocol= &protocol;
+ rc= select_send::send_data(fields);
+ thd->protocol= save_protocol;
+ return rc;
+}
+
+/***************************************************************************
+ Prepared_statement
+****************************************************************************/
+
Prepared_statement::Prepared_statement(THD *thd_arg, Protocol *protocol_arg)
:Statement(INITIALIZED, ++thd_arg->statement_id_counter,
thd_arg->variables.query_alloc_block_size,
thd_arg->variables.query_prealloc_size),
thd(thd_arg),
+ result(thd_arg),
protocol(protocol_arg),
param_array(0),
param_count(0),
@@ -2585,17 +2635,7 @@ Prepared_statement::~Prepared_statement()
{
DBUG_ENTER("Prepared_statement::~Prepared_statement");
DBUG_PRINT("enter",("stmt: %p cursor: %p", this, cursor));
- if (cursor)
- {
- if (cursor->is_open())
- {
- cursor->close(FALSE);
- cleanup_items(free_list);
- thd->rollback_item_tree_changes();
- free_root(cursor->mem_root, MYF(0));
- }
- cursor->Cursor::~Cursor();
- }
+ delete cursor;
/*
We have to call free on the items even if cleanup is called as some items,
like Item_param, don't free everything until free_items()
@@ -2612,25 +2652,18 @@ Query_arena::Type Prepared_statement::type() const
}
-void Prepared_statement::close_cursor()
+void Prepared_statement::cleanup_stmt()
{
- DBUG_ENTER("Prepared_statement::close_cursor");
+ DBUG_ENTER("Prepared_statement::cleanup_stmt");
DBUG_PRINT("enter",("stmt: %p", this));
- if (cursor && cursor->is_open())
- {
- thd->change_list= cursor->change_list;
- cursor->close(FALSE);
- cleanup_stmt_and_thd_after_use(this, thd);
- free_root(cursor->mem_root, MYF(0));
- if (cursor->close_at_commit)
- thd->stmt_map.erase_transient_cursor(this);
- }
- /*
- Clear parameters from data which could be set by
- mysql_stmt_send_long_data() call.
- */
- reset_stmt_params(this);
+ /* The order is important */
+ lex->unit.cleanup();
+ cleanup_items(free_list);
+ thd->cleanup_after_query();
+ close_thread_tables(thd);
+ thd->rollback_item_tree_changes();
+
DBUG_VOID_RETURN;
}
@@ -2734,14 +2767,13 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
if (rc == 0)
rc= check_prepared_statement(this, name.str != 0);
- if (rc && thd->lex->sphead)
+ if (rc && lex->sphead)
{
- delete thd->lex->sphead;
- thd->lex->sphead= NULL;
+ delete lex->sphead;
+ lex->sphead= NULL;
}
lex_end(lex);
- close_thread_tables(thd);
- cleanup_stmt_and_thd_after_use(this, thd);
+ cleanup_stmt();
thd->restore_backup_statement(this, &stmt_backup);
thd->stmt_arena= old_stmt_arena;
@@ -2781,7 +2813,7 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
Statement stmt_backup;
Query_arena *old_stmt_arena;
Item *old_free_list;
- bool rc= 1;
+ bool rc= TRUE;
statistic_increment(thd->status_var.com_stmt_execute, &LOCK_status);
@@ -2789,18 +2821,35 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
if (state == Query_arena::ERROR)
{
my_message(last_errno, last_error, MYF(0));
- return 1;
+ return TRUE;
}
if (flags & IS_IN_USE)
{
my_error(ER_PS_NO_RECURSION, MYF(0));
- return 1;
+ return TRUE;
}
+
+ /*
+ For SHOW VARIABLES lex->result is NULL, as it's a non-SELECT
+ command. For such queries we don't return an error and don't
+ open a cursor -- the client library will recognize this case and
+ materialize the result set.
+ For SELECT statements lex->result is created in
+ check_prepared_statement. lex->result->simple_select() is FALSE
+ in INSERT ... SELECT and similar commands.
+ */
+
+ if (open_cursor && lex->result && !lex->result->simple_select())
+ {
+ DBUG_PRINT("info",("Cursor asked for not SELECT stmt"));
+ my_error(ER_SP_BAD_CURSOR_QUERY, MYF(0));
+ return TRUE;
+ }
+
/* In case the command has a call to SP which re-uses this statement name */
flags|= IS_IN_USE;
- if (cursor && cursor->is_open())
- close_cursor();
+ close_cursor();
/*
If the free_list is not empty, we'll wrongly free some externally
@@ -2808,32 +2857,6 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
*/
DBUG_ASSERT(thd->change_list.is_empty());
DBUG_ASSERT(thd->free_list == NULL);
- if (open_cursor)
- {
- if (!lex->result || !lex->result->simple_select())
- {
- DBUG_PRINT("info",("Cursor asked for not SELECT stmt"));
- /*
- If lex->result is set in the parser, this is not a SELECT
- statement: we can't open a cursor for it.
- */
- my_error(ER_SP_BAD_CURSOR_QUERY, MYF(0));
- goto error;
- }
-
- DBUG_PRINT("info",("Using READ_ONLY cursor"));
- if (!cursor && !(cursor= new (mem_root) Cursor(thd)))
- goto error;
- /* If lex->result is set, mysql_execute_command will use it */
- lex->result= &cursor->result;
- protocol= &cursor->protocol;
- thd->lock_id= &cursor->lock_id;
- /*
- Currently cursors can be used only from C API, so
- we don't have to create an own memory root for them:
- the one in THD is clean and can be used.
- */
- }
thd->set_n_backup_statement(this, &stmt_backup);
if (expanded_query->length() &&
alloc_query(thd, (char*) expanded_query->ptr(),
@@ -2862,38 +2885,27 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
reinit_stmt_before_use(thd, lex);
thd->protocol= protocol; /* activate stmt protocol */
- mysql_execute_command(thd);
+ rc= open_cursor ? mysql_open_cursor(thd, (uint) ALWAYS_MATERIALIZED_CURSOR,
+ &result, &cursor) :
+ mysql_execute_command(thd);
thd->protocol= &thd->protocol_simple; /* use normal protocol */
- if (cursor && cursor->is_open())
- {
- /*
- It's safer if we grab THD state after mysql_execute_command is
- finished and not in Cursor::open(), because currently the call to
- Cursor::open is buried deep in JOIN::exec of the top level join.
- */
- cursor->init_from_thd(thd);
+ /* Assert that if an error, no cursor is open */
+ DBUG_ASSERT(! (rc && cursor));
- if (cursor->close_at_commit)
- thd->stmt_map.add_transient_cursor(this);
- }
- else
+ if (! cursor)
{
- close_thread_tables(thd);
- cleanup_stmt_and_thd_after_use(this, thd);
+ cleanup_stmt();
reset_stmt_params(this);
}
thd->set_statement(&stmt_backup);
- thd->lock_id= &thd->main_lock_id;
thd->stmt_arena= old_stmt_arena;
if (state == Query_arena::PREPARED)
state= Query_arena::EXECUTED;
- rc= 0;
error:
- thd->lock_id= &thd->main_lock_id;
flags&= ~IS_IN_USE;
return rc;
}
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 3916509db69..38466582931 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -23,6 +23,7 @@
#include "mysql_priv.h"
#include "sql_select.h"
+#include "sql_cursor.h"
#include <m_ctype.h>
#include <hash.h>
@@ -107,20 +108,15 @@ 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,
ulong options);
-static Next_select_func setup_end_select_func(JOIN *join);
static int do_select(JOIN *join,List<Item> *fields,TABLE *tmp_table,
Procedure *proc);
static enum_nested_loop_state
-sub_select_cache(JOIN *join, JOIN_TAB *join_tab, bool end_of_records);
-static enum_nested_loop_state
evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
int error, my_bool *report_error);
static enum_nested_loop_state
evaluate_null_complemented_join_record(JOIN *join, JOIN_TAB *join_tab);
static enum_nested_loop_state
-sub_select(JOIN *join,JOIN_TAB *join_tab, bool end_of_records);
-static enum_nested_loop_state
flush_cached_records(JOIN *join, JOIN_TAB *join_tab, bool skip_last);
static enum_nested_loop_state
end_send(JOIN *join, JOIN_TAB *join_tab, bool end_of_records);
@@ -1716,262 +1712,6 @@ JOIN::destroy()
DBUG_RETURN(error);
}
-
-/************************* Cursor ******************************************/
-
-Cursor::Cursor(THD *thd)
- :Query_arena(&main_mem_root, INITIALIZED),
- join(0), unit(0),
- protocol(thd),
- close_at_commit(FALSE)
-{
- /* We will overwrite it at open anyway. */
- init_sql_alloc(&main_mem_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0);
- thr_lock_owner_init(&lock_id, &thd->lock_info);
- bzero((void*) ht_info, sizeof(ht_info));
-}
-
-
-void
-Cursor::init_from_thd(THD *thd)
-{
- Engine_info *info;
- /*
- We need to save and reset thd->mem_root, otherwise it'll be freed
- later in mysql_parse.
-
- We can't just change the thd->mem_root here as we want to keep the
- things that are already allocated in thd->mem_root for Cursor::fetch()
- */
- main_mem_root= *thd->mem_root;
- state= thd->stmt_arena->state;
- /* Allocate new memory root for thd */
- init_sql_alloc(thd->mem_root,
- thd->variables.query_alloc_block_size,
- thd->variables.query_prealloc_size);
-
- /*
- The same is true for open tables and lock: save tables and zero THD
- pointers to prevent table close in close_thread_tables (This is a part
- of the temporary solution to make cursors work with minimal changes to
- the current source base).
- */
- derived_tables= thd->derived_tables;
- open_tables= thd->open_tables;
- lock= thd->lock;
- query_id= thd->query_id;
- free_list= thd->free_list;
- change_list= thd->change_list;
- reset_thd(thd);
- /* Now we have an active cursor and can cause a deadlock */
- thd->lock_info.n_cursors++;
-
- close_at_commit= FALSE; /* reset in case we're reusing the cursor */
- info= &ht_info[0];
- for (handlerton **pht= thd->transaction.stmt.ht; *pht; pht++)
- {
- const handlerton *ht= *pht;
- close_at_commit|= test(ht->flags & HTON_CLOSE_CURSORS_AT_COMMIT);
- if (ht->create_cursor_read_view)
- {
- info->ht= ht;
- info->read_view= (ht->create_cursor_read_view)();
- ++info;
- }
- }
- /*
- XXX: thd->locked_tables is not changed.
- What problems can we have with it if cursor is open?
- TODO: must be fixed because of the prelocked mode.
- */
-}
-
-
-void
-Cursor::reset_thd(THD *thd)
-{
- thd->derived_tables= 0;
- thd->open_tables= 0;
- thd->lock= 0;
- thd->free_list= 0;
- thd->change_list.empty();
-}
-
-
-int
-Cursor::open(JOIN *join_arg)
-{
- join= join_arg;
- THD *thd= join->thd;
- /* First non-constant table */
- JOIN_TAB *join_tab= join->join_tab + join->const_tables;
- DBUG_ENTER("Cursor::open");
-
- /*
- Send fields description to the client; server_status is sent
- in 'EOF' packet, which ends send_fields().
- */
- thd->server_status|= SERVER_STATUS_CURSOR_EXISTS;
- join->result->send_fields(*join->fields, Protocol::SEND_NUM_ROWS);
- ::send_eof(thd);
- thd->server_status&= ~SERVER_STATUS_CURSOR_EXISTS;
-
- /* Prepare JOIN for reading rows. */
- join->tmp_table= 0;
- join->join_tab[join->tables-1].next_select= setup_end_select_func(join);
- join->send_records= 0;
- join->fetch_limit= join->unit->offset_limit_cnt;
-
- /* Disable JOIN CACHE as it is not working with cursors yet */
- for (JOIN_TAB *tab= join_tab;
- tab != join->join_tab + join->tables - 1;
- tab++)
- {
- if (tab->next_select == sub_select_cache)
- tab->next_select= sub_select;
- }
-
- DBUG_ASSERT(join_tab->table->reginfo.not_exists_optimize == 0);
- DBUG_ASSERT(join_tab->not_used_in_distinct == 0);
- /*
- null_row is set only if row not found and it's outer join: should never
- happen for the first table in join_tab list
- */
- DBUG_ASSERT(join_tab->table->null_row == 0);
- DBUG_RETURN(0);
-}
-
-
-/*
- DESCRIPTION
- Fetch next num_rows rows from the cursor and sent them to the client
- PRECONDITION:
- Cursor is open
- RETURN VALUES:
- none, this function will send error or OK to network if necessary.
-*/
-
-void
-Cursor::fetch(ulong num_rows)
-{
- THD *thd= join->thd;
- JOIN_TAB *join_tab= join->join_tab + join->const_tables;
- enum_nested_loop_state error= NESTED_LOOP_OK;
- Query_arena backup_arena;
- Engine_info *info;
- DBUG_ENTER("Cursor::fetch");
- DBUG_PRINT("enter",("rows: %lu", num_rows));
-
- DBUG_ASSERT(thd->derived_tables == 0 && thd->open_tables == 0 &&
- thd->lock == 0);
-
- thd->derived_tables= derived_tables;
- thd->open_tables= open_tables;
- thd->lock= lock;
- thd->query_id= query_id;
- thd->change_list= change_list;
- /* save references to memory, allocated during fetch */
- thd->set_n_backup_active_arena(this, &backup_arena);
-
- for (info= ht_info; info->read_view ; info++)
- (info->ht->set_cursor_read_view)(info->read_view);
-
- join->fetch_limit+= num_rows;
-
- error= sub_select(join, join_tab, 0);
- if (error == NESTED_LOOP_OK || error == NESTED_LOOP_NO_MORE_ROWS)
- error= sub_select(join,join_tab,1);
- if (error == NESTED_LOOP_QUERY_LIMIT)
- error= NESTED_LOOP_OK; /* select_limit used */
- if (error == NESTED_LOOP_CURSOR_LIMIT)
- join->resume_nested_loop= TRUE;
-
-#ifdef USING_TRANSACTIONS
- ha_release_temporary_latches(thd);
-#endif
- /* Grab free_list here to correctly free it in close */
- thd->restore_active_arena(this, &backup_arena);
-
- for (info= ht_info; info->read_view; info++)
- (info->ht->set_cursor_read_view)(0);
-
- if (error == NESTED_LOOP_CURSOR_LIMIT)
- {
- /* Fetch limit worked, possibly more rows are there */
- thd->server_status|= SERVER_STATUS_CURSOR_EXISTS;
- ::send_eof(thd);
- thd->server_status&= ~SERVER_STATUS_CURSOR_EXISTS;
- change_list= thd->change_list;
- reset_thd(thd);
- }
- else
- {
- close(TRUE);
- if (error == NESTED_LOOP_OK)
- {
- thd->server_status|= SERVER_STATUS_LAST_ROW_SENT;
- ::send_eof(thd);
- thd->server_status&= ~SERVER_STATUS_LAST_ROW_SENT;
- }
- else if (error != NESTED_LOOP_KILLED)
- my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
- }
- DBUG_VOID_RETURN;
-}
-
-
-void
-Cursor::close(bool is_active)
-{
- THD *thd= join->thd;
- DBUG_ENTER("Cursor::close");
-
- /*
- In case of UNIONs JOIN is freed inside of unit->cleanup(),
- otherwise in select_lex->cleanup().
- */
- if (unit)
- (void) unit->cleanup();
- else
- (void) join->select_lex->cleanup();
-
- for (Engine_info *info= ht_info; info->read_view; info++)
- {
- (info->ht->close_cursor_read_view)(info->read_view);
- info->read_view= 0;
- info->ht= 0;
- }
-
- if (is_active)
- close_thread_tables(thd);
- else
- {
- /* XXX: Another hack: closing tables used in the cursor */
- DBUG_ASSERT(lock || open_tables || derived_tables);
-
- TABLE *tmp_derived_tables= thd->derived_tables;
- MYSQL_LOCK *tmp_lock= thd->lock;
-
- thd->open_tables= open_tables;
- thd->derived_tables= derived_tables;
- thd->lock= lock;
- close_thread_tables(thd);
-
- thd->open_tables= tmp_derived_tables;
- thd->derived_tables= tmp_derived_tables;
- thd->lock= tmp_lock;
- }
- thd->lock_info.n_cursors--; /* Decrease the number of active cursors */
- join= 0;
- unit= 0;
- free_items();
- change_list.empty();
- DBUG_VOID_RETURN;
-}
-
-
-/*********************************************************************/
-
/*
An entry point to single-unit select (a select without UNION).
@@ -2051,9 +1791,9 @@ mysql_select(THD *thd, Item ***rref_pointer_array,
}
else
{
- if (join->prepare(rref_pointer_array, tables, wild_num,
- conds, og_num, order, group, having, proc_param,
- select_lex, unit))
+ if (err= join->prepare(rref_pointer_array, tables, wild_num,
+ conds, og_num, order, group, having, proc_param,
+ select_lex, unit))
{
goto err;
}
@@ -2068,9 +1808,9 @@ mysql_select(THD *thd, Item ***rref_pointer_array,
DBUG_RETURN(TRUE);
thd->proc_info="init";
thd->used_tables=0; // Updated by setup_fields
- if (join->prepare(rref_pointer_array, tables, wild_num,
- conds, og_num, order, group, having, proc_param,
- select_lex, unit))
+ if (err= join->prepare(rref_pointer_array, tables, wild_num,
+ conds, og_num, order, group, having, proc_param,
+ select_lex, unit))
{
goto err;
}
@@ -2112,7 +1852,7 @@ err:
if (free_join)
{
thd->proc_info="end";
- err= select_lex->cleanup();
+ err|= select_lex->cleanup();
DBUG_RETURN(err || thd->net.report_error);
}
DBUG_RETURN(join->error);
@@ -5976,7 +5716,7 @@ void JOIN::join_free(bool full)
cleanup(full);
for (unit= select_lex->first_inner_unit(); unit; unit= unit->next_unit())
- for (sl= unit->first_select_in_union(); sl; sl= sl->next_select())
+ for (sl= unit->first_select(); sl; sl= sl->next_select())
{
JOIN *join= sl->join;
if (join)
@@ -8077,18 +7817,24 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
if (field->maybe_null && !field->field->maybe_null())
{
result= create_tmp_field_from_item(thd, item, table, NULL,
- modify_item, convert_blob_length);
+ modify_item, convert_blob_length);
*from_field= field->field;
if (result && modify_item)
- ((Item_field*)item)->result_field= result;
+ field->result_field= result;
}
- else if (table_cant_handle_bit_fields && field->field->type() == FIELD_TYPE_BIT)
+ else if (table_cant_handle_bit_fields && field->field->type() ==
+ FIELD_TYPE_BIT)
+ {
+ *from_field= field->field;
result= create_tmp_field_from_item(thd, item, table, copy_func,
modify_item, convert_blob_length);
+ if (result && modify_item)
+ field->result_field= result;
+ }
else
result= create_tmp_field_from_field(thd, (*from_field= field->field),
item->name, table,
- modify_item ? (Item_field*) item :
+ modify_item ? field :
NULL,
convert_blob_length);
if (orig_type == Item::REF_ITEM && orig_modify)
@@ -8122,9 +7868,31 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
/*
Create a temp table according to a field list.
- Set distinct if duplicates could be removed
- Given fields field pointers are changed to point at tmp_table
- for send_fields
+
+ SYNOPSIS
+ create_tmp_table()
+ thd thread handle
+ param a description used as input to create the table
+ fields list of items that will be used to define
+ column types of the table (also see NOTES)
+ group TODO document
+ distinct should table rows be distinct
+ save_sum_fields see NOTES
+ select_options
+ rows_limit
+ table_alias possible name of the temporary table that can be used
+ for name resolving; can be "".
+
+ DESCRIPTION
+ Given field pointers are changed to point at tmp_table for
+ send_fields. The table object is self contained: it's
+ allocated in its own memory root, as well as Field objects
+ created for table columns.
+ This function will replace Item_sum items in 'fields' list with
+ corresponding Item_field items, pointing at the fields in the
+ temporary table, unless this was prohibited by TRUE
+ value of argument save_sum_fields. The Item_field objects
+ are created in THD memory root.
*/
#define STRING_TOTAL_LENGTH_TO_PACK_ROWS 128
@@ -8138,6 +7906,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
ulonglong select_options, ha_rows rows_limit,
char *table_alias)
{
+ MEM_ROOT *mem_root_save, own_root;
TABLE *table;
uint i,field_count,null_count,null_pack_length;
uint hidden_null_count, hidden_null_pack_length, hidden_field_count;
@@ -8202,29 +7971,33 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
field_count=param->field_count+param->func_count+param->sum_func_count;
hidden_field_count=param->hidden_field_count;
- if (!my_multi_malloc(MYF(MY_WME),
- &table,sizeof(*table),
- &reg_field, sizeof(Field*)*(field_count+1),
- &blob_field, sizeof(uint)*(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))
+
+ init_sql_alloc(&own_root, TABLE_ALLOC_BLOCK_SIZE, 0);
+
+ if (!multi_alloc_root(&own_root,
+ &table, sizeof(*table),
+ &reg_field, sizeof(Field*) * (field_count+1),
+ &blob_field, sizeof(uint)*(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))
{
bitmap_clear_bit(&temp_pool, temp_pool_slot);
DBUG_RETURN(NULL); /* purecov: inspected */
}
- if (!(param->copy_field=copy=new Copy_field[field_count]))
+ /* Copy_field belongs to TMP_TABLE_PARAM, allocate it in THD mem_root */
+ if (!(param->copy_field= copy= new (thd->mem_root) Copy_field[field_count]))
{
bitmap_clear_bit(&temp_pool, temp_pool_slot);
- my_free((gptr) table,MYF(0)); /* purecov: inspected */
+ free_root(&own_root, MYF(0)); /* purecov: inspected */
DBUG_RETURN(NULL); /* purecov: inspected */
}
param->items_to_copy= copy_func;
@@ -8234,6 +8007,11 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
bzero((char*) table,sizeof(*table));
bzero((char*) reg_field,sizeof(Field*)*(field_count+1));
bzero((char*) from_field,sizeof(Field*)*field_count);
+
+ table->mem_root= own_root;
+ mem_root_save= thd->mem_root;
+ thd->mem_root= &table->mem_root;
+
table->field=reg_field;
table->alias= table_alias;
table->reginfo.lock_type=TL_WRITE; /* Will be updated */
@@ -8299,7 +8077,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
Field *new_field=
create_tmp_field(thd, table, arg, arg->type(), &copy_func,
tmp_from_field, group != 0,not_all_columns,
- group || distinct,
+ distinct,
param->convert_blob_length);
if (!new_field)
goto err; // Should be OOM
@@ -8310,6 +8088,8 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
*blob_field++= (uint) (reg_field - table->field);
blob_count++;
}
+ if (new_field->type() == FIELD_TYPE_BIT)
+ total_uneven_bit_length+= new_field->field_length & 7;
new_field->field_index= (uint) (reg_field - table->field);
*(reg_field++)= new_field;
if (new_field->real_type() == MYSQL_TYPE_STRING ||
@@ -8318,7 +8098,9 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
string_count++;
string_total_length+= new_field->pack_length();
}
+ thd->mem_root= mem_root_save;
thd->change_item_tree(argp, new Item_field(new_field));
+ thd->mem_root= &table->mem_root;
if (!(new_field->flags & NOT_NULL_FLAG))
{
null_count++;
@@ -8343,12 +8125,16 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
write rows to the temporary table.
We here distinguish between UNION and multi-table-updates by the fact
that in the later case group is set to the row pointer.
+
+ The test for item->marker == 4 is ensure we don't create a group-by
+ key over a bit field as heap tables can't handle that.
*/
Field *new_field= (param->schema_table) ?
create_tmp_field_for_schema(thd, item, table) :
create_tmp_field(thd, table, item, type, &copy_func,
tmp_from_field, group != 0,
- not_all_columns || group != 0, 0,
+ not_all_columns || group != 0,
+ item->marker == 4,
param->convert_blob_length);
if (!new_field)
@@ -8380,7 +8166,14 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
*(reg_field++) =new_field;
}
if (!--hidden_field_count)
+ {
+ /*
+ This was the last hidden field; Remember how many hidden fields could
+ have null
+ */
hidden_null_count=null_count;
+ null_count= 0;
+ }
}
DBUG_ASSERT(field_count >= (uint) (reg_field - table->field));
field_count= (uint) (reg_field - table->field);
@@ -8415,8 +8208,8 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
null_count++;
}
hidden_null_pack_length=(hidden_null_count+7)/8;
- null_pack_length= hidden_null_count +
- (null_count + total_uneven_bit_length + 7) / 8;
+ null_pack_length= (hidden_null_pack_length +
+ (null_count + total_uneven_bit_length + 7) / 8);
reclength+=null_pack_length;
if (!reclength)
reclength=1; // Dummy select
@@ -8432,7 +8225,8 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
{
uint alloc_length=ALIGN_SIZE(reclength+MI_UNIQUE_HASH_LENGTH+1);
table->s->rec_buff_length= alloc_length;
- if (!(table->record[0]= (byte *) my_malloc(alloc_length*3, MYF(MY_WME))))
+ if (!(table->record[0]= (byte*)
+ alloc_root(&table->mem_root, alloc_length*3)))
goto err;
table->record[1]= table->record[0]+alloc_length;
table->s->default_values= table->record[1]+alloc_length;
@@ -8618,8 +8412,10 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
table->s->uniques= 1;
}
if (!(key_part_info= (KEY_PART_INFO*)
- sql_calloc((keyinfo->key_parts)*sizeof(KEY_PART_INFO))))
+ alloc_root(&table->mem_root,
+ keyinfo->key_parts * sizeof(KEY_PART_INFO))))
goto err;
+ bzero((void*) key_part_info, keyinfo->key_parts * sizeof(KEY_PART_INFO));
table->key_info=keyinfo;
keyinfo->key_part=key_part_info;
keyinfo->flags=HA_NOSAME | HA_NULL_ARE_EQUAL;
@@ -8667,10 +8463,15 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
if (create_myisam_tmp_table(table,param,select_options))
goto err;
}
- if (!open_tmp_table(table))
- DBUG_RETURN(table);
+ if (open_tmp_table(table))
+ goto err;
- err:
+ thd->mem_root= mem_root_save;
+
+ DBUG_RETURN(table);
+
+err:
+ thd->mem_root= mem_root_save;
free_tmp_table(thd,table); /* purecov: inspected */
bitmap_clear_bit(&temp_pool, temp_pool_slot);
DBUG_RETURN(NULL); /* purecov: inspected */
@@ -8815,11 +8616,12 @@ static bool create_myisam_tmp_table(TABLE *table,TMP_TABLE_PARAM *param,
if (table->s->keys)
{ // Get keys for ni_create
bool using_unique_constraint=0;
- HA_KEYSEG *seg= (HA_KEYSEG*) sql_calloc(sizeof(*seg) *
- keyinfo->key_parts);
+ HA_KEYSEG *seg= (HA_KEYSEG*) alloc_root(&table->mem_root,
+ sizeof(*seg) * keyinfo->key_parts);
if (!seg)
goto err;
+ bzero(seg, sizeof(*seg) * keyinfo->key_parts);
if (keyinfo->key_length >= table->file->max_key_length() ||
keyinfo->key_parts > table->file->max_key_parts() ||
table->s->uniques)
@@ -8916,13 +8718,14 @@ static bool create_myisam_tmp_table(TABLE *table,TMP_TABLE_PARAM *param,
void
free_tmp_table(THD *thd, TABLE *entry)
{
+ MEM_ROOT own_root= entry->mem_root;
const char *save_proc_info;
DBUG_ENTER("free_tmp_table");
DBUG_PRINT("enter",("table: %s",entry->alias));
save_proc_info=thd->proc_info;
thd->proc_info="removing tmp table";
- free_blobs(entry);
+
if (entry->file)
{
if (entry->db_stat)
@@ -8943,12 +8746,11 @@ free_tmp_table(THD *thd, TABLE *entry)
/* free blobs */
for (Field **ptr=entry->field ; *ptr ; ptr++)
(*ptr)->free();
- my_free((gptr) entry->record[0],MYF(0));
free_io_cache(entry);
bitmap_clear_bit(&temp_pool, entry->temp_pool_slot);
- my_free((gptr) entry,MYF(0));
+ free_root(&own_root, MYF(0)); /* the table is allocated in its own root */
thd->proc_info=save_proc_info;
DBUG_VOID_RETURN;
@@ -9063,7 +8865,7 @@ bool create_myisam_from_heap(THD *thd, TABLE *table, TMP_TABLE_PARAM *param,
end_select function to use. This function can't fail.
*/
-static Next_select_func setup_end_select_func(JOIN *join)
+Next_select_func setup_end_select_func(JOIN *join)
{
TABLE *table= join->tmp_table;
Next_select_func end_select;
@@ -9218,7 +9020,7 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
}
-static enum_nested_loop_state
+enum_nested_loop_state
sub_select_cache(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
{
enum_nested_loop_state rc;
@@ -9359,7 +9161,7 @@ sub_select_cache(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
return one of enum_nested_loop_state, except NESTED_LOOP_NO_MORE_ROWS.
*/
-static enum_nested_loop_state
+enum_nested_loop_state
sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
{
join_tab->table->null_row=0;
@@ -12378,6 +12180,11 @@ calc_group_buffer(JOIN *join,ORDER *group)
key_length+=MAX_BLOB_WIDTH; // Can't be used as a key
else if (field->type() == MYSQL_TYPE_VARCHAR)
key_length+= field->field_length + HA_KEY_BLOB_LENGTH;
+ else if (field->type() == FIELD_TYPE_BIT)
+ {
+ /* Bit is usually stored as a longlong key for group fields */
+ key_length+= 8; // Big enough
+ }
else
key_length+= field->pack_length();
}
@@ -13782,8 +13589,7 @@ bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result)
unit->fake_select_lex->select_number= UINT_MAX; // jost for initialization
unit->fake_select_lex->type= "UNION RESULT";
unit->fake_select_lex->options|= SELECT_DESCRIBE;
- if (!(res= unit->prepare(thd, result, SELECT_NO_UNLOCK | SELECT_DESCRIBE,
- "")))
+ if (!(res= unit->prepare(thd, result, SELECT_NO_UNLOCK | SELECT_DESCRIBE)))
res= unit->exec();
res|= unit->cleanup();
}
diff --git a/sql/sql_select.h b/sql/sql_select.h
index 47906c2697e..0dc4be8c104 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -101,7 +101,7 @@ enum enum_nested_loop_state
typedef enum_nested_loop_state
(*Next_select_func)(JOIN *, struct st_join_table *, bool);
typedef int (*Read_record_func)(struct st_join_table *tab);
-
+Next_select_func setup_end_select_func(JOIN *join);
typedef struct st_join_table {
TABLE *table;
@@ -140,6 +140,11 @@ typedef struct st_join_table {
void cleanup();
} JOIN_TAB;
+enum_nested_loop_state sub_select_cache(JOIN *join, JOIN_TAB *join_tab, bool
+ end_of_records);
+enum_nested_loop_state sub_select(JOIN *join,JOIN_TAB *join_tab, bool
+ end_of_records);
+
typedef struct st_position /* Used in find_best */
{
@@ -372,58 +377,6 @@ class JOIN :public Sql_alloc
};
-/*
- Server-side cursor (now stands only for basic read-only cursor)
- See class implementation in sql_select.cc
- A cursor has its own runtime state - list of used items and memory root of
- used memory - which is different from Prepared statement runtime: it must
- be different at least for the purpose of reusing the same prepared
- statement for many cursors.
-*/
-
-class Cursor: public Sql_alloc, public Query_arena
-{
- MEM_ROOT main_mem_root;
- JOIN *join;
- SELECT_LEX_UNIT *unit;
-
- TABLE *open_tables;
- MYSQL_LOCK *lock;
- TABLE *derived_tables;
- /* List of items created during execution */
- query_id_t query_id;
- struct Engine_info
- {
- const handlerton *ht;
- void *read_view;
- };
- Engine_info ht_info[MAX_HA];
-public:
- Protocol_prep protocol;
- Item_change_list change_list;
- select_send result;
- THR_LOCK_OWNER lock_id;
- my_bool close_at_commit;
-
- /* Temporary implementation as now we replace THD state by value */
- /* Save THD state into cursor */
- void init_from_thd(THD *thd);
- /* bzero cursor state in THD */
- void reset_thd(THD *thd);
-
- int open(JOIN *join);
- void fetch(ulong num_rows);
- void reset() { join= 0; }
- bool is_open() const { return join != 0; }
-
- void close(bool is_active);
-
- void set_unit(SELECT_LEX_UNIT *unit_arg) { unit= unit_arg; }
- Cursor(THD *thd);
- ~Cursor() {}
-};
-
-
typedef struct st_select_check {
uint const_ref,reg_ref;
} SELECT_CHECK;
diff --git a/sql/sql_union.cc b/sql/sql_union.cc
index 556493f4fc8..951248e8cd8 100644
--- a/sql/sql_union.cc
+++ b/sql/sql_union.cc
@@ -23,6 +23,7 @@
#include "mysql_priv.h"
#include "sql_select.h"
+#include "sql_cursor.h"
bool mysql_union(THD *thd, LEX *lex, select_result *result,
SELECT_LEX_UNIT *unit, ulong setup_tables_done_option)
@@ -30,13 +31,9 @@ bool mysql_union(THD *thd, LEX *lex, select_result *result,
DBUG_ENTER("mysql_union");
bool res;
if (!(res= unit->prepare(thd, result, SELECT_NO_UNLOCK |
- setup_tables_done_option, "")))
+ setup_tables_done_option)))
res= unit->exec();
- if (!res && thd->cursor && thd->cursor->is_open())
- {
- thd->cursor->set_unit(unit);
- }
- else
+ if (res || !thd->cursor || !thd->cursor->is_open())
res|= unit->cleanup();
DBUG_RETURN(res);
}
@@ -46,16 +43,6 @@ bool mysql_union(THD *thd, LEX *lex, select_result *result,
** store records in temporary table for UNION
***************************************************************************/
-select_union::select_union(TABLE *table_par)
- :table(table_par)
-{
-}
-
-select_union::~select_union()
-{
-}
-
-
int select_union::prepare(List<Item> &list, SELECT_LEX_UNIT *u)
{
unit= u;
@@ -103,6 +90,45 @@ bool select_union::flush()
return 0;
}
+/*
+ Create a temporary table to store the result of select_union.
+
+ SYNOPSIS
+ select_union::create_result_table()
+ thd thread handle
+ column_types a list of items used to define columns of the
+ temporary table
+ is_union_distinct if set, the temporary table will eliminate
+ duplicates on insert
+ options create options
+
+ DESCRIPTION
+ Create a temporary table that is used to store the result of a UNION,
+ derived table, or a materialized cursor.
+
+ RETURN VALUE
+ 0 The table has been created successfully.
+ 1 create_tmp_table failed.
+*/
+
+bool
+select_union::create_result_table(THD *thd, List<Item> *column_types,
+ bool is_union_distinct, ulonglong options,
+ const char *alias)
+{
+ DBUG_ASSERT(table == 0);
+ tmp_table_param.init();
+ tmp_table_param.field_count= column_types->elements;
+
+ if (! (table= create_tmp_table(thd, &tmp_table_param, *column_types,
+ (ORDER*) 0, is_union_distinct, 1,
+ options, HA_POS_ERROR, (char*) alias)))
+ return TRUE;
+ table->file->extra(HA_EXTRA_WRITE_CACHE);
+ table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
+ return FALSE;
+}
+
/*
initialization procedures before fake_select_lex preparation()
@@ -133,11 +159,10 @@ st_select_lex_unit::init_prepare_fake_select_lex(THD *thd)
bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
- ulong additional_options,
- const char *tmp_table_alias)
+ ulong additional_options)
{
SELECT_LEX *lex_select_save= thd_arg->lex->current_select;
- SELECT_LEX *sl, *first_select;
+ SELECT_LEX *sl, *first_sl= first_select();
select_result *tmp_result;
bool is_union;
TABLE *empty_table= 0;
@@ -156,7 +181,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
if (describe)
{
/* fast reinit for EXPLAIN */
- for (sl= first_select_in_union(); sl; sl= sl->next_select())
+ for (sl= first_sl; sl; sl= sl->next_select())
{
sl->join->result= result;
select_limit_cnt= HA_POS_ERROR;
@@ -175,17 +200,16 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
prepared= 1;
res= FALSE;
- thd_arg->lex->current_select= sl= first_select= first_select_in_union();
- found_rows_for_union= first_select->options & OPTION_FOUND_ROWS;
- is_union= test(first_select->next_select());
+ thd_arg->lex->current_select= sl= first_sl;
+ found_rows_for_union= first_sl->options & OPTION_FOUND_ROWS;
+ is_union= test(first_sl->next_select());
/* Global option */
if (is_union)
{
- if (!(tmp_result= union_result= new select_union(0)))
+ if (!(tmp_result= union_result= new select_union))
goto err;
- union_result->tmp_table_param.init();
if (describe)
tmp_result= sel_result;
}
@@ -238,8 +262,8 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
information about fields lengths and exact types
*/
if (!is_union)
- types= first_select_in_union()->item_list;
- else if (sl == first_select)
+ types= first_sl->item_list;
+ else if (sl == first_sl)
{
/*
We need to create an empty table object. It is used
@@ -287,7 +311,6 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
all collations together for UNION.
*/
List_iterator_fast<Item> tp(types);
- Query_arena *arena= thd->stmt_arena;
Item *type;
ulonglong create_options;
@@ -301,7 +324,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
}
}
- create_options= (first_select_in_union()->options | thd_arg->options |
+ create_options= (first_sl->options | thd_arg->options |
TMP_TABLE_ALL_COLUMNS);
/*
Force the temporary table to be a MyISAM table if we're going to use
@@ -312,47 +335,35 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
if (global_parameters->ftfunc_list->elements)
create_options= create_options | TMP_TABLE_FORCE_MYISAM;
- union_result->tmp_table_param.field_count= types.elements;
- if (!(table= create_tmp_table(thd_arg,
- &union_result->tmp_table_param, types,
- (ORDER*) 0, (bool) union_distinct, 1,
- create_options, HA_POS_ERROR,
- (char *) tmp_table_alias)))
+ if (union_result->create_result_table(thd, &types, test(union_distinct),
+ create_options, ""))
goto err;
- table->file->extra(HA_EXTRA_WRITE_CACHE);
- table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
bzero((char*) &result_table_list, sizeof(result_table_list));
result_table_list.db= (char*) "";
result_table_list.table_name= result_table_list.alias= (char*) "union";
- result_table_list.table= table;
- union_result->set_table(table);
+ result_table_list.table= table= union_result->table;
thd_arg->lex->current_select= lex_select_save;
if (!item_list.elements)
{
- Field **field;
- Query_arena *tmp_arena,backup;
- tmp_arena= thd->activate_stmt_arena_if_needed(&backup);
+ Query_arena *arena, backup_arena;
- for (field= table->field; *field; field++)
- {
- Item_field *item= new Item_field(*field);
- if (!item || item_list.push_back(item))
- {
- if (tmp_arena)
- thd->restore_active_arena(tmp_arena, &backup);
- DBUG_RETURN(TRUE);
- }
- }
- if (tmp_arena)
- thd->restore_active_arena(tmp_arena, &backup);
- if (arena->is_stmt_prepare_or_first_sp_execute())
+ arena= thd->activate_stmt_arena_if_needed(&backup_arena);
+
+ res= table->fill_item_list(&item_list);
+
+ if (arena)
+ thd->restore_active_arena(arena, &backup_arena);
+
+ if (res)
+ goto err;
+
+ if (thd->stmt_arena->is_stmt_prepare())
{
- /* prepare fake select to initialize it correctly */
+ /* Validate the global parameters of this union */
+
init_prepare_fake_select_lex(thd);
- /*
- Should be done only once (the only item_list per statement).
- */
+ /* Should be done only once (the only item_list per statement) */
DBUG_ASSERT(fake_select_lex->join == 0);
if (!(fake_select_lex->join= new JOIN(thd, item_list, thd->options,
result)))
@@ -375,19 +386,14 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
fake_select_lex->table_list.empty();
}
}
- else if (!arena->is_conventional())
+ else
{
+ DBUG_ASSERT(!thd->stmt_arena->is_conventional());
/*
We're in execution of a prepared statement or stored procedure:
reset field items to point at fields from the created temporary table.
*/
- List_iterator_fast<Item> it(item_list);
- for (Field **field= table->field; *field; field++)
- {
- Item_field *item_field= (Item_field*) it++;
- DBUG_ASSERT(item_field != 0);
- item_field->reset_field(*field);
- }
+ table->reset_item_list(&item_list);
}
}
@@ -404,7 +410,7 @@ err:
bool st_select_lex_unit::exec()
{
SELECT_LEX *lex_select_save= thd->lex->current_select;
- SELECT_LEX *select_cursor=first_select_in_union();
+ SELECT_LEX *select_cursor=first_select();
ulonglong add_rows=0;
ha_rows examined_rows= 0;
DBUG_ENTER("st_select_lex_unit::exec");
@@ -595,7 +601,7 @@ bool st_select_lex_unit::cleanup()
table= 0; // Safety
}
- for (SELECT_LEX *sl= first_select_in_union(); sl; sl= sl->next_select())
+ for (SELECT_LEX *sl= first_select(); sl; sl= sl->next_select())
error|= sl->cleanup();
if (fake_select_lex)
@@ -652,7 +658,7 @@ bool st_select_lex_unit::change_result(select_subselect *result,
select_subselect *old_result)
{
bool res= FALSE;
- for (SELECT_LEX *sl= first_select_in_union(); sl; sl= sl->next_select())
+ for (SELECT_LEX *sl= first_select(); sl; sl= sl->next_select())
{
if (sl->join && sl->join->result == old_result)
if (sl->join->change_result(result))
@@ -663,6 +669,36 @@ bool st_select_lex_unit::change_result(select_subselect *result,
return (res);
}
+/*
+ Get column type information for this unit.
+
+ SYNOPSIS
+ st_select_lex_unit::get_unit_column_types()
+
+ DESCRIPTION
+ For a single-select the column types are taken
+ from the list of selected items. For a union this function
+ assumes that st_select_lex_unit::prepare has been called
+ and returns the type holders that were created for unioned
+ column types of all selects.
+
+ NOTES
+ The implementation of this function should be in sync with
+ st_select_lex_unit::prepare()
+*/
+
+List<Item> *st_select_lex_unit::get_unit_column_types()
+{
+ bool is_union= test(first_select()->next_select());
+
+ if (is_union)
+ {
+ DBUG_ASSERT(prepared);
+ /* Types are generated during prepare */
+ return &types;
+ }
+ return &first_select()->item_list;
+}
bool st_select_lex::cleanup()
{
diff --git a/sql/sql_view.cc b/sql/sql_view.cc
index 1495feeca09..b45bca4ff67 100644
--- a/sql/sql_view.cc
+++ b/sql/sql_view.cc
@@ -380,7 +380,7 @@ bool mysql_create_view(THD *thd,
/* prepare select to resolve all fields */
lex->view_prepare_mode= 1;
- if (unit->prepare(thd, 0, 0, view->view_name.str))
+ if (unit->prepare(thd, 0, 0))
{
/*
some errors from prepare are reported to user, if is not then
diff --git a/sql/table.cc b/sql/table.cc
index 67d4ebd713b..982d5e7ddc9 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -349,7 +349,7 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat,
char *buff;
if (!(buff= alloc_root(&outparam->mem_root, n_length)))
goto err;
- if (my_pread(file, buff, n_length, record_offset + share->reclength,
+ if (my_pread(file, (byte*)buff, n_length, record_offset + share->reclength,
MYF(MY_NABP)))
goto err;
share->connect_string.length= uint2korr(buff);
@@ -1691,6 +1691,63 @@ db_type get_table_type(THD *thd, const char *name)
DBUG_RETURN(ha_checktype(thd,(enum db_type) (uint) *(head+3),0,0));
}
+/*
+ Create Item_field for each column in the table.
+
+ SYNPOSIS
+ st_table::fill_item_list()
+ item_list a pointer to an empty list used to store items
+
+ DESCRIPTION
+ Create Item_field object for each column in the table and
+ initialize it with the corresponding Field. New items are
+ created in the current THD memory root.
+
+ RETURN VALUE
+ 0 success
+ 1 out of memory
+*/
+
+bool st_table::fill_item_list(List<Item> *item_list) const
+{
+ /*
+ All Item_field's created using a direct pointer to a field
+ are fixed in Item_field constructor.
+ */
+ for (Field **ptr= field; *ptr; ptr++)
+ {
+ Item_field *item= new Item_field(*ptr);
+ if (!item || item_list->push_back(item))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ Reset an existing list of Item_field items to point to the
+ Fields of this table.
+
+ SYNPOSIS
+ st_table::fill_item_list()
+ item_list a non-empty list with Item_fields
+
+ DESCRIPTION
+ This is a counterpart of fill_item_list used to redirect
+ Item_fields to the fields of a newly created table.
+ The caller must ensure that number of items in the item_list
+ is the same as the number of columns in the table.
+*/
+
+void st_table::reset_item_list(List<Item> *item_list) const
+{
+ List_iterator_fast<Item> it(*item_list);
+ for (Field **ptr= field; *ptr; ptr++)
+ {
+ Item_field *item_field= (Item_field*) it++;
+ DBUG_ASSERT(item_field != 0);
+ item_field->reset_field(*ptr);
+ }
+}
/*
calculate md5 of query
diff --git a/sql/table.h b/sql/table.h
index 43b6fddeee6..b01d774bf10 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -267,6 +267,9 @@ struct st_table {
GRANT_INFO grant;
FILESORT_INFO sort;
TABLE_SHARE share_not_to_be_used; /* To be deleted when true shares */
+
+ bool fill_item_list(List<Item> *item_list) const;
+ void reset_item_list(List<Item> *item_list) const;
};
diff --git a/sql/unireg.cc b/sql/unireg.cc
index 1ac8be0f49a..d297b143d3b 100644
--- a/sql/unireg.cc
+++ b/sql/unireg.cc
@@ -153,8 +153,8 @@ bool mysql_create_frm(THD *thd, my_string file_name,
{
char buff[2];
int2store(buff,create_info->connect_string.length);
- if (my_write(file, buff, sizeof(buff), MYF(MY_NABP)) ||
- my_write(file, create_info->connect_string.str,
+ if (my_write(file, (const byte*)buff, sizeof(buff), MYF(MY_NABP)) ||
+ my_write(file, (const byte*)create_info->connect_string.str,
create_info->connect_string.length, MYF(MY_NABP)))
goto err;
}
diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c
index 84a32b52284..65a4617445c 100644
--- a/tests/mysql_client_test.c
+++ b/tests/mysql_client_test.c
@@ -13828,8 +13828,11 @@ static void test_bug10760()
rc= mysql_stmt_execute(stmt);
check_execute(stmt, rc);
rc= mysql_query(mysql, "update t1 set id=id+100");
- DIE_UNLESS(rc);
- if (!opt_silent)
+ /*
+ If cursors are not materialized, the update will return an error;
+ we mainly test that it won't deadlock.
+ */
+ if (rc && !opt_silent)
printf("Got error (as expected): %s\n", mysql_error(mysql));
/*
2: check that MyISAM tables used in cursors survive