diff options
author | unknown <konstantin@mysql.com> | 2005-09-23 00:46:59 +0400 |
---|---|---|
committer | unknown <konstantin@mysql.com> | 2005-09-23 00:46:59 +0400 |
commit | c4eee8bd69d4099132a99948f890b69ae5539b3a (patch) | |
tree | c8753cbe4273232bc93f91ecd04ad7014cee9cdb | |
parent | 9a0ee3bffb39954c8668f47dd48f98252b3b2b2b (diff) | |
parent | b7ee1513d46d2dd1057ed9a30a9357ea86b31cca (diff) | |
download | mariadb-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
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), - ®_field, sizeof(Field*)*(field_count+1), - &blob_field, sizeof(uint)*(field_count+1), - &from_field, sizeof(Field*)*field_count, - ©_func,sizeof(*copy_func)*(param->func_count+1), - ¶m->keyinfo,sizeof(*param->keyinfo), - &key_part_info, - sizeof(*key_part_info)*(param->group_parts+1), - ¶m->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), + ®_field, sizeof(Field*) * (field_count+1), + &blob_field, sizeof(uint)*(field_count+1), + &from_field, sizeof(Field*)*field_count, + ©_func, sizeof(*copy_func)*(param->func_count+1), + ¶m->keyinfo, sizeof(*param->keyinfo), + &key_part_info, + sizeof(*key_part_info)*(param->group_parts+1), + ¶m->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(), ©_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, ©_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 |